diff --git a/.secrets.baseline b/.secrets.baseline index 748400d4..c64bb30f 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -511,7 +511,7 @@ "filename": "tests/test_file.py", "hashed_secret": "bc5494e9e5c7bd002f295376f6130e2eced73a4a", "is_verified": false, - "line_number": 84 + "line_number": 86 } ], "tests/test_index.py": [ @@ -656,5 +656,5 @@ } ] }, - "generated_at": "2025-11-07T21:07:45Z" + "generated_at": "2026-01-14T17:34:14Z" } diff --git a/docs/_build/doctrees/file.doctree b/docs/_build/doctrees/file.doctree index bd794497..1fc88b0b 100644 Binary files a/docs/_build/doctrees/file.doctree and b/docs/_build/doctrees/file.doctree differ diff --git a/docs/_build/html/_modules/gen3/file.html b/docs/_build/html/_modules/gen3/file.html index 8bc4759c..ea50f149 100644 --- a/docs/_build/html/_modules/gen3/file.html +++ b/docs/_build/html/_modules/gen3/file.html @@ -33,27 +33,33 @@

Source code for gen3.file

 import json
 import requests
-import json
 import asyncio
 import aiohttp
 import aiofiles
 import time
+import multiprocessing as mp
+import threading
 from tqdm import tqdm
-from types import SimpleNamespace as Namespace
 import os
-import requests
 from pathlib import Path
+from typing import List, Dict, Any, Optional
+from urllib.parse import parse_qsl, urlencode, urlparse, urlunparse, quote
+from queue import Empty
 
 from cdislogging import get_logger
 
+from werkzeug.security import safe_join
+
 from gen3.index import Gen3Index
-from gen3.utils import DEFAULT_BACKOFF_SETTINGS, raise_for_status_and_print_error
-from urllib.parse import parse_qsl, urlencode, urlparse, urlunparse
+from gen3.utils import raise_for_status_and_print_error
 
 logging = get_logger("__name__")
 
 
 MAX_RETRIES = 3
+DEFAULT_NUM_PARALLEL = 3
+DEFAULT_MAX_CONCURRENT_REQUESTS = 300
+DEFAULT_QUEUE_SIZE = 1000
 
 
 
@@ -80,7 +86,6 @@

Source code for gen3.file

         # auth_provider legacy interface required endpoint as 1st arg
         self._auth_provider = auth_provider or endpoint
         self._endpoint = self._auth_provider.endpoint
-        self.unsuccessful_downloads = []
 
 
[docs] @@ -104,10 +109,29 @@

Source code for gen3.file

         resp = requests.get(api_url, auth=self._auth_provider)
         raise_for_status_and_print_error(resp)
 
-        try:
-            return resp.json()
-        except:
-            return resp.text
+ return resp.json()
+ + +
+[docs] + def get_presigned_urls_batch(self, guids, protocol=None): + """Get presigned URLs for multiple files efficiently. + + Args: + guids (List[str]): List of GUIDs to get presigned URLs for + protocol (str, optional): Protocol preference for URLs + + Returns: + Dict[str, Dict]: Mapping of GUID to presigned URL response + """ + results = {} + for guid in guids: + try: + results[guid] = self.get_presigned_url(guid, protocol) + except Exception as e: + logging.error(f"Failed to get presigned URL for {guid}: {e}") + results[guid] = None + return results
@@ -186,12 +210,7 @@

Source code for gen3.file

             api_url, auth=self._auth_provider, json=body, headers=headers
         )
         raise_for_status_and_print_error(resp)
-        try:
-            data = json.loads(resp.text)
-        except:
-            return resp.text
-
-        return data
+ return resp.json()
def _ensure_dirpath_exists(path: Path) -> Path: @@ -212,16 +231,19 @@

Source code for gen3.file

 
 
[docs] - def download_single(self, object_id, path): + def download_single(self, object_id, path, protocol=None): """ Download a single file using its GUID. Args: object_id (str): The file's unique ID path (str): Path to store the downloaded file at + + Returns: + bool: True if download successful, False otherwise """ try: - url = self.get_presigned_url(object_id) + url = self.get_presigned_url(object_id, protocol=protocol) except Exception as e: logging.critical(f"Unable to get a presigned URL for download: {e}") return False @@ -235,9 +257,9 @@

Source code for gen3.file

                     # NOTE could be updated with exponential backoff
                     time.sleep(1)
                     response = requests.get(url["url"], stream=True)
-                    if response.status == 200:
+                    if response.status_code == 200:
                         break
-                if response.status != 200:
+                if response.status_code != 200:
                     logging.critical("Response status not 200, try again later")
                     return False
             else:
@@ -251,18 +273,18 @@ 

Source code for gen3.file

         index = Gen3Index(self._auth_provider)
         record = index.get_record(object_id)
 
-        filename = record["file_name"]
+        filename = record["file_name"] or url["url"].split("?")[0].split("/")[-1]
 
-        out_path = Gen3File._ensure_dirpath_exists(Path(path))
+        out_path = safe_join(path, filename)
+        Gen3File._ensure_dirpath_exists(Path(os.path.dirname(out_path)))
 
-        with open(os.path.join(out_path, filename), "wb") as f:
+        with open(out_path, "wb") as f:
             for data in response.iter_content(4096):
                 total_downloaded += len(data)
                 f.write(data)
 
         if total_size_in_bytes == total_downloaded:
             logging.info(f"File {filename} downloaded successfully")
-
         else:
             logging.error(f"File {filename} not downloaded successfully")
             return False
@@ -311,7 +333,445 @@ 

Source code for gen3.file

         resp = requests.get(url, auth=self._auth_provider)
         raise_for_status_and_print_error(resp)
         return resp.json()
-
+ + +
+[docs] + async def async_download_multiple( + self, + manifest_data, + download_path=".", + filename_format="original", + protocol=None, + max_concurrent_requests=DEFAULT_MAX_CONCURRENT_REQUESTS, + num_processes=DEFAULT_NUM_PARALLEL, + queue_size=DEFAULT_QUEUE_SIZE, + skip_completed=False, + rename=False, + no_progress=False, + ): + """Asynchronously download multiple files using multiprocessing and queues.""" + if not manifest_data: + return {"succeeded": [], "failed": [], "skipped": []} + + guids = [] + for item in manifest_data: + guid = item.get("guid") or item.get("object_id") + if guid: + if "/" in guid: + guid = guid.split("/")[-1] + guids.append(guid) + + if not guids: + logging.error("No valid GUIDs found in manifest data") + return {"succeeded": [], "failed": [], "skipped": []} + + output_dir = Gen3File._ensure_dirpath_exists(Path(download_path)) + + input_queue = mp.Queue(maxsize=queue_size) + output_queue = mp.Queue() + + worker_config = { + "endpoint": self._endpoint, + "auth_provider": self._auth_provider, + "download_path": str(output_dir), + "filename_format": filename_format, + "protocol": protocol, + "max_concurrent": max_concurrent_requests, + "skip_completed": skip_completed, + "rename": rename, + } + + processes = [] + producer_thread = None + + try: + for i in range(num_processes): + p = mp.Process( + target=self._async_worker_process, + args=(input_queue, output_queue, worker_config, i), + ) + p.start() + processes.append(p) + + producer_thread = threading.Thread( + target=self._guid_producer, + args=(guids, input_queue, num_processes), + ) + producer_thread.start() + + results = {"succeeded": [], "failed": [], "skipped": []} + completed_count = 0 + + if not no_progress: + pbar = tqdm(total=len(guids), desc="Downloading") + + while completed_count < len(guids): + try: + batch_results = output_queue.get() + + if not batch_results: + continue + + for result in batch_results: + if result["status"] == "downloaded": + results["succeeded"].append(result["guid"]) + elif result["status"] == "skipped": + results["skipped"].append(result["guid"]) + else: + results["failed"].append(result["guid"]) + + completed_count += 1 + if not no_progress: + pbar.update(1) + + except Empty: + logging.warning( + f"No more results available ({completed_count}/{len(guids)}): Queue is empty" + ) + break + except Exception as e: + logging.warning( + f"Error waiting for results ({completed_count}/{len(guids)}): {e}" + ) + + alive_processes = [p for p in processes if p.is_alive()] + if not alive_processes: + logging.error("All worker processes have died") + break + + if not no_progress: + pbar.close() + + if producer_thread: + producer_thread.join() + + except Exception as e: + logging.error(f"Error in download: {e}") + results = {"succeeded": [], "failed": [], "skipped": [], "error": str(e)} + + finally: + for p in processes: + if p.is_alive(): + p.terminate() + + p.join() + if p.is_alive(): + p.kill() + + logging.info( + f"Download complete: {len(results['succeeded'])} succeeded, " + f"{len(results['failed'])} failed, {len(results['skipped'])} skipped" + ) + return results
+ + + def _guid_producer(self, guids, input_queue, num_processes): + try: + for guid in guids: + input_queue.put(guid) + + except Exception as e: + logging.error(f"Error in producer: {e}") + + @staticmethod + def _async_worker_process(input_queue, output_queue, config, process_id): + try: + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + loop.run_until_complete( + Gen3File._worker_main(input_queue, output_queue, config, process_id) + ) + except Exception as e: + logging.error(f"Error in worker process {process_id}: {e}") + finally: + try: + loop.close() + except Exception as e: + logging.warning(f"Error closing event loop in worker {process_id}: {e}") + + @staticmethod + async def _worker_main(input_queue, output_queue, config, process_id): + endpoint = config["endpoint"] + auth_provider = config["auth_provider"] + download_path = Path(config["download_path"]) + filename_format = config["filename_format"] + protocol = config["protocol"] + max_concurrent = config["max_concurrent"] + skip_completed = config["skip_completed"] + rename = config["rename"] + + # Configure connector with optimized settings for large files + timeout = aiohttp.ClientTimeout(total=None, connect=3600, sock_read=3600) + connector = aiohttp.TCPConnector( + limit=max_concurrent * 2, + limit_per_host=max_concurrent, + ttl_dns_cache=300, + use_dns_cache=True, + keepalive_timeout=3600, + enable_cleanup_closed=True, + ) + semaphore = asyncio.Semaphore(max_concurrent) + + async with aiohttp.ClientSession( + connector=connector, timeout=timeout + ) as session: + while True: + try: + # Check if queue is empty with timeout + guid = input_queue.get() + except Empty: + # If queue is empty (timeout), break the loop + break + + # Process single GUID as a batch of one + try: + batch_results = await Gen3File._process_batch( + session, + [guid], + endpoint, + auth_provider, + download_path, + filename_format, + protocol, + semaphore, + skip_completed, + rename, + ) + output_queue.put(batch_results) + except Exception as e: + logging.error( + f"Worker {process_id}: Failed to process {guid} - {type(e).__name__}: {e}" + ) + error_result = [{"guid": guid, "status": "failed", "error": str(e)}] + try: + output_queue.put(error_result) + except Exception as queue_error: + logging.error( + f"Worker {process_id}: Failed to send error result for {guid} - {type(queue_error).__name__}: {queue_error}" + ) + + @staticmethod + async def _process_batch( + session, + guids, + endpoint, + auth_provider, + download_path, + filename_format, + protocol, + semaphore, + skip_completed, + rename, + ): + """Process a batch of GUIDs for downloading.""" + batch_results = [] + for guid in guids: + async with semaphore: + result = await Gen3File._download_single_async( + session, + guid, + endpoint, + auth_provider, + download_path, + filename_format, + protocol, + semaphore, + skip_completed, + rename, + ) + batch_results.append(result) + return batch_results + + @staticmethod + async def _download_single_async( + session, + guid, + endpoint, + auth_provider, + download_path, + filename_format, + protocol, + semaphore, + skip_completed, + rename, + ): + async with semaphore: + try: + metadata = await Gen3File._get_metadata( + session, guid, endpoint, auth_provider.get_access_token() + ) + + original_filename = metadata.get("file_name") + filename = Gen3File._format_filename_static( + guid, original_filename, filename_format + ) + filepath = download_path / filename + + if skip_completed and filepath.exists(): + return { + "guid": guid, + "status": "skipped", + "filepath": str(filepath), + "reason": "File already exists", + } + + filepath = Gen3File._handle_conflict_static(filepath, rename) + + presigned_data = await Gen3File._get_presigned_url_async( + session, guid, endpoint, auth_provider.get_access_token(), protocol + ) + + url = presigned_data.get("url") + if not url: + return { + "guid": guid, + "status": "failed", + "error": "No URL in presigned data", + } + + filepath.parent.mkdir(parents=True, exist_ok=True) + + success = await Gen3File._download_content(session, url, guid, filepath) + if success: + return { + "guid": guid, + "status": "downloaded", + "filepath": str(filepath), + "size": filepath.stat().st_size if filepath.exists() else 0, + } + else: + return { + "guid": guid, + "status": "failed", + "error": "Download failed", + } + + except Exception as e: + logging.error(f"Error downloading {guid}: {e}") + return { + "guid": guid, + "status": "failed", + "error": str(e), + } + + @staticmethod + async def _get_metadata(session, guid, endpoint, auth_token): + encoded_guid = quote(guid, safe="") + api_url = f"{endpoint}/index/{encoded_guid}" + headers = {"Authorization": f"Bearer {auth_token}"} + + try: + async with session.get( + api_url, headers=headers, timeout=aiohttp.ClientTimeout(total=3600) + ) as resp: + if resp.status == 200: + return await resp.json() + raise Exception( + f"Failed to get metadata for {guid}: HTTP {resp.status}" + ) + except aiohttp.ClientError as e: + raise Exception(f"Network error getting metadata for {guid}: {e}") + except asyncio.TimeoutError: + raise Exception(f"Timeout getting metadata for {guid}") + except Exception as e: + if "Failed to get metadata" not in str(e): + raise Exception(f"Unexpected error getting metadata for {guid}: {e}") + raise + + @staticmethod + async def _get_presigned_url_async( + session, guid, endpoint, auth_token, protocol=None + ): + encoded_guid = quote(guid, safe="") + api_url = f"{endpoint}/user/data/download/{encoded_guid}" + headers = {"Authorization": f"Bearer {auth_token}"} + + if protocol: + api_url += f"?protocol={protocol}" + + try: + async with session.get( + api_url, headers=headers, timeout=aiohttp.ClientTimeout(total=3600) + ) as resp: + if resp.status == 200: + return await resp.json() + raise Exception( + f"Failed to get presigned URL for {guid}: HTTP {resp.status}" + ) + except aiohttp.ClientError as e: + raise Exception(f"Network error getting presigned URL for {guid}: {e}") + except asyncio.TimeoutError: + raise Exception(f"Timeout getting presigned URL for {guid}") + except Exception as e: + if "Failed to get presigned URL" not in str(e): + raise Exception( + f"Unexpected error getting presigned URL for {guid}: {e}" + ) + raise + + @staticmethod + async def _download_content(session, url, guid, filepath): + """Download content directly to file with optimized streaming.""" + try: + async with session.get( + url, timeout=aiohttp.ClientTimeout(total=None) + ) as resp: + if resp.status == 200: + async with aiofiles.open(filepath, "wb") as f: + chunk_size = 1024 * 1024 + async for chunk in resp.content.iter_chunked(chunk_size): + await f.write(chunk) + return True + logging.error(f"Download failed for {guid}: HTTP {resp.status}") + return False + except aiohttp.ClientError as e: + logging.error(f"Network error downloading {guid}: {e}") + return False + except asyncio.TimeoutError: + logging.error(f"Timeout downloading {guid}") + return False + except OSError as e: + logging.error(f"File system error downloading {guid} to {filepath}: {e}") + return False + except Exception as e: + logging.error( + f"Unexpected error downloading {guid}: {type(e).__name__}: {e}" + ) + return False + + @staticmethod + def _format_filename_static(guid, original_filename, filename_format): + if filename_format == "guid": + return guid + elif filename_format == "combined": + if original_filename: + name, ext = os.path.splitext(original_filename) + return f"{name}_{guid}{ext}" + return guid + else: + return original_filename or guid + + @staticmethod + def _handle_conflict_static(filepath, rename): + if not rename: + if filepath.exists(): + logging.warning(f"File will be overwritten: {filepath}") + return filepath + + if not filepath.exists(): + return filepath + + counter = 1 + name = filepath.stem + ext = filepath.suffix + parent = filepath.parent + + while True: + new_path = parent / f"{name}_{counter}{ext}" + if not new_path.exists(): + return new_path + counter += 1
diff --git a/docs/_build/html/file.html b/docs/_build/html/file.html index cb970d07..aae09d2d 100644 --- a/docs/_build/html/file.html +++ b/docs/_build/html/file.html @@ -54,6 +54,12 @@

Gen3 File Class... file = Gen3File(auth)

+
+
+async async_download_multiple(manifest_data, download_path='.', filename_format='original', protocol=None, max_concurrent_requests=300, num_processes=3, queue_size=1000, skip_completed=False, rename=False, no_progress=False)[source]
+

Asynchronously download multiple files using multiprocessing and queues.

+
+
delete_file(guid)[source]
@@ -91,7 +97,7 @@

Gen3 File Class
-download_single(object_id, path)[source]
+download_single(object_id, path, protocol=None)[source]

Download a single file using its GUID.

Parameters:
@@ -100,6 +106,12 @@

Gen3 File ClassReturns: +

True if download successful, False otherwise

+
+
Return type:
+

bool

+

@@ -122,6 +134,26 @@

Gen3 File Class +
+get_presigned_urls_batch(guids, protocol=None)[source]
+

Get presigned URLs for multiple files efficiently.

+
+
Parameters:
+
    +
  • guids (List[str]) – List of GUIDs to get presigned URLs for

  • +
  • protocol (str, optional) – Protocol preference for URLs

  • +
+
+
Returns:
+

Mapping of GUID to presigned URL response

+
+
Return type:
+

Dict[str, Dict]

+
+
+
+
upload_file(file_name, authz=None, protocol=None, expires_in=None, bucket=None)[source]
@@ -218,10 +250,12 @@

Gen3 SDK

  • Gen3 Auth Helper
  • Gen3 File Class - +
    • get_fresh_token() (gen3.tools.download.drs_download.DownloadManager method)
    • get_graphql_schema() (gen3.submission.Gen3Submission method) @@ -369,6 +371,8 @@

      G

    • get_output() (gen3.jobs.Gen3Jobs method)
    • get_presigned_url() (gen3.file.Gen3File method) +
    • +
    • get_presigned_urls_batch() (gen3.file.Gen3File method)
    • get_programs() (gen3.submission.Gen3Submission method)
    • diff --git a/docs/_build/html/index.html b/docs/_build/html/index.html index d329e8bc..cd7e2af8 100644 --- a/docs/_build/html/index.html +++ b/docs/_build/html/index.html @@ -50,10 +50,12 @@

      Welcome to Gen3 SDK’s documentation!Gen3 File Class
      • Gen3File diff --git a/docs/_build/html/objects.inv b/docs/_build/html/objects.inv index 8f2fb369..cef04df4 100644 Binary files a/docs/_build/html/objects.inv and b/docs/_build/html/objects.inv differ diff --git a/docs/_build/html/searchindex.js b/docs/_build/html/searchindex.js index 9928858b..748fb2a7 100644 --- a/docs/_build/html/searchindex.js +++ b/docs/_build/html/searchindex.js @@ -1 +1 @@ -Search.setIndex({"alltitles":{"DRS Download Tools":[[10,null]],"Download":[[11,"module-gen3.tools.indexing.download_manifest"]],"Gen3 Auth Helper":[[0,null]],"Gen3 File Class":[[1,null]],"Gen3 Index Class":[[3,null]],"Gen3 Jobs Class":[[4,null]],"Gen3 Metadata Class":[[5,null]],"Gen3 Object Class":[[6,null]],"Gen3 Query Class":[[7,null]],"Gen3 Submission Class":[[8,null]],"Gen3 Tools":[[9,null]],"Gen3 Workspace Storage":[[13,null]],"Index":[[11,"module-gen3.tools.indexing.index_manifest"]],"Indexing Tools":[[11,null]],"Indices and tables":[[2,"indices-and-tables"]],"Ingest":[[12,"module-gen3.tools.metadata.ingest_manifest"]],"Metadata Tools":[[12,null]],"Verify":[[11,"module-gen3.tools.indexing.verify_manifest"]],"Welcome to Gen3 SDK\u2019s documentation!":[[2,null]]},"docnames":["auth","file","index","indexing","jobs","metadata","object","query","submission","tools","tools/drs_pull","tools/indexing","tools/metadata","wss"],"envversion":{"sphinx":66,"sphinx.domains.c":3,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":9,"sphinx.domains.index":1,"sphinx.domains.javascript":3,"sphinx.domains.math":2,"sphinx.domains.python":4,"sphinx.domains.rst":2,"sphinx.domains.std":2,"sphinx.ext.viewcode":1},"filenames":["auth.rst","file.rst","index.rst","indexing.rst","jobs.rst","metadata.rst","object.rst","query.rst","submission.rst","tools.rst","tools/drs_pull.rst","tools/indexing.rst","tools/metadata.rst","wss.rst"],"indexentries":{"_manager (gen3.tools.download.drs_download.downloadable attribute)":[[10,"gen3.tools.download.drs_download.Downloadable._manager",false]],"access_methods (gen3.tools.download.drs_download.downloadable attribute)":[[10,"gen3.tools.download.drs_download.Downloadable.access_methods",false]],"acls (in module gen3.tools.indexing.index_manifest)":[[11,"gen3.tools.indexing.index_manifest.ACLS",false]],"async_create() (gen3.metadata.gen3metadata method)":[[5,"gen3.metadata.Gen3Metadata.async_create",false]],"async_create_aliases() (gen3.metadata.gen3metadata method)":[[5,"gen3.metadata.Gen3Metadata.async_create_aliases",false]],"async_create_record() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.async_create_record",false]],"async_delete_alias() (gen3.metadata.gen3metadata method)":[[5,"gen3.metadata.Gen3Metadata.async_delete_alias",false]],"async_delete_aliases() (gen3.metadata.gen3metadata method)":[[5,"gen3.metadata.Gen3Metadata.async_delete_aliases",false]],"async_download_object_manifest() (in module gen3.tools.indexing.download_manifest)":[[11,"gen3.tools.indexing.download_manifest.async_download_object_manifest",false]],"async_get() (gen3.metadata.gen3metadata method)":[[5,"gen3.metadata.Gen3Metadata.async_get",false]],"async_get_aliases() (gen3.metadata.gen3metadata method)":[[5,"gen3.metadata.Gen3Metadata.async_get_aliases",false]],"async_get_record() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.async_get_record",false]],"async_get_records_from_checksum() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.async_get_records_from_checksum",false]],"async_get_records_on_page() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.async_get_records_on_page",false]],"async_get_with_params() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.async_get_with_params",false]],"async_ingest_metadata_manifest() (in module gen3.tools.metadata.ingest_manifest)":[[12,"gen3.tools.metadata.ingest_manifest.async_ingest_metadata_manifest",false]],"async_query_urls() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.async_query_urls",false]],"async_query_urls_from_indexd() (in module gen3.tools.metadata.ingest_manifest)":[[12,"gen3.tools.metadata.ingest_manifest.async_query_urls_from_indexd",false]],"async_run_job_and_wait() (gen3.jobs.gen3jobs method)":[[4,"gen3.jobs.Gen3Jobs.async_run_job_and_wait",false]],"async_update() (gen3.metadata.gen3metadata method)":[[5,"gen3.metadata.Gen3Metadata.async_update",false]],"async_update_aliases() (gen3.metadata.gen3metadata method)":[[5,"gen3.metadata.Gen3Metadata.async_update_aliases",false]],"async_update_record() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.async_update_record",false]],"async_verify_object_manifest() (in module gen3.tools.indexing.verify_manifest)":[[11,"gen3.tools.indexing.verify_manifest.async_verify_object_manifest",false]],"auth_provider (gen3.metadata.gen3metadata attribute)":[[5,"gen3.metadata.Gen3Metadata.auth_provider",false]],"authz (in module gen3.tools.indexing.index_manifest)":[[11,"gen3.tools.indexing.index_manifest.AUTHZ",false]],"batch_create() (gen3.metadata.gen3metadata method)":[[5,"gen3.metadata.Gen3Metadata.batch_create",false]],"cache_hosts_wts_tokens() (gen3.tools.download.drs_download.downloadmanager method)":[[10,"gen3.tools.download.drs_download.DownloadManager.cache_hosts_wts_tokens",false]],"children (gen3.tools.download.drs_download.downloadable attribute)":[[10,"gen3.tools.download.drs_download.Downloadable.children",false]],"column_to_use_as_guid (in module gen3.tools.metadata.ingest_manifest)":[[12,"gen3.tools.metadata.ingest_manifest.COLUMN_TO_USE_AS_GUID",false]],"commons_url (gen3.tools.download.drs_download.manifest attribute)":[[10,"gen3.tools.download.drs_download.Manifest.commons_url",false]],"copy() (gen3.wss.gen3wsstorage method)":[[13,"gen3.wss.Gen3WsStorage.copy",false]],"create() (gen3.metadata.gen3metadata method)":[[5,"gen3.metadata.Gen3Metadata.create",false]],"create_aliases() (gen3.metadata.gen3metadata method)":[[5,"gen3.metadata.Gen3Metadata.create_aliases",false]],"create_blank() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.create_blank",false]],"create_index_key_path() (gen3.metadata.gen3metadata method)":[[5,"gen3.metadata.Gen3Metadata.create_index_key_path",false]],"create_job() (gen3.jobs.gen3jobs method)":[[4,"gen3.jobs.Gen3Jobs.create_job",false]],"create_new_version() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.create_new_version",false]],"create_object_list() (gen3.tools.download.drs_download.manifest static method)":[[10,"gen3.tools.download.drs_download.Manifest.create_object_list",false]],"create_program() (gen3.submission.gen3submission method)":[[8,"gen3.submission.Gen3Submission.create_program",false]],"create_project() (gen3.submission.gen3submission method)":[[8,"gen3.submission.Gen3Submission.create_project",false]],"create_record() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.create_record",false]],"created_time (gen3.tools.download.drs_download.downloadable attribute)":[[10,"gen3.tools.download.drs_download.Downloadable.created_time",false]],"curl() (gen3.auth.gen3auth method)":[[0,"gen3.auth.Gen3Auth.curl",false]],"current_dir (in module gen3.tools.indexing.download_manifest)":[[11,"gen3.tools.indexing.download_manifest.CURRENT_DIR",false]],"current_dir (in module gen3.tools.indexing.index_manifest)":[[11,"gen3.tools.indexing.index_manifest.CURRENT_DIR",false]],"current_dir (in module gen3.tools.indexing.verify_manifest)":[[11,"gen3.tools.indexing.verify_manifest.CURRENT_DIR",false]],"delete() (gen3.metadata.gen3metadata method)":[[5,"gen3.metadata.Gen3Metadata.delete",false]],"delete_alias() (gen3.metadata.gen3metadata method)":[[5,"gen3.metadata.Gen3Metadata.delete_alias",false]],"delete_aliases() (gen3.metadata.gen3metadata method)":[[5,"gen3.metadata.Gen3Metadata.delete_aliases",false]],"delete_all_guids() (in module gen3.tools.indexing.index_manifest)":[[11,"gen3.tools.indexing.index_manifest.delete_all_guids",false]],"delete_file() (gen3.file.gen3file method)":[[1,"gen3.file.Gen3File.delete_file",false]],"delete_file_locations() (gen3.file.gen3file method)":[[1,"gen3.file.Gen3File.delete_file_locations",false]],"delete_index_key_path() (gen3.metadata.gen3metadata method)":[[5,"gen3.metadata.Gen3Metadata.delete_index_key_path",false]],"delete_node() (gen3.submission.gen3submission method)":[[8,"gen3.submission.Gen3Submission.delete_node",false]],"delete_nodes() (gen3.submission.gen3submission method)":[[8,"gen3.submission.Gen3Submission.delete_nodes",false]],"delete_object() (gen3.object.gen3object method)":[[6,"gen3.object.Gen3Object.delete_object",false]],"delete_program() (gen3.submission.gen3submission method)":[[8,"gen3.submission.Gen3Submission.delete_program",false]],"delete_project() (gen3.submission.gen3submission method)":[[8,"gen3.submission.Gen3Submission.delete_project",false]],"delete_record() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.delete_record",false]],"delete_record() (gen3.submission.gen3submission method)":[[8,"gen3.submission.Gen3Submission.delete_record",false]],"delete_records() (gen3.submission.gen3submission method)":[[8,"gen3.submission.Gen3Submission.delete_records",false]],"download() (gen3.tools.download.drs_download.downloadable method)":[[10,"gen3.tools.download.drs_download.Downloadable.download",false]],"download() (gen3.tools.download.drs_download.downloadmanager method)":[[10,"gen3.tools.download.drs_download.DownloadManager.download",false]],"download() (gen3.wss.gen3wsstorage method)":[[13,"gen3.wss.Gen3WsStorage.download",false]],"download_files_in_drs_manifest() (in module gen3.tools.download.drs_download)":[[10,"gen3.tools.download.drs_download.download_files_in_drs_manifest",false]],"download_single() (gen3.file.gen3file method)":[[1,"gen3.file.Gen3File.download_single",false]],"download_url() (gen3.wss.gen3wsstorage method)":[[13,"gen3.wss.Gen3WsStorage.download_url",false]],"downloadable (class in gen3.tools.download.drs_download)":[[10,"gen3.tools.download.drs_download.Downloadable",false]],"downloadmanager (class in gen3.tools.download.drs_download)":[[10,"gen3.tools.download.drs_download.DownloadManager",false]],"downloadstatus (class in gen3.tools.download.drs_download)":[[10,"gen3.tools.download.drs_download.DownloadStatus",false]],"end_time (gen3.tools.download.drs_download.downloadstatus attribute)":[[10,"gen3.tools.download.drs_download.DownloadStatus.end_time",false]],"endpoint (gen3.metadata.gen3metadata attribute)":[[5,"gen3.metadata.Gen3Metadata.endpoint",false]],"export_node() (gen3.submission.gen3submission method)":[[8,"gen3.submission.Gen3Submission.export_node",false]],"export_record() (gen3.submission.gen3submission method)":[[8,"gen3.submission.Gen3Submission.export_record",false]],"file_name (gen3.tools.download.drs_download.downloadable attribute)":[[10,"gen3.tools.download.drs_download.Downloadable.file_name",false]],"file_name (gen3.tools.download.drs_download.manifest attribute)":[[10,"gen3.tools.download.drs_download.Manifest.file_name",false]],"file_size (gen3.tools.download.drs_download.downloadable attribute)":[[10,"gen3.tools.download.drs_download.Downloadable.file_size",false]],"file_size (gen3.tools.download.drs_download.manifest attribute)":[[10,"gen3.tools.download.drs_download.Manifest.file_size",false]],"gen3.tools":[[9,"module-gen3.tools",false]],"gen3.tools.download.drs_download":[[10,"module-gen3.tools.download.drs_download",false]],"gen3.tools.indexing.download_manifest":[[11,"module-gen3.tools.indexing.download_manifest",false]],"gen3.tools.indexing.index_manifest":[[11,"module-gen3.tools.indexing.index_manifest",false]],"gen3.tools.indexing.verify_manifest":[[11,"module-gen3.tools.indexing.verify_manifest",false]],"gen3.tools.metadata.ingest_manifest":[[12,"module-gen3.tools.metadata.ingest_manifest",false]],"gen3auth (class in gen3.auth)":[[0,"gen3.auth.Gen3Auth",false]],"gen3file (class in gen3.file)":[[1,"gen3.file.Gen3File",false]],"gen3index (class in gen3.index)":[[3,"gen3.index.Gen3Index",false]],"gen3jobs (class in gen3.jobs)":[[4,"gen3.jobs.Gen3Jobs",false]],"gen3metadata (class in gen3.metadata)":[[5,"gen3.metadata.Gen3Metadata",false]],"gen3object (class in gen3.object)":[[6,"gen3.object.Gen3Object",false]],"gen3query (class in gen3.query)":[[7,"gen3.query.Gen3Query",false]],"gen3submission (class in gen3.submission)":[[8,"gen3.submission.Gen3Submission",false]],"gen3wsstorage (class in gen3.wss)":[[13,"gen3.wss.Gen3WsStorage",false]],"get() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.get",false]],"get() (gen3.metadata.gen3metadata method)":[[5,"gen3.metadata.Gen3Metadata.get",false]],"get_access_token() (gen3.auth.gen3auth method)":[[0,"gen3.auth.Gen3Auth.get_access_token",false]],"get_access_token_from_wts() (gen3.auth.gen3auth method)":[[0,"gen3.auth.Gen3Auth.get_access_token_from_wts",false]],"get_aliases() (gen3.metadata.gen3metadata method)":[[5,"gen3.metadata.Gen3Metadata.get_aliases",false]],"get_all_records() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.get_all_records",false]],"get_dictionary_all() (gen3.submission.gen3submission method)":[[8,"gen3.submission.Gen3Submission.get_dictionary_all",false]],"get_dictionary_node() (gen3.submission.gen3submission method)":[[8,"gen3.submission.Gen3Submission.get_dictionary_node",false]],"get_fresh_token() (gen3.tools.download.drs_download.downloadmanager method)":[[10,"gen3.tools.download.drs_download.DownloadManager.get_fresh_token",false]],"get_graphql_schema() (gen3.submission.gen3submission method)":[[8,"gen3.submission.Gen3Submission.get_graphql_schema",false]],"get_guids_prefix() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.get_guids_prefix",false]],"get_index_key_paths() (gen3.metadata.gen3metadata method)":[[5,"gen3.metadata.Gen3Metadata.get_index_key_paths",false]],"get_latest_version() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.get_latest_version",false]],"get_output() (gen3.jobs.gen3jobs method)":[[4,"gen3.jobs.Gen3Jobs.get_output",false]],"get_presigned_url() (gen3.file.gen3file method)":[[1,"gen3.file.Gen3File.get_presigned_url",false]],"get_programs() (gen3.submission.gen3submission method)":[[8,"gen3.submission.Gen3Submission.get_programs",false]],"get_project_dictionary() (gen3.submission.gen3submission method)":[[8,"gen3.submission.Gen3Submission.get_project_dictionary",false]],"get_project_manifest() (gen3.submission.gen3submission method)":[[8,"gen3.submission.Gen3Submission.get_project_manifest",false]],"get_projects() (gen3.submission.gen3submission method)":[[8,"gen3.submission.Gen3Submission.get_projects",false]],"get_record() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.get_record",false]],"get_record_doc() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.get_record_doc",false]],"get_records() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.get_records",false]],"get_records_on_page() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.get_records_on_page",false]],"get_stats() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.get_stats",false]],"get_status() (gen3.jobs.gen3jobs method)":[[4,"gen3.jobs.Gen3Jobs.get_status",false]],"get_urls() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.get_urls",false]],"get_valid_guids() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.get_valid_guids",false]],"get_version() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.get_version",false]],"get_version() (gen3.jobs.gen3jobs method)":[[4,"gen3.jobs.Gen3Jobs.get_version",false]],"get_version() (gen3.metadata.gen3metadata method)":[[5,"gen3.metadata.Gen3Metadata.get_version",false]],"get_versions() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.get_versions",false]],"get_with_params() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.get_with_params",false]],"graphql_query() (gen3.query.gen3query method)":[[7,"gen3.query.Gen3Query.graphql_query",false]],"guid (in module gen3.tools.indexing.index_manifest)":[[11,"gen3.tools.indexing.index_manifest.GUID",false]],"guid_type_for_indexed_file_object (in module gen3.tools.metadata.ingest_manifest)":[[12,"gen3.tools.metadata.ingest_manifest.GUID_TYPE_FOR_INDEXED_FILE_OBJECT",false]],"guid_type_for_non_indexed_file_object (in module gen3.tools.metadata.ingest_manifest)":[[12,"gen3.tools.metadata.ingest_manifest.GUID_TYPE_FOR_NON_INDEXED_FILE_OBJECT",false]],"hostname (gen3.tools.download.drs_download.downloadable attribute)":[[10,"gen3.tools.download.drs_download.Downloadable.hostname",false]],"index_object_manifest() (in module gen3.tools.indexing.index_manifest)":[[11,"gen3.tools.indexing.index_manifest.index_object_manifest",false]],"indexd_record_page_size (in module gen3.tools.indexing.download_manifest)":[[11,"gen3.tools.indexing.download_manifest.INDEXD_RECORD_PAGE_SIZE",false]],"is_healthy() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.is_healthy",false]],"is_healthy() (gen3.jobs.gen3jobs method)":[[4,"gen3.jobs.Gen3Jobs.is_healthy",false]],"is_healthy() (gen3.metadata.gen3metadata method)":[[5,"gen3.metadata.Gen3Metadata.is_healthy",false]],"list_access_in_drs_manifest() (in module gen3.tools.download.drs_download)":[[10,"gen3.tools.download.drs_download.list_access_in_drs_manifest",false]],"list_drs_object() (in module gen3.tools.download.drs_download)":[[10,"gen3.tools.download.drs_download.list_drs_object",false]],"list_files_in_drs_manifest() (in module gen3.tools.download.drs_download)":[[10,"gen3.tools.download.drs_download.list_files_in_drs_manifest",false]],"list_jobs() (gen3.jobs.gen3jobs method)":[[4,"gen3.jobs.Gen3Jobs.list_jobs",false]],"load() (gen3.tools.download.drs_download.manifest static method)":[[10,"gen3.tools.download.drs_download.Manifest.load",false]],"load_manifest() (gen3.tools.download.drs_download.manifest static method)":[[10,"gen3.tools.download.drs_download.Manifest.load_manifest",false]],"ls() (gen3.wss.gen3wsstorage method)":[[13,"gen3.wss.Gen3WsStorage.ls",false]],"ls_path() (gen3.wss.gen3wsstorage method)":[[13,"gen3.wss.Gen3WsStorage.ls_path",false]],"manifest (class in gen3.tools.download.drs_download)":[[10,"gen3.tools.download.drs_download.Manifest",false]],"max_concurrent_requests (in module gen3.tools.indexing.download_manifest)":[[11,"gen3.tools.indexing.download_manifest.MAX_CONCURRENT_REQUESTS",false]],"max_concurrent_requests (in module gen3.tools.indexing.verify_manifest)":[[11,"gen3.tools.indexing.verify_manifest.MAX_CONCURRENT_REQUESTS",false]],"max_concurrent_requests (in module gen3.tools.metadata.ingest_manifest)":[[12,"gen3.tools.metadata.ingest_manifest.MAX_CONCURRENT_REQUESTS",false]],"md5 (in module gen3.tools.indexing.index_manifest)":[[11,"gen3.tools.indexing.index_manifest.MD5",false]],"md5sum (gen3.tools.download.drs_download.manifest attribute)":[[10,"gen3.tools.download.drs_download.Manifest.md5sum",false]],"module":[[9,"module-gen3.tools",false],[10,"module-gen3.tools.download.drs_download",false],[11,"module-gen3.tools.indexing.download_manifest",false],[11,"module-gen3.tools.indexing.index_manifest",false],[11,"module-gen3.tools.indexing.verify_manifest",false],[12,"module-gen3.tools.metadata.ingest_manifest",false]],"object_id (gen3.tools.download.drs_download.downloadable attribute)":[[10,"gen3.tools.download.drs_download.Downloadable.object_id",false]],"object_id (gen3.tools.download.drs_download.manifest attribute)":[[10,"gen3.tools.download.drs_download.Manifest.object_id",false]],"object_type (gen3.tools.download.drs_download.downloadable attribute)":[[10,"gen3.tools.download.drs_download.Downloadable.object_type",false]],"open_project() (gen3.submission.gen3submission method)":[[8,"gen3.submission.Gen3Submission.open_project",false]],"pprint() (gen3.tools.download.drs_download.downloadable method)":[[10,"gen3.tools.download.drs_download.Downloadable.pprint",false]],"prev_guid (in module gen3.tools.indexing.index_manifest)":[[11,"gen3.tools.indexing.index_manifest.PREV_GUID",false]],"query() (gen3.metadata.gen3metadata method)":[[5,"gen3.metadata.Gen3Metadata.query",false]],"query() (gen3.query.gen3query method)":[[7,"gen3.query.Gen3Query.query",false]],"query() (gen3.submission.gen3submission method)":[[8,"gen3.submission.Gen3Submission.query",false]],"query_urls() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.query_urls",false]],"raw_data_download() (gen3.query.gen3query method)":[[7,"gen3.query.Gen3Query.raw_data_download",false]],"refresh_access_token() (gen3.auth.gen3auth method)":[[0,"gen3.auth.Gen3Auth.refresh_access_token",false]],"resolve_objects() (gen3.tools.download.drs_download.downloadmanager method)":[[10,"gen3.tools.download.drs_download.DownloadManager.resolve_objects",false]],"rm() (gen3.wss.gen3wsstorage method)":[[13,"gen3.wss.Gen3WsStorage.rm",false]],"rm_path() (gen3.wss.gen3wsstorage method)":[[13,"gen3.wss.Gen3WsStorage.rm_path",false]],"size (in module gen3.tools.indexing.index_manifest)":[[11,"gen3.tools.indexing.index_manifest.SIZE",false]],"start_time (gen3.tools.download.drs_download.downloadstatus attribute)":[[10,"gen3.tools.download.drs_download.DownloadStatus.start_time",false]],"status (gen3.tools.download.drs_download.downloadstatus attribute)":[[10,"gen3.tools.download.drs_download.DownloadStatus.status",false]],"submit_file() (gen3.submission.gen3submission method)":[[8,"gen3.submission.Gen3Submission.submit_file",false]],"submit_record() (gen3.submission.gen3submission method)":[[8,"gen3.submission.Gen3Submission.submit_record",false]],"threadcontrol (class in gen3.tools.indexing.index_manifest)":[[11,"gen3.tools.indexing.index_manifest.ThreadControl",false]],"tmp_folder (in module gen3.tools.indexing.download_manifest)":[[11,"gen3.tools.indexing.download_manifest.TMP_FOLDER",false]],"update() (gen3.metadata.gen3metadata method)":[[5,"gen3.metadata.Gen3Metadata.update",false]],"update_aliases() (gen3.metadata.gen3metadata method)":[[5,"gen3.metadata.Gen3Metadata.update_aliases",false]],"update_blank() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.update_blank",false]],"update_record() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.update_record",false]],"updated_time (gen3.tools.download.drs_download.downloadable attribute)":[[10,"gen3.tools.download.drs_download.Downloadable.updated_time",false]],"upload() (gen3.wss.gen3wsstorage method)":[[13,"gen3.wss.Gen3WsStorage.upload",false]],"upload_file() (gen3.file.gen3file method)":[[1,"gen3.file.Gen3File.upload_file",false]],"upload_file_to_guid() (gen3.file.gen3file method)":[[1,"gen3.file.Gen3File.upload_file_to_guid",false]],"upload_url() (gen3.wss.gen3wsstorage method)":[[13,"gen3.wss.Gen3WsStorage.upload_url",false]],"urls (in module gen3.tools.indexing.index_manifest)":[[11,"gen3.tools.indexing.index_manifest.URLS",false]],"user_access() (gen3.tools.download.drs_download.downloadmanager method)":[[10,"gen3.tools.download.drs_download.DownloadManager.user_access",false]]},"objects":{"gen3":[[9,3,0,"-","tools"]],"gen3.auth":[[0,0,1,"","Gen3Auth"]],"gen3.auth.Gen3Auth":[[0,1,1,"","curl"],[0,1,1,"","get_access_token"],[0,1,1,"","get_access_token_from_wts"],[0,1,1,"","refresh_access_token"]],"gen3.file":[[1,0,1,"","Gen3File"]],"gen3.file.Gen3File":[[1,1,1,"","delete_file"],[1,1,1,"","delete_file_locations"],[1,1,1,"","download_single"],[1,1,1,"","get_presigned_url"],[1,1,1,"","upload_file"],[1,1,1,"","upload_file_to_guid"]],"gen3.index":[[3,0,1,"","Gen3Index"]],"gen3.index.Gen3Index":[[3,1,1,"","async_create_record"],[3,1,1,"","async_get_record"],[3,1,1,"","async_get_records_from_checksum"],[3,1,1,"","async_get_records_on_page"],[3,1,1,"","async_get_with_params"],[3,1,1,"","async_query_urls"],[3,1,1,"","async_update_record"],[3,1,1,"","create_blank"],[3,1,1,"","create_new_version"],[3,1,1,"","create_record"],[3,1,1,"","delete_record"],[3,1,1,"","get"],[3,1,1,"","get_all_records"],[3,1,1,"","get_guids_prefix"],[3,1,1,"","get_latest_version"],[3,1,1,"","get_record"],[3,1,1,"","get_record_doc"],[3,1,1,"","get_records"],[3,1,1,"","get_records_on_page"],[3,1,1,"","get_stats"],[3,1,1,"","get_urls"],[3,1,1,"","get_valid_guids"],[3,1,1,"","get_version"],[3,1,1,"","get_versions"],[3,1,1,"","get_with_params"],[3,1,1,"","is_healthy"],[3,1,1,"","query_urls"],[3,1,1,"","update_blank"],[3,1,1,"","update_record"]],"gen3.jobs":[[4,0,1,"","Gen3Jobs"]],"gen3.jobs.Gen3Jobs":[[4,1,1,"","async_run_job_and_wait"],[4,1,1,"","create_job"],[4,1,1,"","get_output"],[4,1,1,"","get_status"],[4,1,1,"","get_version"],[4,1,1,"","is_healthy"],[4,1,1,"","list_jobs"]],"gen3.metadata":[[5,0,1,"","Gen3Metadata"]],"gen3.metadata.Gen3Metadata":[[5,1,1,"","async_create"],[5,1,1,"","async_create_aliases"],[5,1,1,"","async_delete_alias"],[5,1,1,"","async_delete_aliases"],[5,1,1,"","async_get"],[5,1,1,"","async_get_aliases"],[5,1,1,"","async_update"],[5,1,1,"","async_update_aliases"],[5,2,1,"","auth_provider"],[5,1,1,"","batch_create"],[5,1,1,"","create"],[5,1,1,"","create_aliases"],[5,1,1,"","create_index_key_path"],[5,1,1,"","delete"],[5,1,1,"","delete_alias"],[5,1,1,"","delete_aliases"],[5,1,1,"","delete_index_key_path"],[5,2,1,"","endpoint"],[5,1,1,"","get"],[5,1,1,"","get_aliases"],[5,1,1,"","get_index_key_paths"],[5,1,1,"","get_version"],[5,1,1,"","is_healthy"],[5,1,1,"","query"],[5,1,1,"","update"],[5,1,1,"","update_aliases"]],"gen3.object":[[6,0,1,"","Gen3Object"]],"gen3.object.Gen3Object":[[6,1,1,"","delete_object"]],"gen3.query":[[7,0,1,"","Gen3Query"]],"gen3.query.Gen3Query":[[7,1,1,"","graphql_query"],[7,1,1,"","query"],[7,1,1,"","raw_data_download"]],"gen3.submission":[[8,0,1,"","Gen3Submission"]],"gen3.submission.Gen3Submission":[[8,1,1,"","create_program"],[8,1,1,"","create_project"],[8,1,1,"","delete_node"],[8,1,1,"","delete_nodes"],[8,1,1,"","delete_program"],[8,1,1,"","delete_project"],[8,1,1,"","delete_record"],[8,1,1,"","delete_records"],[8,1,1,"","export_node"],[8,1,1,"","export_record"],[8,1,1,"","get_dictionary_all"],[8,1,1,"","get_dictionary_node"],[8,1,1,"","get_graphql_schema"],[8,1,1,"","get_programs"],[8,1,1,"","get_project_dictionary"],[8,1,1,"","get_project_manifest"],[8,1,1,"","get_projects"],[8,1,1,"","open_project"],[8,1,1,"","query"],[8,1,1,"","submit_file"],[8,1,1,"","submit_record"]],"gen3.tools.download":[[10,3,0,"-","drs_download"]],"gen3.tools.download.drs_download":[[10,0,1,"","DownloadManager"],[10,0,1,"","DownloadStatus"],[10,0,1,"","Downloadable"],[10,0,1,"","Manifest"],[10,4,1,"","download_files_in_drs_manifest"],[10,4,1,"","list_access_in_drs_manifest"],[10,4,1,"","list_drs_object"],[10,4,1,"","list_files_in_drs_manifest"]],"gen3.tools.download.drs_download.DownloadManager":[[10,1,1,"","cache_hosts_wts_tokens"],[10,1,1,"","download"],[10,1,1,"","get_fresh_token"],[10,1,1,"","resolve_objects"],[10,1,1,"","user_access"]],"gen3.tools.download.drs_download.DownloadStatus":[[10,2,1,"","end_time"],[10,2,1,"","start_time"],[10,2,1,"","status"]],"gen3.tools.download.drs_download.Downloadable":[[10,2,1,"","_manager"],[10,2,1,"","access_methods"],[10,2,1,"","children"],[10,2,1,"","created_time"],[10,1,1,"","download"],[10,2,1,"","file_name"],[10,2,1,"","file_size"],[10,2,1,"","hostname"],[10,2,1,"","object_id"],[10,2,1,"","object_type"],[10,1,1,"","pprint"],[10,2,1,"","updated_time"]],"gen3.tools.download.drs_download.Manifest":[[10,2,1,"","commons_url"],[10,1,1,"","create_object_list"],[10,2,1,"","file_name"],[10,2,1,"","file_size"],[10,1,1,"","load"],[10,1,1,"","load_manifest"],[10,2,1,"","md5sum"],[10,2,1,"","object_id"]],"gen3.tools.indexing":[[11,3,0,"-","download_manifest"],[11,3,0,"-","index_manifest"],[11,3,0,"-","verify_manifest"]],"gen3.tools.indexing.download_manifest":[[11,2,1,"","CURRENT_DIR"],[11,2,1,"","INDEXD_RECORD_PAGE_SIZE"],[11,2,1,"","MAX_CONCURRENT_REQUESTS"],[11,2,1,"","TMP_FOLDER"],[11,4,1,"","async_download_object_manifest"]],"gen3.tools.indexing.index_manifest":[[11,2,1,"","ACLS"],[11,2,1,"","AUTHZ"],[11,2,1,"","CURRENT_DIR"],[11,2,1,"","GUID"],[11,2,1,"","MD5"],[11,2,1,"","PREV_GUID"],[11,2,1,"","SIZE"],[11,0,1,"","ThreadControl"],[11,2,1,"","URLS"],[11,4,1,"","delete_all_guids"],[11,4,1,"","index_object_manifest"]],"gen3.tools.indexing.verify_manifest":[[11,2,1,"","CURRENT_DIR"],[11,2,1,"","MAX_CONCURRENT_REQUESTS"],[11,4,1,"","async_verify_object_manifest"]],"gen3.tools.metadata":[[12,3,0,"-","ingest_manifest"]],"gen3.tools.metadata.ingest_manifest":[[12,2,1,"","COLUMN_TO_USE_AS_GUID"],[12,2,1,"","GUID_TYPE_FOR_INDEXED_FILE_OBJECT"],[12,2,1,"","GUID_TYPE_FOR_NON_INDEXED_FILE_OBJECT"],[12,2,1,"","MAX_CONCURRENT_REQUESTS"],[12,4,1,"","async_ingest_metadata_manifest"],[12,4,1,"","async_query_urls_from_indexd"]],"gen3.wss":[[13,0,1,"","Gen3WsStorage"]],"gen3.wss.Gen3WsStorage":[[13,1,1,"","copy"],[13,1,1,"","download"],[13,1,1,"","download_url"],[13,1,1,"","ls"],[13,1,1,"","ls_path"],[13,1,1,"","rm"],[13,1,1,"","rm_path"],[13,1,1,"","upload"],[13,1,1,"","upload_url"]]},"objnames":{"0":["py","class","Python class"],"1":["py","method","Python method"],"2":["py","attribute","Python attribute"],"3":["py","module","Python module"],"4":["py","function","Python function"]},"objtypes":{"0":"py:class","1":"py:method","2":"py:attribute","3":"py:module","4":"py:function"},"terms":{"0a80fada010c":11,"0a80fada096c":11,"0a80fada097c":11,"0a80fada098c":11,"0a80fada099c":11,"11e9":11,"255e396f":11,"450c":11,"473d83400bc1bc9dc635e334fadd433c":11,"473d83400bc1bc9dc635e334faddd33c":11,"473d83400bc1bc9dc635e334fadde33c":11,"473d83400bc1bc9dc635e334faddf33c":11,"6f90":8,"7d3d8d2083b4":11,"93d9af72":11,"9a07":11,"A":[1,3,4,5,6,7,8,10,11,13],"ALL":7,"AND":5,"All":11,"Be":1,"But":5,"By":11,"For":[1,5,6,7,8,9,11],"IF":11,"If":[0,1,7,11,12],"In":10,"It":10,"Most":9,"NOT":12,"OR":5,"Same":13,"Such":9,"THE":11,"THIS":11,"That":3,"The":[0,1,2,3,5,8,10,11],"There":11,"These":9,"This":[0,1,2,3,4,5,6,7,8,10,11,13],"To":11,"We":11,"When":12,"YOU":11,"_get_acl_from_row":11,"_get_authz_from_row":11,"_get_file_name_from_row":11,"_get_file_size_from_row":11,"_get_guid_for_row":12,"_get_guid_from_row":11,"_get_md5_from_row":11,"_get_urls_from_row":11,"_guid_typ":12,"_manag":[2,9,10],"_query_for_associated_indexd_record_guid":12,"_ssl":[3,4,5],"a5c6":11,"ab167e49d25b488939b1ede42752458b":3,"abov":11,"access":[0,1,3,7,10],"access_method":[2,9,10],"access_token":0,"accesstoken":0,"acl":[2,3,9,11],"across":11,"act":0,"action":[9,11],"actual":11,"add":[3,5],"addit":[3,5,10,11],"admin":[5,11],"admin_endpoint_suffix":5,"algorithm":3,"alia":[3,5],"alias":5,"aliv":7,"allow":[0,6,8,10,11,12],"allowed_data_upload_bucket":1,"along":2,"alreadi":9,"also":1,"altern":[5,11],"alway":5,"ammount":12,"amount":[1,9],"ani":[0,5,10,11],"anoth":5,"api":[0,5,8,11],"api_key":11,"appli":7,"appropri":13,"arbitrari":0,"argument":[0,13],"array":8,"asc":7,"assign":9,"assist":10,"associ":[3,5],"assum":11,"async":[3,4,5,9,11,12],"async_cr":[2,5],"async_create_alias":[2,5],"async_create_record":[2,3],"async_delete_alia":[2,5],"async_delete_alias":[2,5],"async_download_object_manifest":[2,9,11],"async_get":[2,5],"async_get_alias":[2,5],"async_get_record":[2,3],"async_get_records_from_checksum":[2,3],"async_get_records_on_pag":[2,3],"async_get_with_param":[2,3],"async_ingest_metadata_manifest":[2,9,12],"async_query_url":[2,3],"async_query_urls_from_indexd":[2,9,12],"async_run_job_and_wait":[2,4],"async_upd":[2,5],"async_update_alias":[2,5],"async_update_record":[2,3],"async_verify_object_manifest":[2,9,11],"asynchron":[3,4,5],"asyncio":[11,12],"asyncron":5,"attach":[3,5],"attempt":11,"attribut":[10,11],"auth":[1,2,3,4,5,6,7,8,10,11,12,13],"auth_provid":[1,2,3,4,5,6,7,8,13],"authbas":0,"authent":0,"author":1,"authz":[0,1,2,3,9,10,11],"auto":[0,2],"automat":0,"avail":[1,2,10,11],"az":1,"b":[5,11],"b0f1":11,"bar":10,"base":[0,1,3,4,5,6,7,8,9,11,13],"baseid":3,"basic":[3,11,12],"batch_creat":[2,5],"batch_siz":8,"behalf":0,"behavior":11,"belong":8,"blank":3,"blob":[5,7],"bodi":3,"bool":[4,5,8,10,11,12],"boolean":3,"bownload":10,"broad":9,"broken":9,"bucket":[1,6],"bundl":10,"byte":10,"c":[5,11],"cach":10,"cache_hosts_wts_token":[2,9,10],"call":[10,13],"can":[0,3,4,8,11,12],"capabl":9,"case":[0,10],"categori":9,"ccle":8,"ccle_one_record":8,"ccle_sample_nod":8,"cdis":7,"chang":[3,11],"checksum":[3,10],"checksum_typ":3,"child":10,"children":[2,9,10],"chunk_siz":8,"class":[0,2,10,11,13],"cli":10,"client":[0,3],"client_credenti":0,"client_id":0,"client_scop":0,"client_secret":0,"code":[2,8],"column":[11,12],"column_to_use_as_guid":[2,9,12],"columna":11,"columnb":11,"columnc":11,"com":7,"comma":11,"command":[10,11],"common":[0,1,3,4,5,6,7,8,9,10,11,12,13],"commons_url":[2,9,10,11,12],"complet":[4,11],"complex":7,"concat":11,"concurr":[11,12],"configur":1,"connect":12,"consist":3,"constructor":0,"contain":[0,2,5,8,9,10,11,12],"content":[3,13],"content_created_d":3,"content_updated_d":3,"continu":10,"control":3,"conveni":10,"copi":[2,13],"coroutin":11,"correspond":3,"count":3,"crdc":0,"creat":[2,3,4,5,6,8,10,11],"create_alias":[2,5],"create_blank":[2,3],"create_index_key_path":[2,5],"create_job":[2,4],"create_new_vers":[2,3],"create_object_list":[2,9,10],"create_program":[2,8],"create_project":[2,8],"create_record":[2,3],"created_tim":[2,9,10],"creation":[3,11],"cred":3,"credenti":[0,1,3,4,5,6,7,8,10,11,13],"csv":[8,11,12],"curl":[0,2],"current":[6,8,10],"current_dir":[2,9,11],"custom":11,"d":5,"d70b41b9":8,"data":[0,1,3,5,7,8,10,11],"data_spreadsheet":8,"data_typ":7,"data_upload_bucket":1,"dataa":11,"datab":11,"databas":5,"datacommon":0,"datafil":10,"datamanag":10,"date":3,"datetim":[1,3,10],"dbgap":12,"dcf":8,"def":11,"default":[0,1,3,7,8,11,12],"defin":[5,8,10],"delay":4,"delet":[0,1,2,3,5,6,8,10,11],"delete_alia":[2,5],"delete_alias":[2,5],"delete_all_guid":[2,9,11],"delete_fil":[1,2],"delete_file_loc":[1,2,6],"delete_index_key_path":[2,5],"delete_nod":[2,8],"delete_object":[2,6],"delete_program":[2,8],"delete_project":[2,8],"delete_record":[2,3,8],"delete_unpacked_packag":10,"delimet":[11,12],"delimit":11,"demograph":8,"deprec":1,"descript":[3,5],"desir":11,"dest_path":13,"dest_urlstr":13,"dest_w":13,"dest_wskey":13,"detail":[2,7,10],"determin":[10,11,12],"dev":11,"dict":[3,4,5,10,11,12],"dictionari":[3,4,5,7,8],"dids":3,"differ":5,"direct":0,"directori":[10,11],"disabl":10,"discoveri":10,"disk":13,"dispatch":4,"dist_resolut":3,"distribut":3,"doc":[7,10],"docstr":2,"document":[1,3],"doe":[0,12],"domain":[11,12],"done":4,"download":[0,1,2,3,4,5,6,7,8,9,13],"download_files_in_drs_manifest":[2,9,10],"download_list":10,"download_manifest":11,"download_singl":[1,2],"download_url":[2,13],"downloadmanag":[2,9,10],"downloadstatus":[2,9,10],"drs":[2,9],"drs_download":10,"drs_hostnam":10,"drsdownload":10,"drsobjecttyp":10,"e":[5,10],"e043ab8b77b9":8,"effici":9,"eg":3,"either":8,"elasticsearch":7,"els":[0,12],"elsewher":12,"empti":[8,11],"enabl":11,"end":[5,10],"end_tim":[2,9,10],"endpoint":[0,1,2,3,4,5,7,8,13],"entir":8,"entri":[3,11],"env":0,"environ":0,"equal":7,"error":[10,11],"error_nam":11,"etc":8,"even":11,"everi":[9,11],"everyth":11,"ex":[0,11,12],"exampl":[0,1,3,4,5,6,7,8,10,11,13],"exclud":3,"execut":[7,8,11],"exist":[1,3,5,6,9,12],"expect":[5,9,11],"experi":8,"expir":[0,1],"expires_in":1,"export":[8,10],"export_nod":[2,8],"export_record":[2,8],"f1f8":11,"factori":10,"fail":[8,10],"fals":[3,5,6,10,11],"featur":[1,6],"fenc":[0,1],"fetch":0,"field":[3,5,7,11,12],"fieldnam":11,"file":[0,2,3,4,8,9,10,11,12,13],"file_nam":[1,2,3,9,10,11],"file_s":[2,9,10,11],"file_st":3,"fileformat":8,"filenam":[0,8,10,11,12],"files":10,"fill":12,"filter":[5,7],"filter_object":7,"first":[7,8],"flag":11,"folder":11,"follow":[0,11],"forc":11,"force_metadata_columns_even_if_empti":11,"form":13,"format":[3,5,8,11],"func_to_parse_row":[11,12],"function":[2,3,4,5,9,10,11,12],"g":10,"gen3":[10,11,12],"gen3_api_key":0,"gen3_oidc_client_creds_secret":0,"gen3auth":[0,1,2,3,4,5,6,7,8,10,11,12,13],"gen3fil":[1,2],"gen3index":[2,3],"gen3job":[2,4,10],"gen3metadata":[2,5],"gen3object":[2,6],"gen3queri":[2,7],"gen3submiss":[2,8],"gen3wsstorag":[2,13],"generat":[0,1,2,3,4,5,6,7,8,10,13],"get":[0,1,2,3,4,5,8,10,11,12,13],"get_access_token":[0,2],"get_access_token_from_wt":[0,2],"get_alias":[2,5],"get_all_record":[2,3],"get_dictionary_al":[2,8],"get_dictionary_nod":[2,8],"get_fresh_token":[2,9,10],"get_graphql_schema":[2,8],"get_guid_from_fil":12,"get_guids_prefix":[2,3],"get_index_key_path":[2,5],"get_latest_vers":[2,3],"get_output":[2,4],"get_presigned_url":[1,2],"get_program":[2,8],"get_project":[2,8],"get_project_dictionari":[2,8],"get_project_manifest":[2,8],"get_record":[2,3],"get_record_doc":[2,3],"get_records_on_pag":[2,3],"get_stat":[2,3],"get_status":[2,4],"get_url":[2,3],"get_valid_guid":[2,3],"get_vers":[2,3,4,5],"get_with_param":[2,3],"giangb":11,"github":[2,7],"give":1,"given":[0,3,4,5,8,10,12,13],"global":[4,5],"good":3,"grant":0,"graph":8,"graphql":[7,8],"graphql_queri":[2,7],"group":3,"guid":[1,2,3,5,6,9,11,12],"guid_exampl":11,"guid_for_row":12,"guid_from_fil":12,"guid_type_for_indexed_file_object":[2,9,12],"guid_type_for_non_indexed_file_object":[2,9,12],"guppi":7,"handl":[3,10],"hardcod":0,"has_vers":3,"hash":[3,11],"hash_typ":3,"header":11,"healthi":[3,4,5],"help":11,"helper":2,"hit":11,"host":10,"hostnam":[2,9,10],"howto":10,"http":12,"https":[0,7,11],"id":[0,1,3,5,10,11],"idea":3,"identifi":[3,5,9,11],"idp":0,"illustr":11,"immut":3,"implement":0,"implic":11,"import":11,"includ":[0,3],"indent":10,"index":[0,2,5,9],"index_manifest":11,"index_object_manifest":[2,9,11],"indexd":[1,3,6,10,11,12],"indexd_field":[11,12],"indexd_record_page_s":[2,9,11],"indexed_file_object_guid":12,"indic":[0,11],"infil":10,"info":[3,11],"inform":[2,3,10],"ingest":[2,9],"ingest_manifest":12,"initi":[0,10],"input":[4,10,11],"input_manifest":11,"instal":[0,2,11],"instanc":[1,3,6,7,8,9,10],"instead":[1,7,11],"int":[1,3,5,7,8,10,11,12],"integ":[1,3,8],"intend":0,"interact":[1,3,4,5,6,8,13],"interest":10,"interpret":0,"introspect":8,"involv":9,"is_healthi":[2,3,4,5],"is_indexed_file_object":12,"isn":1,"issu":0,"job":2,"job_id":4,"job_input":4,"job_nam":4,"json":[0,1,3,4,5,6,7,8,10,11,13],"just":[5,11,12],"jwt":0,"key":[0,3,5,13],"know":11,"known":10,"kwarg":[3,4,5],"larg":9,"last":10,"latest":3,"least":3,"level":6,"librari":11,"like":[3,5,9,11,12],"limit":[1,3,5,12],"linear":4,"linux":10,"list":[0,1,3,4,5,7,8,10,11,13],"list_access_in_drs_manifest":[2,9,10],"list_drs_object":[2,9,10],"list_files_in_drs_manifest":[2,9,10],"list_job":[2,4],"live":[11,12],"load":[2,9,10],"load_manifest":[2,9,10],"local":[0,13],"locat":[1,6],"lock":12,"log":[8,10,11,12],"logic":[5,12],"loop":11,"ls":[2,13],"ls_path":[2,13],"maco":11,"made":3,"main":10,"make":[9,11],"manag":[1,5,10],"mani":[8,11],"manifest":[2,8,9,10,11,12],"manifest_1":10,"manifest_fil":[11,12],"manifest_file_delimit":[11,12],"manifest_row_pars":[11,12],"map":[0,11],"mark":8,"master":7,"match":[3,5,12],"max":5,"max_concurrent_request":[2,9,11,12],"max_presigned_url_ttl":1,"max_tri":8,"maximum":[11,12],"may":[0,9,11],"md":[7,10],"md5":[2,3,9,11],"md5_hash":11,"md5sum":[2,9,10],"mds":[5,12],"mean":8,"mechan":3,"merg":5,"metadata":[2,3,6,9,11],"metadata_list":5,"metadata_sourc":12,"metadata_typ":12,"metdata":12,"method":[1,7,10],"minimum":10,"minut":0,"mode":7,"modul":[2,10,11],"mostly":2,"multipl":[8,11],"must":[1,5],"my_common":10,"my_credenti":10,"my_field":7,"my_index":7,"my_program":7,"my_project":7,"name":[3,4,8,10,11,12,13],"namespac":[0,12],"necessari":[3,5],"need":[3,7,10,11],"nest":5,"net":11,"never":0,"new":[0,3],"node":8,"node_nam":8,"node_typ":8,"none":[0,1,3,4,5,6,7,8,10,11,12,13],"note":[0,3,11,12],"noth":[3,6],"now":[1,8],"num":5,"num_process":11,"num_total_fil":11,"number":[3,7,8,11,12],"object":[1,2,3,4,5,7,8,9,10,11,13],"object_id":[1,2,9,10],"object_list":10,"object_typ":[2,9,10],"objectid":10,"obtain":[0,10],"occur":10,"offset":[5,7],"oidc":0,"old":3,"one":[3,5,7,10,11],"onli":[3,5,7,8,10,11],"open":[8,10,11],"open_project":[2,8],"openid":0,"opt":0,"option":[0,1,3,4,5,6,7,8,10,11],"order":[0,8],"ordered_node_list":8,"org":10,"os":0,"otherwis":10,"output":[4,5,11,12],"output_dir":10,"output_filenam":[11,12],"overrid":[0,11,12],"overwrit":5,"packag":10,"page":[0,1,2,3,4,5,6,7,8,10,11,13],"pagin":3,"parallel":11,"param":[3,5,8,10],"paramet":[0,1,3,4,5,6,7,8,10,11,12,13],"pars":[10,11,12,13],"parser":[11,12],"particular":0,"pass":[0,7,8,10],"password":[11,12],"path":[0,1,5,10,11,13],"path_to_manifest":11,"pattern":[3,12],"pdcdatastor":11,"pend":10,"per":[11,12],"peregrin":8,"permiss":10,"persist":9,"phs0001":11,"phs0002":11,"pick":1,"pla":11,"place":11,"planx":11,"point":[0,1,3,4,5,6,7,8,10,13],"popul":[10,12],"posit":[1,7],"possibl":10,"post":[0,11],"pprint":[2,9,10],"prefix":3,"presign":1,"pretti":10,"prev_guid":[2,9,11],"previous":[3,4,11],"print":[8,10],"process":11,"processed_fil":11,"profil":[0,1,3,4,5,6,7,8,10,13],"program":[8,11],"progress":[8,10],"project":[8,11],"project_id":[7,8],"protocol":1,"provid":[0,1,3,5,7,8,12],"public":[3,5],"put":0,"py":11,"python":[2,9,11],"python3":11,"python_subprocess_command":11,"queri":[1,2,3,5,8,11,12],"query_str":7,"query_txt":[7,8],"query_url":[2,3],"quickstart":2,"rather":0,"raw":[7,11],"raw_data_download":[2,7],"rbac":3,"read":[3,5,11],"readm":2,"reason":10,"record":[1,3,5,7,8,11,12],"refresh":[0,10],"refresh_access_token":[0,2],"refresh_fil":[0,1,3,4,5,6,7,8,10,13],"refresh_token":0,"regist":8,"regular":7,"relat":9,"remov":[1,6,11,13],"replac":11,"replace_url":11,"repo":2,"repres":[3,5,10],"represent":[1,3],"request":[0,1,3,5,8,11,12],"requir":10,"resolv":10,"resolve_object":[2,9,10],"respect":7,"respons":[0,1,3,4,5],"result":[1,8,10,11],"retri":8,"retriev":[1,8,10,12],"return":[0,1,3,4,5,6,7,8,10,11],"return_full_metadata":5,"rev":3,"revers":8,"revis":3,"right":1,"rm":[2,13],"rm_path":[2,13],"root":[11,12],"row":[7,8,11,12],"row_offset":8,"rtype":3,"run":[8,11],"s":[1,4,8,10,11],"s3":[1,10,11],"safe":11,"sampl":[8,10],"sandbox":[0,1,3,4,5,6,7,8,10,13],"save":10,"save_directori":10,"schema":8,"scope":[0,1],"screen":8,"script":2,"search":[0,2,3],"second":[1,4],"secret":0,"see":[7,10,11],"self":10,"semaphon":12,"semaphor":12,"separ":[0,11],"server":10,"servic":[1,3,4,5,6,8,11,12,13],"service_loc":[3,4,5],"session":11,"set":[0,1,5,10],"setup":2,"sheepdog":8,"show":10,"show_progress":10,"shown":11,"sign":1,"signpost":3,"similar":10,"simpl":3,"simpli":11,"sinc":3,"singl":[1,5,8],"size":[2,3,9,10,11],"skip":8,"sleep":4,"someth":11,"sort":7,"sort_field":7,"sort_object":7,"sourc":[0,1,2,3,4,5,6,7,8,10,11,12,13],"space":[0,11],"specif":[5,8,11,12],"specifi":[0,1,3,11,13],"spreadsheet":8,"src_path":13,"src_urlstr":13,"src_ws":13,"src_wskey":13,"ssl":[3,4,5],"start":[4,7,8,10],"start_tim":[2,9,10],"static":10,"status":[2,4,9,10],"status_cod":10,"storag":[1,2,6],"store":[1,3,10],"str":[0,1,3,4,5,7,8,10,11,12],"string":[0,3,5,11,13],"strip":11,"sub":8,"subject":[7,8],"submiss":2,"submit":[8,11],"submit_additional_metadata_column":11,"submit_fil":[2,8],"submit_record":[2,8],"submitter_id":7,"success":10,"suffici":3,"suppli":3,"support":[0,1,5,8,11],"sure":1,"synchron":11,"syntax":7,"system":[6,7,8,9],"t":[1,5,11],"tab":11,"task":9,"temporari":11,"test":11,"test1":11,"test2":11,"test3":11,"test4":11,"test5":11,"text":[1,7,8],"thread":11,"thread_num":11,"threadcontrol":[2,9,11],"tier":7,"time":[1,3,8,10,11],"timestamp":10,"tmp_folder":[2,9,11],"token":[0,10],"tool":2,"total":11,"treat":[1,5],"tree":10,"tri":0,"true":[3,4,5,6,7,8,10,11,12],"tsv":[8,11,12],"tupl":[0,3,11,12],"type":[1,3,4,5,7,8,10,11,12],"typic":10,"uc":7,"unaccess":7,"uniqu":[1,5],"unknown":10,"unpack":10,"unpack_packag":10,"updat":[2,3,5,10,11],"update_alias":[2,5],"update_blank":[2,3],"update_record":[2,3],"updated_tim":[2,9,10],"upload":[1,2,3,8,13],"upload_fil":[1,2],"upload_file_to_guid":[1,2],"upload_url":[2,13],"url":[1,2,3,9,10,11,12,13],"urls_metadata":3,"usag":11,"use":[0,1,3,4,5,6,7,8,10,11,12,13],"use_agg_md":5,"user":[0,10,12],"user_access":[2,9,10],"usual":12,"utcnow":1,"util":9,"uuid":[1,3,8],"uuid1":8,"uuid2":8,"valid":[3,7],"valu":[0,1,3,5,7,10,11],"value_from_indexd":11,"value_from_manifest":11,"variabl":[0,7,8],"various":2,"verbos":[7,8],"verif":11,"verifi":[2,9],"verify_manifest":11,"verify_object_manifest":11,"version":[3,4,5],"vital_status":7,"wait":4,"want":[0,3,8],"warn":11,"way":10,"web":0,"whether":[3,4,5,8,11,12],"whose":5,"will":[1,3,4,5,7,10,11,12],"within":[0,2,9],"without":[3,5],"won":5,"work":[0,10],"workaround":11,"worksheet":8,"workspac":[0,2],"wrapper":10,"write":11,"ws":13,"ws_urlstr":13,"wskey":13,"wss":13,"wts":[0,10],"x":11,"xlsx":8},"titles":["Gen3 Auth Helper","Gen3 File Class","Welcome to Gen3 SDK\u2019s documentation!","Gen3 Index Class","Gen3 Jobs Class","Gen3 Metadata Class","Gen3 Object Class","Gen3 Query Class","Gen3 Submission Class","Gen3 Tools","DRS Download Tools","Indexing Tools","Metadata Tools","Gen3 Workspace Storage"],"titleterms":{"auth":0,"class":[1,3,4,5,6,7,8],"document":2,"download":[10,11],"drs":10,"file":1,"gen3":[0,1,2,3,4,5,6,7,8,9,13],"helper":0,"index":[3,11],"indic":2,"ingest":12,"job":4,"metadata":[5,12],"object":6,"queri":7,"s":2,"sdk":2,"storag":13,"submiss":8,"tabl":2,"tool":[9,10,11,12],"verifi":11,"welcom":2,"workspac":13}}) \ No newline at end of file +Search.setIndex({"alltitles":{"DRS Download Tools":[[10,null]],"Download":[[11,"module-gen3.tools.indexing.download_manifest"]],"Gen3 Auth Helper":[[0,null]],"Gen3 File Class":[[1,null]],"Gen3 Index Class":[[3,null]],"Gen3 Jobs Class":[[4,null]],"Gen3 Metadata Class":[[5,null]],"Gen3 Object Class":[[6,null]],"Gen3 Query Class":[[7,null]],"Gen3 Submission Class":[[8,null]],"Gen3 Tools":[[9,null]],"Gen3 Workspace Storage":[[13,null]],"Index":[[11,"module-gen3.tools.indexing.index_manifest"]],"Indexing Tools":[[11,null]],"Indices and tables":[[2,"indices-and-tables"]],"Ingest":[[12,"module-gen3.tools.metadata.ingest_manifest"]],"Metadata Tools":[[12,null]],"Verify":[[11,"module-gen3.tools.indexing.verify_manifest"]],"Welcome to Gen3 SDK\u2019s documentation!":[[2,null]]},"docnames":["auth","file","index","indexing","jobs","metadata","object","query","submission","tools","tools/drs_pull","tools/indexing","tools/metadata","wss"],"envversion":{"sphinx":66,"sphinx.domains.c":3,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":9,"sphinx.domains.index":1,"sphinx.domains.javascript":3,"sphinx.domains.math":2,"sphinx.domains.python":4,"sphinx.domains.rst":2,"sphinx.domains.std":2,"sphinx.ext.viewcode":1},"filenames":["auth.rst","file.rst","index.rst","indexing.rst","jobs.rst","metadata.rst","object.rst","query.rst","submission.rst","tools.rst","tools/drs_pull.rst","tools/indexing.rst","tools/metadata.rst","wss.rst"],"indexentries":{"_manager (gen3.tools.download.drs_download.downloadable attribute)":[[10,"gen3.tools.download.drs_download.Downloadable._manager",false]],"access_methods (gen3.tools.download.drs_download.downloadable attribute)":[[10,"gen3.tools.download.drs_download.Downloadable.access_methods",false]],"acls (in module gen3.tools.indexing.index_manifest)":[[11,"gen3.tools.indexing.index_manifest.ACLS",false]],"async_create() (gen3.metadata.gen3metadata method)":[[5,"gen3.metadata.Gen3Metadata.async_create",false]],"async_create_aliases() (gen3.metadata.gen3metadata method)":[[5,"gen3.metadata.Gen3Metadata.async_create_aliases",false]],"async_create_record() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.async_create_record",false]],"async_delete_alias() (gen3.metadata.gen3metadata method)":[[5,"gen3.metadata.Gen3Metadata.async_delete_alias",false]],"async_delete_aliases() (gen3.metadata.gen3metadata method)":[[5,"gen3.metadata.Gen3Metadata.async_delete_aliases",false]],"async_download_multiple() (gen3.file.gen3file method)":[[1,"gen3.file.Gen3File.async_download_multiple",false]],"async_download_object_manifest() (in module gen3.tools.indexing.download_manifest)":[[11,"gen3.tools.indexing.download_manifest.async_download_object_manifest",false]],"async_get() (gen3.metadata.gen3metadata method)":[[5,"gen3.metadata.Gen3Metadata.async_get",false]],"async_get_aliases() (gen3.metadata.gen3metadata method)":[[5,"gen3.metadata.Gen3Metadata.async_get_aliases",false]],"async_get_record() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.async_get_record",false]],"async_get_records_from_checksum() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.async_get_records_from_checksum",false]],"async_get_records_on_page() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.async_get_records_on_page",false]],"async_get_with_params() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.async_get_with_params",false]],"async_ingest_metadata_manifest() (in module gen3.tools.metadata.ingest_manifest)":[[12,"gen3.tools.metadata.ingest_manifest.async_ingest_metadata_manifest",false]],"async_query_urls() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.async_query_urls",false]],"async_query_urls_from_indexd() (in module gen3.tools.metadata.ingest_manifest)":[[12,"gen3.tools.metadata.ingest_manifest.async_query_urls_from_indexd",false]],"async_run_job_and_wait() (gen3.jobs.gen3jobs method)":[[4,"gen3.jobs.Gen3Jobs.async_run_job_and_wait",false]],"async_update() (gen3.metadata.gen3metadata method)":[[5,"gen3.metadata.Gen3Metadata.async_update",false]],"async_update_aliases() (gen3.metadata.gen3metadata method)":[[5,"gen3.metadata.Gen3Metadata.async_update_aliases",false]],"async_update_record() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.async_update_record",false]],"async_verify_object_manifest() (in module gen3.tools.indexing.verify_manifest)":[[11,"gen3.tools.indexing.verify_manifest.async_verify_object_manifest",false]],"auth_provider (gen3.metadata.gen3metadata attribute)":[[5,"gen3.metadata.Gen3Metadata.auth_provider",false]],"authz (in module gen3.tools.indexing.index_manifest)":[[11,"gen3.tools.indexing.index_manifest.AUTHZ",false]],"batch_create() (gen3.metadata.gen3metadata method)":[[5,"gen3.metadata.Gen3Metadata.batch_create",false]],"cache_hosts_wts_tokens() (gen3.tools.download.drs_download.downloadmanager method)":[[10,"gen3.tools.download.drs_download.DownloadManager.cache_hosts_wts_tokens",false]],"children (gen3.tools.download.drs_download.downloadable attribute)":[[10,"gen3.tools.download.drs_download.Downloadable.children",false]],"column_to_use_as_guid (in module gen3.tools.metadata.ingest_manifest)":[[12,"gen3.tools.metadata.ingest_manifest.COLUMN_TO_USE_AS_GUID",false]],"commons_url (gen3.tools.download.drs_download.manifest attribute)":[[10,"gen3.tools.download.drs_download.Manifest.commons_url",false]],"copy() (gen3.wss.gen3wsstorage method)":[[13,"gen3.wss.Gen3WsStorage.copy",false]],"create() (gen3.metadata.gen3metadata method)":[[5,"gen3.metadata.Gen3Metadata.create",false]],"create_aliases() (gen3.metadata.gen3metadata method)":[[5,"gen3.metadata.Gen3Metadata.create_aliases",false]],"create_blank() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.create_blank",false]],"create_index_key_path() (gen3.metadata.gen3metadata method)":[[5,"gen3.metadata.Gen3Metadata.create_index_key_path",false]],"create_job() (gen3.jobs.gen3jobs method)":[[4,"gen3.jobs.Gen3Jobs.create_job",false]],"create_new_version() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.create_new_version",false]],"create_object_list() (gen3.tools.download.drs_download.manifest static method)":[[10,"gen3.tools.download.drs_download.Manifest.create_object_list",false]],"create_program() (gen3.submission.gen3submission method)":[[8,"gen3.submission.Gen3Submission.create_program",false]],"create_project() (gen3.submission.gen3submission method)":[[8,"gen3.submission.Gen3Submission.create_project",false]],"create_record() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.create_record",false]],"created_time (gen3.tools.download.drs_download.downloadable attribute)":[[10,"gen3.tools.download.drs_download.Downloadable.created_time",false]],"curl() (gen3.auth.gen3auth method)":[[0,"gen3.auth.Gen3Auth.curl",false]],"current_dir (in module gen3.tools.indexing.download_manifest)":[[11,"gen3.tools.indexing.download_manifest.CURRENT_DIR",false]],"current_dir (in module gen3.tools.indexing.index_manifest)":[[11,"gen3.tools.indexing.index_manifest.CURRENT_DIR",false]],"current_dir (in module gen3.tools.indexing.verify_manifest)":[[11,"gen3.tools.indexing.verify_manifest.CURRENT_DIR",false]],"delete() (gen3.metadata.gen3metadata method)":[[5,"gen3.metadata.Gen3Metadata.delete",false]],"delete_alias() (gen3.metadata.gen3metadata method)":[[5,"gen3.metadata.Gen3Metadata.delete_alias",false]],"delete_aliases() (gen3.metadata.gen3metadata method)":[[5,"gen3.metadata.Gen3Metadata.delete_aliases",false]],"delete_all_guids() (in module gen3.tools.indexing.index_manifest)":[[11,"gen3.tools.indexing.index_manifest.delete_all_guids",false]],"delete_file() (gen3.file.gen3file method)":[[1,"gen3.file.Gen3File.delete_file",false]],"delete_file_locations() (gen3.file.gen3file method)":[[1,"gen3.file.Gen3File.delete_file_locations",false]],"delete_index_key_path() (gen3.metadata.gen3metadata method)":[[5,"gen3.metadata.Gen3Metadata.delete_index_key_path",false]],"delete_node() (gen3.submission.gen3submission method)":[[8,"gen3.submission.Gen3Submission.delete_node",false]],"delete_nodes() (gen3.submission.gen3submission method)":[[8,"gen3.submission.Gen3Submission.delete_nodes",false]],"delete_object() (gen3.object.gen3object method)":[[6,"gen3.object.Gen3Object.delete_object",false]],"delete_program() (gen3.submission.gen3submission method)":[[8,"gen3.submission.Gen3Submission.delete_program",false]],"delete_project() (gen3.submission.gen3submission method)":[[8,"gen3.submission.Gen3Submission.delete_project",false]],"delete_record() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.delete_record",false]],"delete_record() (gen3.submission.gen3submission method)":[[8,"gen3.submission.Gen3Submission.delete_record",false]],"delete_records() (gen3.submission.gen3submission method)":[[8,"gen3.submission.Gen3Submission.delete_records",false]],"download() (gen3.tools.download.drs_download.downloadable method)":[[10,"gen3.tools.download.drs_download.Downloadable.download",false]],"download() (gen3.tools.download.drs_download.downloadmanager method)":[[10,"gen3.tools.download.drs_download.DownloadManager.download",false]],"download() (gen3.wss.gen3wsstorage method)":[[13,"gen3.wss.Gen3WsStorage.download",false]],"download_files_in_drs_manifest() (in module gen3.tools.download.drs_download)":[[10,"gen3.tools.download.drs_download.download_files_in_drs_manifest",false]],"download_single() (gen3.file.gen3file method)":[[1,"gen3.file.Gen3File.download_single",false]],"download_url() (gen3.wss.gen3wsstorage method)":[[13,"gen3.wss.Gen3WsStorage.download_url",false]],"downloadable (class in gen3.tools.download.drs_download)":[[10,"gen3.tools.download.drs_download.Downloadable",false]],"downloadmanager (class in gen3.tools.download.drs_download)":[[10,"gen3.tools.download.drs_download.DownloadManager",false]],"downloadstatus (class in gen3.tools.download.drs_download)":[[10,"gen3.tools.download.drs_download.DownloadStatus",false]],"end_time (gen3.tools.download.drs_download.downloadstatus attribute)":[[10,"gen3.tools.download.drs_download.DownloadStatus.end_time",false]],"endpoint (gen3.metadata.gen3metadata attribute)":[[5,"gen3.metadata.Gen3Metadata.endpoint",false]],"export_node() (gen3.submission.gen3submission method)":[[8,"gen3.submission.Gen3Submission.export_node",false]],"export_record() (gen3.submission.gen3submission method)":[[8,"gen3.submission.Gen3Submission.export_record",false]],"file_name (gen3.tools.download.drs_download.downloadable attribute)":[[10,"gen3.tools.download.drs_download.Downloadable.file_name",false]],"file_name (gen3.tools.download.drs_download.manifest attribute)":[[10,"gen3.tools.download.drs_download.Manifest.file_name",false]],"file_size (gen3.tools.download.drs_download.downloadable attribute)":[[10,"gen3.tools.download.drs_download.Downloadable.file_size",false]],"file_size (gen3.tools.download.drs_download.manifest attribute)":[[10,"gen3.tools.download.drs_download.Manifest.file_size",false]],"gen3.tools":[[9,"module-gen3.tools",false]],"gen3.tools.download.drs_download":[[10,"module-gen3.tools.download.drs_download",false]],"gen3.tools.indexing.download_manifest":[[11,"module-gen3.tools.indexing.download_manifest",false]],"gen3.tools.indexing.index_manifest":[[11,"module-gen3.tools.indexing.index_manifest",false]],"gen3.tools.indexing.verify_manifest":[[11,"module-gen3.tools.indexing.verify_manifest",false]],"gen3.tools.metadata.ingest_manifest":[[12,"module-gen3.tools.metadata.ingest_manifest",false]],"gen3auth (class in gen3.auth)":[[0,"gen3.auth.Gen3Auth",false]],"gen3file (class in gen3.file)":[[1,"gen3.file.Gen3File",false]],"gen3index (class in gen3.index)":[[3,"gen3.index.Gen3Index",false]],"gen3jobs (class in gen3.jobs)":[[4,"gen3.jobs.Gen3Jobs",false]],"gen3metadata (class in gen3.metadata)":[[5,"gen3.metadata.Gen3Metadata",false]],"gen3object (class in gen3.object)":[[6,"gen3.object.Gen3Object",false]],"gen3query (class in gen3.query)":[[7,"gen3.query.Gen3Query",false]],"gen3submission (class in gen3.submission)":[[8,"gen3.submission.Gen3Submission",false]],"gen3wsstorage (class in gen3.wss)":[[13,"gen3.wss.Gen3WsStorage",false]],"get() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.get",false]],"get() (gen3.metadata.gen3metadata method)":[[5,"gen3.metadata.Gen3Metadata.get",false]],"get_access_token() (gen3.auth.gen3auth method)":[[0,"gen3.auth.Gen3Auth.get_access_token",false]],"get_access_token_from_wts() (gen3.auth.gen3auth method)":[[0,"gen3.auth.Gen3Auth.get_access_token_from_wts",false]],"get_aliases() (gen3.metadata.gen3metadata method)":[[5,"gen3.metadata.Gen3Metadata.get_aliases",false]],"get_all_records() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.get_all_records",false]],"get_dictionary_all() (gen3.submission.gen3submission method)":[[8,"gen3.submission.Gen3Submission.get_dictionary_all",false]],"get_dictionary_node() (gen3.submission.gen3submission method)":[[8,"gen3.submission.Gen3Submission.get_dictionary_node",false]],"get_fresh_token() (gen3.tools.download.drs_download.downloadmanager method)":[[10,"gen3.tools.download.drs_download.DownloadManager.get_fresh_token",false]],"get_graphql_schema() (gen3.submission.gen3submission method)":[[8,"gen3.submission.Gen3Submission.get_graphql_schema",false]],"get_guids_prefix() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.get_guids_prefix",false]],"get_index_key_paths() (gen3.metadata.gen3metadata method)":[[5,"gen3.metadata.Gen3Metadata.get_index_key_paths",false]],"get_latest_version() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.get_latest_version",false]],"get_output() (gen3.jobs.gen3jobs method)":[[4,"gen3.jobs.Gen3Jobs.get_output",false]],"get_presigned_url() (gen3.file.gen3file method)":[[1,"gen3.file.Gen3File.get_presigned_url",false]],"get_presigned_urls_batch() (gen3.file.gen3file method)":[[1,"gen3.file.Gen3File.get_presigned_urls_batch",false]],"get_programs() (gen3.submission.gen3submission method)":[[8,"gen3.submission.Gen3Submission.get_programs",false]],"get_project_dictionary() (gen3.submission.gen3submission method)":[[8,"gen3.submission.Gen3Submission.get_project_dictionary",false]],"get_project_manifest() (gen3.submission.gen3submission method)":[[8,"gen3.submission.Gen3Submission.get_project_manifest",false]],"get_projects() (gen3.submission.gen3submission method)":[[8,"gen3.submission.Gen3Submission.get_projects",false]],"get_record() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.get_record",false]],"get_record_doc() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.get_record_doc",false]],"get_records() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.get_records",false]],"get_records_on_page() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.get_records_on_page",false]],"get_stats() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.get_stats",false]],"get_status() (gen3.jobs.gen3jobs method)":[[4,"gen3.jobs.Gen3Jobs.get_status",false]],"get_urls() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.get_urls",false]],"get_valid_guids() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.get_valid_guids",false]],"get_version() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.get_version",false]],"get_version() (gen3.jobs.gen3jobs method)":[[4,"gen3.jobs.Gen3Jobs.get_version",false]],"get_version() (gen3.metadata.gen3metadata method)":[[5,"gen3.metadata.Gen3Metadata.get_version",false]],"get_versions() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.get_versions",false]],"get_with_params() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.get_with_params",false]],"graphql_query() (gen3.query.gen3query method)":[[7,"gen3.query.Gen3Query.graphql_query",false]],"guid (in module gen3.tools.indexing.index_manifest)":[[11,"gen3.tools.indexing.index_manifest.GUID",false]],"guid_type_for_indexed_file_object (in module gen3.tools.metadata.ingest_manifest)":[[12,"gen3.tools.metadata.ingest_manifest.GUID_TYPE_FOR_INDEXED_FILE_OBJECT",false]],"guid_type_for_non_indexed_file_object (in module gen3.tools.metadata.ingest_manifest)":[[12,"gen3.tools.metadata.ingest_manifest.GUID_TYPE_FOR_NON_INDEXED_FILE_OBJECT",false]],"hostname (gen3.tools.download.drs_download.downloadable attribute)":[[10,"gen3.tools.download.drs_download.Downloadable.hostname",false]],"index_object_manifest() (in module gen3.tools.indexing.index_manifest)":[[11,"gen3.tools.indexing.index_manifest.index_object_manifest",false]],"indexd_record_page_size (in module gen3.tools.indexing.download_manifest)":[[11,"gen3.tools.indexing.download_manifest.INDEXD_RECORD_PAGE_SIZE",false]],"is_healthy() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.is_healthy",false]],"is_healthy() (gen3.jobs.gen3jobs method)":[[4,"gen3.jobs.Gen3Jobs.is_healthy",false]],"is_healthy() (gen3.metadata.gen3metadata method)":[[5,"gen3.metadata.Gen3Metadata.is_healthy",false]],"list_access_in_drs_manifest() (in module gen3.tools.download.drs_download)":[[10,"gen3.tools.download.drs_download.list_access_in_drs_manifest",false]],"list_drs_object() (in module gen3.tools.download.drs_download)":[[10,"gen3.tools.download.drs_download.list_drs_object",false]],"list_files_in_drs_manifest() (in module gen3.tools.download.drs_download)":[[10,"gen3.tools.download.drs_download.list_files_in_drs_manifest",false]],"list_jobs() (gen3.jobs.gen3jobs method)":[[4,"gen3.jobs.Gen3Jobs.list_jobs",false]],"load() (gen3.tools.download.drs_download.manifest static method)":[[10,"gen3.tools.download.drs_download.Manifest.load",false]],"load_manifest() (gen3.tools.download.drs_download.manifest static method)":[[10,"gen3.tools.download.drs_download.Manifest.load_manifest",false]],"ls() (gen3.wss.gen3wsstorage method)":[[13,"gen3.wss.Gen3WsStorage.ls",false]],"ls_path() (gen3.wss.gen3wsstorage method)":[[13,"gen3.wss.Gen3WsStorage.ls_path",false]],"manifest (class in gen3.tools.download.drs_download)":[[10,"gen3.tools.download.drs_download.Manifest",false]],"max_concurrent_requests (in module gen3.tools.indexing.download_manifest)":[[11,"gen3.tools.indexing.download_manifest.MAX_CONCURRENT_REQUESTS",false]],"max_concurrent_requests (in module gen3.tools.indexing.verify_manifest)":[[11,"gen3.tools.indexing.verify_manifest.MAX_CONCURRENT_REQUESTS",false]],"max_concurrent_requests (in module gen3.tools.metadata.ingest_manifest)":[[12,"gen3.tools.metadata.ingest_manifest.MAX_CONCURRENT_REQUESTS",false]],"md5 (in module gen3.tools.indexing.index_manifest)":[[11,"gen3.tools.indexing.index_manifest.MD5",false]],"md5sum (gen3.tools.download.drs_download.manifest attribute)":[[10,"gen3.tools.download.drs_download.Manifest.md5sum",false]],"module":[[9,"module-gen3.tools",false],[10,"module-gen3.tools.download.drs_download",false],[11,"module-gen3.tools.indexing.download_manifest",false],[11,"module-gen3.tools.indexing.index_manifest",false],[11,"module-gen3.tools.indexing.verify_manifest",false],[12,"module-gen3.tools.metadata.ingest_manifest",false]],"object_id (gen3.tools.download.drs_download.downloadable attribute)":[[10,"gen3.tools.download.drs_download.Downloadable.object_id",false]],"object_id (gen3.tools.download.drs_download.manifest attribute)":[[10,"gen3.tools.download.drs_download.Manifest.object_id",false]],"object_type (gen3.tools.download.drs_download.downloadable attribute)":[[10,"gen3.tools.download.drs_download.Downloadable.object_type",false]],"open_project() (gen3.submission.gen3submission method)":[[8,"gen3.submission.Gen3Submission.open_project",false]],"pprint() (gen3.tools.download.drs_download.downloadable method)":[[10,"gen3.tools.download.drs_download.Downloadable.pprint",false]],"prev_guid (in module gen3.tools.indexing.index_manifest)":[[11,"gen3.tools.indexing.index_manifest.PREV_GUID",false]],"query() (gen3.metadata.gen3metadata method)":[[5,"gen3.metadata.Gen3Metadata.query",false]],"query() (gen3.query.gen3query method)":[[7,"gen3.query.Gen3Query.query",false]],"query() (gen3.submission.gen3submission method)":[[8,"gen3.submission.Gen3Submission.query",false]],"query_urls() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.query_urls",false]],"raw_data_download() (gen3.query.gen3query method)":[[7,"gen3.query.Gen3Query.raw_data_download",false]],"refresh_access_token() (gen3.auth.gen3auth method)":[[0,"gen3.auth.Gen3Auth.refresh_access_token",false]],"resolve_objects() (gen3.tools.download.drs_download.downloadmanager method)":[[10,"gen3.tools.download.drs_download.DownloadManager.resolve_objects",false]],"rm() (gen3.wss.gen3wsstorage method)":[[13,"gen3.wss.Gen3WsStorage.rm",false]],"rm_path() (gen3.wss.gen3wsstorage method)":[[13,"gen3.wss.Gen3WsStorage.rm_path",false]],"size (in module gen3.tools.indexing.index_manifest)":[[11,"gen3.tools.indexing.index_manifest.SIZE",false]],"start_time (gen3.tools.download.drs_download.downloadstatus attribute)":[[10,"gen3.tools.download.drs_download.DownloadStatus.start_time",false]],"status (gen3.tools.download.drs_download.downloadstatus attribute)":[[10,"gen3.tools.download.drs_download.DownloadStatus.status",false]],"submit_file() (gen3.submission.gen3submission method)":[[8,"gen3.submission.Gen3Submission.submit_file",false]],"submit_record() (gen3.submission.gen3submission method)":[[8,"gen3.submission.Gen3Submission.submit_record",false]],"threadcontrol (class in gen3.tools.indexing.index_manifest)":[[11,"gen3.tools.indexing.index_manifest.ThreadControl",false]],"tmp_folder (in module gen3.tools.indexing.download_manifest)":[[11,"gen3.tools.indexing.download_manifest.TMP_FOLDER",false]],"update() (gen3.metadata.gen3metadata method)":[[5,"gen3.metadata.Gen3Metadata.update",false]],"update_aliases() (gen3.metadata.gen3metadata method)":[[5,"gen3.metadata.Gen3Metadata.update_aliases",false]],"update_blank() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.update_blank",false]],"update_record() (gen3.index.gen3index method)":[[3,"gen3.index.Gen3Index.update_record",false]],"updated_time (gen3.tools.download.drs_download.downloadable attribute)":[[10,"gen3.tools.download.drs_download.Downloadable.updated_time",false]],"upload() (gen3.wss.gen3wsstorage method)":[[13,"gen3.wss.Gen3WsStorage.upload",false]],"upload_file() (gen3.file.gen3file method)":[[1,"gen3.file.Gen3File.upload_file",false]],"upload_file_to_guid() (gen3.file.gen3file method)":[[1,"gen3.file.Gen3File.upload_file_to_guid",false]],"upload_url() (gen3.wss.gen3wsstorage method)":[[13,"gen3.wss.Gen3WsStorage.upload_url",false]],"urls (in module gen3.tools.indexing.index_manifest)":[[11,"gen3.tools.indexing.index_manifest.URLS",false]],"user_access() (gen3.tools.download.drs_download.downloadmanager method)":[[10,"gen3.tools.download.drs_download.DownloadManager.user_access",false]]},"objects":{"gen3":[[9,3,0,"-","tools"]],"gen3.auth":[[0,0,1,"","Gen3Auth"]],"gen3.auth.Gen3Auth":[[0,1,1,"","curl"],[0,1,1,"","get_access_token"],[0,1,1,"","get_access_token_from_wts"],[0,1,1,"","refresh_access_token"]],"gen3.file":[[1,0,1,"","Gen3File"]],"gen3.file.Gen3File":[[1,1,1,"","async_download_multiple"],[1,1,1,"","delete_file"],[1,1,1,"","delete_file_locations"],[1,1,1,"","download_single"],[1,1,1,"","get_presigned_url"],[1,1,1,"","get_presigned_urls_batch"],[1,1,1,"","upload_file"],[1,1,1,"","upload_file_to_guid"]],"gen3.index":[[3,0,1,"","Gen3Index"]],"gen3.index.Gen3Index":[[3,1,1,"","async_create_record"],[3,1,1,"","async_get_record"],[3,1,1,"","async_get_records_from_checksum"],[3,1,1,"","async_get_records_on_page"],[3,1,1,"","async_get_with_params"],[3,1,1,"","async_query_urls"],[3,1,1,"","async_update_record"],[3,1,1,"","create_blank"],[3,1,1,"","create_new_version"],[3,1,1,"","create_record"],[3,1,1,"","delete_record"],[3,1,1,"","get"],[3,1,1,"","get_all_records"],[3,1,1,"","get_guids_prefix"],[3,1,1,"","get_latest_version"],[3,1,1,"","get_record"],[3,1,1,"","get_record_doc"],[3,1,1,"","get_records"],[3,1,1,"","get_records_on_page"],[3,1,1,"","get_stats"],[3,1,1,"","get_urls"],[3,1,1,"","get_valid_guids"],[3,1,1,"","get_version"],[3,1,1,"","get_versions"],[3,1,1,"","get_with_params"],[3,1,1,"","is_healthy"],[3,1,1,"","query_urls"],[3,1,1,"","update_blank"],[3,1,1,"","update_record"]],"gen3.jobs":[[4,0,1,"","Gen3Jobs"]],"gen3.jobs.Gen3Jobs":[[4,1,1,"","async_run_job_and_wait"],[4,1,1,"","create_job"],[4,1,1,"","get_output"],[4,1,1,"","get_status"],[4,1,1,"","get_version"],[4,1,1,"","is_healthy"],[4,1,1,"","list_jobs"]],"gen3.metadata":[[5,0,1,"","Gen3Metadata"]],"gen3.metadata.Gen3Metadata":[[5,1,1,"","async_create"],[5,1,1,"","async_create_aliases"],[5,1,1,"","async_delete_alias"],[5,1,1,"","async_delete_aliases"],[5,1,1,"","async_get"],[5,1,1,"","async_get_aliases"],[5,1,1,"","async_update"],[5,1,1,"","async_update_aliases"],[5,2,1,"","auth_provider"],[5,1,1,"","batch_create"],[5,1,1,"","create"],[5,1,1,"","create_aliases"],[5,1,1,"","create_index_key_path"],[5,1,1,"","delete"],[5,1,1,"","delete_alias"],[5,1,1,"","delete_aliases"],[5,1,1,"","delete_index_key_path"],[5,2,1,"","endpoint"],[5,1,1,"","get"],[5,1,1,"","get_aliases"],[5,1,1,"","get_index_key_paths"],[5,1,1,"","get_version"],[5,1,1,"","is_healthy"],[5,1,1,"","query"],[5,1,1,"","update"],[5,1,1,"","update_aliases"]],"gen3.object":[[6,0,1,"","Gen3Object"]],"gen3.object.Gen3Object":[[6,1,1,"","delete_object"]],"gen3.query":[[7,0,1,"","Gen3Query"]],"gen3.query.Gen3Query":[[7,1,1,"","graphql_query"],[7,1,1,"","query"],[7,1,1,"","raw_data_download"]],"gen3.submission":[[8,0,1,"","Gen3Submission"]],"gen3.submission.Gen3Submission":[[8,1,1,"","create_program"],[8,1,1,"","create_project"],[8,1,1,"","delete_node"],[8,1,1,"","delete_nodes"],[8,1,1,"","delete_program"],[8,1,1,"","delete_project"],[8,1,1,"","delete_record"],[8,1,1,"","delete_records"],[8,1,1,"","export_node"],[8,1,1,"","export_record"],[8,1,1,"","get_dictionary_all"],[8,1,1,"","get_dictionary_node"],[8,1,1,"","get_graphql_schema"],[8,1,1,"","get_programs"],[8,1,1,"","get_project_dictionary"],[8,1,1,"","get_project_manifest"],[8,1,1,"","get_projects"],[8,1,1,"","open_project"],[8,1,1,"","query"],[8,1,1,"","submit_file"],[8,1,1,"","submit_record"]],"gen3.tools.download":[[10,3,0,"-","drs_download"]],"gen3.tools.download.drs_download":[[10,0,1,"","DownloadManager"],[10,0,1,"","DownloadStatus"],[10,0,1,"","Downloadable"],[10,0,1,"","Manifest"],[10,4,1,"","download_files_in_drs_manifest"],[10,4,1,"","list_access_in_drs_manifest"],[10,4,1,"","list_drs_object"],[10,4,1,"","list_files_in_drs_manifest"]],"gen3.tools.download.drs_download.DownloadManager":[[10,1,1,"","cache_hosts_wts_tokens"],[10,1,1,"","download"],[10,1,1,"","get_fresh_token"],[10,1,1,"","resolve_objects"],[10,1,1,"","user_access"]],"gen3.tools.download.drs_download.DownloadStatus":[[10,2,1,"","end_time"],[10,2,1,"","start_time"],[10,2,1,"","status"]],"gen3.tools.download.drs_download.Downloadable":[[10,2,1,"","_manager"],[10,2,1,"","access_methods"],[10,2,1,"","children"],[10,2,1,"","created_time"],[10,1,1,"","download"],[10,2,1,"","file_name"],[10,2,1,"","file_size"],[10,2,1,"","hostname"],[10,2,1,"","object_id"],[10,2,1,"","object_type"],[10,1,1,"","pprint"],[10,2,1,"","updated_time"]],"gen3.tools.download.drs_download.Manifest":[[10,2,1,"","commons_url"],[10,1,1,"","create_object_list"],[10,2,1,"","file_name"],[10,2,1,"","file_size"],[10,1,1,"","load"],[10,1,1,"","load_manifest"],[10,2,1,"","md5sum"],[10,2,1,"","object_id"]],"gen3.tools.indexing":[[11,3,0,"-","download_manifest"],[11,3,0,"-","index_manifest"],[11,3,0,"-","verify_manifest"]],"gen3.tools.indexing.download_manifest":[[11,2,1,"","CURRENT_DIR"],[11,2,1,"","INDEXD_RECORD_PAGE_SIZE"],[11,2,1,"","MAX_CONCURRENT_REQUESTS"],[11,2,1,"","TMP_FOLDER"],[11,4,1,"","async_download_object_manifest"]],"gen3.tools.indexing.index_manifest":[[11,2,1,"","ACLS"],[11,2,1,"","AUTHZ"],[11,2,1,"","CURRENT_DIR"],[11,2,1,"","GUID"],[11,2,1,"","MD5"],[11,2,1,"","PREV_GUID"],[11,2,1,"","SIZE"],[11,0,1,"","ThreadControl"],[11,2,1,"","URLS"],[11,4,1,"","delete_all_guids"],[11,4,1,"","index_object_manifest"]],"gen3.tools.indexing.verify_manifest":[[11,2,1,"","CURRENT_DIR"],[11,2,1,"","MAX_CONCURRENT_REQUESTS"],[11,4,1,"","async_verify_object_manifest"]],"gen3.tools.metadata":[[12,3,0,"-","ingest_manifest"]],"gen3.tools.metadata.ingest_manifest":[[12,2,1,"","COLUMN_TO_USE_AS_GUID"],[12,2,1,"","GUID_TYPE_FOR_INDEXED_FILE_OBJECT"],[12,2,1,"","GUID_TYPE_FOR_NON_INDEXED_FILE_OBJECT"],[12,2,1,"","MAX_CONCURRENT_REQUESTS"],[12,4,1,"","async_ingest_metadata_manifest"],[12,4,1,"","async_query_urls_from_indexd"]],"gen3.wss":[[13,0,1,"","Gen3WsStorage"]],"gen3.wss.Gen3WsStorage":[[13,1,1,"","copy"],[13,1,1,"","download"],[13,1,1,"","download_url"],[13,1,1,"","ls"],[13,1,1,"","ls_path"],[13,1,1,"","rm"],[13,1,1,"","rm_path"],[13,1,1,"","upload"],[13,1,1,"","upload_url"]]},"objnames":{"0":["py","class","Python class"],"1":["py","method","Python method"],"2":["py","attribute","Python attribute"],"3":["py","module","Python module"],"4":["py","function","Python function"]},"objtypes":{"0":"py:class","1":"py:method","2":"py:attribute","3":"py:module","4":"py:function"},"terms":{"0a80fada010c":11,"0a80fada096c":11,"0a80fada097c":11,"0a80fada098c":11,"0a80fada099c":11,"11e9":11,"255e396f":11,"450c":11,"473d83400bc1bc9dc635e334fadd433c":11,"473d83400bc1bc9dc635e334faddd33c":11,"473d83400bc1bc9dc635e334fadde33c":11,"473d83400bc1bc9dc635e334faddf33c":11,"6f90":8,"7d3d8d2083b4":11,"93d9af72":11,"9a07":11,"A":[1,3,4,5,6,7,8,10,11,13],"ALL":7,"AND":5,"All":11,"Be":1,"But":5,"By":11,"For":[1,5,6,7,8,9,11],"IF":11,"If":[0,1,7,11,12],"In":10,"It":10,"Most":9,"NOT":12,"OR":5,"Same":13,"Such":9,"THE":11,"THIS":11,"That":3,"The":[0,1,2,3,5,8,10,11],"There":11,"These":9,"This":[0,1,2,3,4,5,6,7,8,10,11,13],"To":11,"We":11,"When":12,"YOU":11,"_get_acl_from_row":11,"_get_authz_from_row":11,"_get_file_name_from_row":11,"_get_file_size_from_row":11,"_get_guid_for_row":12,"_get_guid_from_row":11,"_get_md5_from_row":11,"_get_urls_from_row":11,"_guid_typ":12,"_manag":[2,9,10],"_query_for_associated_indexd_record_guid":12,"_ssl":[3,4,5],"a5c6":11,"ab167e49d25b488939b1ede42752458b":3,"abov":11,"access":[0,1,3,7,10],"access_method":[2,9,10],"access_token":0,"accesstoken":0,"acl":[2,3,9,11],"across":11,"act":0,"action":[9,11],"actual":11,"add":[3,5],"addit":[3,5,10,11],"admin":[5,11],"admin_endpoint_suffix":5,"algorithm":3,"alia":[3,5],"alias":5,"aliv":7,"allow":[0,6,8,10,11,12],"allowed_data_upload_bucket":1,"along":2,"alreadi":9,"also":1,"altern":[5,11],"alway":5,"ammount":12,"amount":[1,9],"ani":[0,5,10,11],"anoth":5,"api":[0,5,8,11],"api_key":11,"appli":7,"appropri":13,"arbitrari":0,"argument":[0,13],"array":8,"asc":7,"assign":9,"assist":10,"associ":[3,5],"assum":11,"async":[1,3,4,5,9,11,12],"async_cr":[2,5],"async_create_alias":[2,5],"async_create_record":[2,3],"async_delete_alia":[2,5],"async_delete_alias":[2,5],"async_download_multipl":[1,2],"async_download_object_manifest":[2,9,11],"async_get":[2,5],"async_get_alias":[2,5],"async_get_record":[2,3],"async_get_records_from_checksum":[2,3],"async_get_records_on_pag":[2,3],"async_get_with_param":[2,3],"async_ingest_metadata_manifest":[2,9,12],"async_query_url":[2,3],"async_query_urls_from_indexd":[2,9,12],"async_run_job_and_wait":[2,4],"async_upd":[2,5],"async_update_alias":[2,5],"async_update_record":[2,3],"async_verify_object_manifest":[2,9,11],"asynchron":[1,3,4,5],"asyncio":[11,12],"asyncron":5,"attach":[3,5],"attempt":11,"attribut":[10,11],"auth":[1,2,3,4,5,6,7,8,10,11,12,13],"auth_provid":[1,2,3,4,5,6,7,8,13],"authbas":0,"authent":0,"author":1,"authz":[0,1,2,3,9,10,11],"auto":[0,2],"automat":0,"avail":[1,2,10,11],"az":1,"b":[5,11],"b0f1":11,"bar":10,"base":[0,1,3,4,5,6,7,8,9,11,13],"baseid":3,"basic":[3,11,12],"batch_creat":[2,5],"batch_siz":8,"behalf":0,"behavior":11,"belong":8,"blank":3,"blob":[5,7],"bodi":3,"bool":[1,4,5,8,10,11,12],"boolean":3,"bownload":10,"broad":9,"broken":9,"bucket":[1,6],"bundl":10,"byte":10,"c":[5,11],"cach":10,"cache_hosts_wts_token":[2,9,10],"call":[10,13],"can":[0,3,4,8,11,12],"capabl":9,"case":[0,10],"categori":9,"ccle":8,"ccle_one_record":8,"ccle_sample_nod":8,"cdis":7,"chang":[3,11],"checksum":[3,10],"checksum_typ":3,"child":10,"children":[2,9,10],"chunk_siz":8,"class":[0,2,10,11,13],"cli":10,"client":[0,3],"client_credenti":0,"client_id":0,"client_scop":0,"client_secret":0,"code":[2,8],"column":[11,12],"column_to_use_as_guid":[2,9,12],"columna":11,"columnb":11,"columnc":11,"com":7,"comma":11,"command":[10,11],"common":[0,1,3,4,5,6,7,8,9,10,11,12,13],"commons_url":[2,9,10,11,12],"complet":[4,11],"complex":7,"concat":11,"concurr":[11,12],"configur":1,"connect":12,"consist":3,"constructor":0,"contain":[0,2,5,8,9,10,11,12],"content":[3,13],"content_created_d":3,"content_updated_d":3,"continu":10,"control":3,"conveni":10,"copi":[2,13],"coroutin":11,"correspond":3,"count":3,"crdc":0,"creat":[2,3,4,5,6,8,10,11],"create_alias":[2,5],"create_blank":[2,3],"create_index_key_path":[2,5],"create_job":[2,4],"create_new_vers":[2,3],"create_object_list":[2,9,10],"create_program":[2,8],"create_project":[2,8],"create_record":[2,3],"created_tim":[2,9,10],"creation":[3,11],"cred":3,"credenti":[0,1,3,4,5,6,7,8,10,11,13],"csv":[8,11,12],"curl":[0,2],"current":[6,8,10],"current_dir":[2,9,11],"custom":11,"d":5,"d70b41b9":8,"data":[0,1,3,5,7,8,10,11],"data_spreadsheet":8,"data_typ":7,"data_upload_bucket":1,"dataa":11,"datab":11,"databas":5,"datacommon":0,"datafil":10,"datamanag":10,"date":3,"datetim":[1,3,10],"dbgap":12,"dcf":8,"def":11,"default":[0,1,3,7,8,11,12],"defin":[5,8,10],"delay":4,"delet":[0,1,2,3,5,6,8,10,11],"delete_alia":[2,5],"delete_alias":[2,5],"delete_all_guid":[2,9,11],"delete_fil":[1,2],"delete_file_loc":[1,2,6],"delete_index_key_path":[2,5],"delete_nod":[2,8],"delete_object":[2,6],"delete_program":[2,8],"delete_project":[2,8],"delete_record":[2,3,8],"delete_unpacked_packag":10,"delimet":[11,12],"delimit":11,"demograph":8,"deprec":1,"descript":[3,5],"desir":11,"dest_path":13,"dest_urlstr":13,"dest_w":13,"dest_wskey":13,"detail":[2,7,10],"determin":[10,11,12],"dev":11,"dict":[1,3,4,5,10,11,12],"dictionari":[3,4,5,7,8],"dids":3,"differ":5,"direct":0,"directori":[10,11],"disabl":10,"discoveri":10,"disk":13,"dispatch":4,"dist_resolut":3,"distribut":3,"doc":[7,10],"docstr":2,"document":[1,3],"doe":[0,12],"domain":[11,12],"done":4,"download":[0,1,2,3,4,5,6,7,8,9,13],"download_files_in_drs_manifest":[2,9,10],"download_list":10,"download_manifest":11,"download_path":1,"download_singl":[1,2],"download_url":[2,13],"downloadmanag":[2,9,10],"downloadstatus":[2,9,10],"drs":[2,9],"drs_download":10,"drs_hostnam":10,"drsdownload":10,"drsobjecttyp":10,"e":[5,10],"e043ab8b77b9":8,"effici":[1,9],"eg":3,"either":8,"elasticsearch":7,"els":[0,12],"elsewher":12,"empti":[8,11],"enabl":11,"end":[5,10],"end_tim":[2,9,10],"endpoint":[0,1,2,3,4,5,7,8,13],"entir":8,"entri":[3,11],"env":0,"environ":0,"equal":7,"error":[10,11],"error_nam":11,"etc":8,"even":11,"everi":[9,11],"everyth":11,"ex":[0,11,12],"exampl":[0,1,3,4,5,6,7,8,10,11,13],"exclud":3,"execut":[7,8,11],"exist":[1,3,5,6,9,12],"expect":[5,9,11],"experi":8,"expir":[0,1],"expires_in":1,"export":[8,10],"export_nod":[2,8],"export_record":[2,8],"f1f8":11,"factori":10,"fail":[8,10],"fals":[1,3,5,6,10,11],"featur":[1,6],"fenc":[0,1],"fetch":0,"field":[3,5,7,11,12],"fieldnam":11,"file":[0,2,3,4,8,9,10,11,12,13],"file_nam":[1,2,3,9,10,11],"file_s":[2,9,10,11],"file_st":3,"fileformat":8,"filenam":[0,8,10,11,12],"filename_format":1,"files":10,"fill":12,"filter":[5,7],"filter_object":7,"first":[7,8],"flag":11,"folder":11,"follow":[0,11],"forc":11,"force_metadata_columns_even_if_empti":11,"form":13,"format":[3,5,8,11],"func_to_parse_row":[11,12],"function":[2,3,4,5,9,10,11,12],"g":10,"gen3":[10,11,12],"gen3_api_key":0,"gen3_oidc_client_creds_secret":0,"gen3auth":[0,1,2,3,4,5,6,7,8,10,11,12,13],"gen3fil":[1,2],"gen3index":[2,3],"gen3job":[2,4,10],"gen3metadata":[2,5],"gen3object":[2,6],"gen3queri":[2,7],"gen3submiss":[2,8],"gen3wsstorag":[2,13],"generat":[0,1,2,3,4,5,6,7,8,10,13],"get":[0,1,2,3,4,5,8,10,11,12,13],"get_access_token":[0,2],"get_access_token_from_wt":[0,2],"get_alias":[2,5],"get_all_record":[2,3],"get_dictionary_al":[2,8],"get_dictionary_nod":[2,8],"get_fresh_token":[2,9,10],"get_graphql_schema":[2,8],"get_guid_from_fil":12,"get_guids_prefix":[2,3],"get_index_key_path":[2,5],"get_latest_vers":[2,3],"get_output":[2,4],"get_presigned_url":[1,2],"get_presigned_urls_batch":[1,2],"get_program":[2,8],"get_project":[2,8],"get_project_dictionari":[2,8],"get_project_manifest":[2,8],"get_record":[2,3],"get_record_doc":[2,3],"get_records_on_pag":[2,3],"get_stat":[2,3],"get_status":[2,4],"get_url":[2,3],"get_valid_guid":[2,3],"get_vers":[2,3,4,5],"get_with_param":[2,3],"giangb":11,"github":[2,7],"give":1,"given":[0,3,4,5,8,10,12,13],"global":[4,5],"good":3,"grant":0,"graph":8,"graphql":[7,8],"graphql_queri":[2,7],"group":3,"guid":[1,2,3,5,6,9,11,12],"guid_exampl":11,"guid_for_row":12,"guid_from_fil":12,"guid_type_for_indexed_file_object":[2,9,12],"guid_type_for_non_indexed_file_object":[2,9,12],"guppi":7,"handl":[3,10],"hardcod":0,"has_vers":3,"hash":[3,11],"hash_typ":3,"header":11,"healthi":[3,4,5],"help":11,"helper":2,"hit":11,"host":10,"hostnam":[2,9,10],"howto":10,"http":12,"https":[0,7,11],"id":[0,1,3,5,10,11],"idea":3,"identifi":[3,5,9,11],"idp":0,"illustr":11,"immut":3,"implement":0,"implic":11,"import":11,"includ":[0,3],"indent":10,"index":[0,2,5,9],"index_manifest":11,"index_object_manifest":[2,9,11],"indexd":[1,3,6,10,11,12],"indexd_field":[11,12],"indexd_record_page_s":[2,9,11],"indexed_file_object_guid":12,"indic":[0,11],"infil":10,"info":[3,11],"inform":[2,3,10],"ingest":[2,9],"ingest_manifest":12,"initi":[0,10],"input":[4,10,11],"input_manifest":11,"instal":[0,2,11],"instanc":[1,3,6,7,8,9,10],"instead":[1,7,11],"int":[1,3,5,7,8,10,11,12],"integ":[1,3,8],"intend":0,"interact":[1,3,4,5,6,8,13],"interest":10,"interpret":0,"introspect":8,"involv":9,"is_healthi":[2,3,4,5],"is_indexed_file_object":12,"isn":1,"issu":0,"job":2,"job_id":4,"job_input":4,"job_nam":4,"json":[0,1,3,4,5,6,7,8,10,11,13],"just":[5,11,12],"jwt":0,"key":[0,3,5,13],"know":11,"known":10,"kwarg":[3,4,5],"larg":9,"last":10,"latest":3,"least":3,"level":6,"librari":11,"like":[3,5,9,11,12],"limit":[1,3,5,12],"linear":4,"linux":10,"list":[0,1,3,4,5,7,8,10,11,13],"list_access_in_drs_manifest":[2,9,10],"list_drs_object":[2,9,10],"list_files_in_drs_manifest":[2,9,10],"list_job":[2,4],"live":[11,12],"load":[2,9,10],"load_manifest":[2,9,10],"local":[0,13],"locat":[1,6],"lock":12,"log":[8,10,11,12],"logic":[5,12],"loop":11,"ls":[2,13],"ls_path":[2,13],"maco":11,"made":3,"main":10,"make":[9,11],"manag":[1,5,10],"mani":[8,11],"manifest":[2,8,9,10,11,12],"manifest_1":10,"manifest_data":1,"manifest_fil":[11,12],"manifest_file_delimit":[11,12],"manifest_row_pars":[11,12],"map":[0,1,11],"mark":8,"master":7,"match":[3,5,12],"max":5,"max_concurrent_request":[1,2,9,11,12],"max_presigned_url_ttl":1,"max_tri":8,"maximum":[11,12],"may":[0,9,11],"md":[7,10],"md5":[2,3,9,11],"md5_hash":11,"md5sum":[2,9,10],"mds":[5,12],"mean":8,"mechan":3,"merg":5,"metadata":[2,3,6,9,11],"metadata_list":5,"metadata_sourc":12,"metadata_typ":12,"metdata":12,"method":[1,7,10],"minimum":10,"minut":0,"mode":7,"modul":[2,10,11],"mostly":2,"multipl":[1,8,11],"multiprocess":1,"must":[1,5],"my_common":10,"my_credenti":10,"my_field":7,"my_index":7,"my_program":7,"my_project":7,"name":[3,4,8,10,11,12,13],"namespac":[0,12],"necessari":[3,5],"need":[3,7,10,11],"nest":5,"net":11,"never":0,"new":[0,3],"no_progress":1,"node":8,"node_nam":8,"node_typ":8,"none":[0,1,3,4,5,6,7,8,10,11,12,13],"note":[0,3,11,12],"noth":[3,6],"now":[1,8],"num":5,"num_process":[1,11],"num_total_fil":11,"number":[3,7,8,11,12],"object":[1,2,3,4,5,7,8,9,10,11,13],"object_id":[1,2,9,10],"object_list":10,"object_typ":[2,9,10],"objectid":10,"obtain":[0,10],"occur":10,"offset":[5,7],"oidc":0,"old":3,"one":[3,5,7,10,11],"onli":[3,5,7,8,10,11],"open":[8,10,11],"open_project":[2,8],"openid":0,"opt":0,"option":[0,1,3,4,5,6,7,8,10,11],"order":[0,8],"ordered_node_list":8,"org":10,"origin":1,"os":0,"otherwis":[1,10],"output":[4,5,11,12],"output_dir":10,"output_filenam":[11,12],"overrid":[0,11,12],"overwrit":5,"packag":10,"page":[0,1,2,3,4,5,6,7,8,10,11,13],"pagin":3,"parallel":11,"param":[3,5,8,10],"paramet":[0,1,3,4,5,6,7,8,10,11,12,13],"pars":[10,11,12,13],"parser":[11,12],"particular":0,"pass":[0,7,8,10],"password":[11,12],"path":[0,1,5,10,11,13],"path_to_manifest":11,"pattern":[3,12],"pdcdatastor":11,"pend":10,"per":[11,12],"peregrin":8,"permiss":10,"persist":9,"phs0001":11,"phs0002":11,"pick":1,"pla":11,"place":11,"planx":11,"point":[0,1,3,4,5,6,7,8,10,13],"popul":[10,12],"posit":[1,7],"possibl":10,"post":[0,11],"pprint":[2,9,10],"prefer":1,"prefix":3,"presign":1,"pretti":10,"prev_guid":[2,9,11],"previous":[3,4,11],"print":[8,10],"process":11,"processed_fil":11,"profil":[0,1,3,4,5,6,7,8,10,13],"program":[8,11],"progress":[8,10],"project":[8,11],"project_id":[7,8],"protocol":1,"provid":[0,1,3,5,7,8,12],"public":[3,5],"put":0,"py":11,"python":[2,9,11],"python3":11,"python_subprocess_command":11,"queri":[1,2,3,5,8,11,12],"query_str":7,"query_txt":[7,8],"query_url":[2,3],"queue":1,"queue_siz":1,"quickstart":2,"rather":0,"raw":[7,11],"raw_data_download":[2,7],"rbac":3,"read":[3,5,11],"readm":2,"reason":10,"record":[1,3,5,7,8,11,12],"refresh":[0,10],"refresh_access_token":[0,2],"refresh_fil":[0,1,3,4,5,6,7,8,10,13],"refresh_token":0,"regist":8,"regular":7,"relat":9,"remov":[1,6,11,13],"renam":1,"replac":11,"replace_url":11,"repo":2,"repres":[3,5,10],"represent":[1,3],"request":[0,1,3,5,8,11,12],"requir":10,"resolv":10,"resolve_object":[2,9,10],"respect":7,"respons":[0,1,3,4,5],"result":[1,8,10,11],"retri":8,"retriev":[1,8,10,12],"return":[0,1,3,4,5,6,7,8,10,11],"return_full_metadata":5,"rev":3,"revers":8,"revis":3,"right":1,"rm":[2,13],"rm_path":[2,13],"root":[11,12],"row":[7,8,11,12],"row_offset":8,"rtype":3,"run":[8,11],"s":[1,4,8,10,11],"s3":[1,10,11],"safe":11,"sampl":[8,10],"sandbox":[0,1,3,4,5,6,7,8,10,13],"save":10,"save_directori":10,"schema":8,"scope":[0,1],"screen":8,"script":2,"search":[0,2,3],"second":[1,4],"secret":0,"see":[7,10,11],"self":10,"semaphon":12,"semaphor":12,"separ":[0,11],"server":10,"servic":[1,3,4,5,6,8,11,12,13],"service_loc":[3,4,5],"session":11,"set":[0,1,5,10],"setup":2,"sheepdog":8,"show":10,"show_progress":10,"shown":11,"sign":1,"signpost":3,"similar":10,"simpl":3,"simpli":11,"sinc":3,"singl":[1,5,8],"size":[2,3,9,10,11],"skip":8,"skip_complet":1,"sleep":4,"someth":11,"sort":7,"sort_field":7,"sort_object":7,"sourc":[0,1,2,3,4,5,6,7,8,10,11,12,13],"space":[0,11],"specif":[5,8,11,12],"specifi":[0,1,3,11,13],"spreadsheet":8,"src_path":13,"src_urlstr":13,"src_ws":13,"src_wskey":13,"ssl":[3,4,5],"start":[4,7,8,10],"start_tim":[2,9,10],"static":10,"status":[2,4,9,10],"status_cod":10,"storag":[1,2,6],"store":[1,3,10],"str":[0,1,3,4,5,7,8,10,11,12],"string":[0,3,5,11,13],"strip":11,"sub":8,"subject":[7,8],"submiss":2,"submit":[8,11],"submit_additional_metadata_column":11,"submit_fil":[2,8],"submit_record":[2,8],"submitter_id":7,"success":[1,10],"suffici":3,"suppli":3,"support":[0,1,5,8,11],"sure":1,"synchron":11,"syntax":7,"system":[6,7,8,9],"t":[1,5,11],"tab":11,"task":9,"temporari":11,"test":11,"test1":11,"test2":11,"test3":11,"test4":11,"test5":11,"text":[1,7,8],"thread":11,"thread_num":11,"threadcontrol":[2,9,11],"tier":7,"time":[1,3,8,10,11],"timestamp":10,"tmp_folder":[2,9,11],"token":[0,10],"tool":2,"total":11,"treat":[1,5],"tree":10,"tri":0,"true":[1,3,4,5,6,7,8,10,11,12],"tsv":[8,11,12],"tupl":[0,3,11,12],"type":[1,3,4,5,7,8,10,11,12],"typic":10,"uc":7,"unaccess":7,"uniqu":[1,5],"unknown":10,"unpack":10,"unpack_packag":10,"updat":[2,3,5,10,11],"update_alias":[2,5],"update_blank":[2,3],"update_record":[2,3],"updated_tim":[2,9,10],"upload":[1,2,3,8,13],"upload_fil":[1,2],"upload_file_to_guid":[1,2],"upload_url":[2,13],"url":[1,2,3,9,10,11,12,13],"urls_metadata":3,"usag":11,"use":[0,1,3,4,5,6,7,8,10,11,12,13],"use_agg_md":5,"user":[0,10,12],"user_access":[2,9,10],"usual":12,"utcnow":1,"util":9,"uuid":[1,3,8],"uuid1":8,"uuid2":8,"valid":[3,7],"valu":[0,1,3,5,7,10,11],"value_from_indexd":11,"value_from_manifest":11,"variabl":[0,7,8],"various":2,"verbos":[7,8],"verif":11,"verifi":[2,9],"verify_manifest":11,"verify_object_manifest":11,"version":[3,4,5],"vital_status":7,"wait":4,"want":[0,3,8],"warn":11,"way":10,"web":0,"whether":[3,4,5,8,11,12],"whose":5,"will":[1,3,4,5,7,10,11,12],"within":[0,2,9],"without":[3,5],"won":5,"work":[0,10],"workaround":11,"worksheet":8,"workspac":[0,2],"wrapper":10,"write":11,"ws":13,"ws_urlstr":13,"wskey":13,"wss":13,"wts":[0,10],"x":11,"xlsx":8},"titles":["Gen3 Auth Helper","Gen3 File Class","Welcome to Gen3 SDK\u2019s documentation!","Gen3 Index Class","Gen3 Jobs Class","Gen3 Metadata Class","Gen3 Object Class","Gen3 Query Class","Gen3 Submission Class","Gen3 Tools","DRS Download Tools","Indexing Tools","Metadata Tools","Gen3 Workspace Storage"],"titleterms":{"auth":0,"class":[1,3,4,5,6,7,8],"document":2,"download":[10,11],"drs":10,"file":1,"gen3":[0,1,2,3,4,5,6,7,8,9,13],"helper":0,"index":[3,11],"indic":2,"ingest":12,"job":4,"metadata":[5,12],"object":6,"queri":7,"s":2,"sdk":2,"storag":13,"submiss":8,"tabl":2,"tool":[9,10,11,12],"verifi":11,"welcom":2,"workspac":13}}) \ No newline at end of file diff --git a/docs/howto/asyncDownloadMultiple.md b/docs/howto/asyncDownloadMultiple.md new file mode 100644 index 00000000..4dee1153 --- /dev/null +++ b/docs/howto/asyncDownloadMultiple.md @@ -0,0 +1,186 @@ +## Asynchronous Multiple File Downloads + +The Gen3 SDK provides an optimized asynchronous download method `async_download_multiple` for efficiently downloading large numbers of files with high throughput and memory efficiency. + +## Overview + +The `async_download_multiple` method implements a hybrid architecture combining: + +- **Multiprocessing**: Multiple Python subprocesses for CPU utilization +- **Asyncio**: High I/O concurrency within each process +- **Queue-based memory management**: Efficient handling of large file sets +- **Just-in-time presigned URL generation**: Optimized authentication flow + +## Architecture + +### Concurrency Model + +The implementation uses a three-tier architecture: + +1. **Producer Thread**: Feeds GUIDs to worker processes via bounded queues +2. **Worker Processes**: Multiple Python subprocesses with asyncio event loops +3. **Queue System**: Memory-efficient streaming of work items + +```python +# Architecture overview +Producer Thread → Input Queue → Worker Processes → Output Queue → Results + (1) (configurable) (configurable) (configurable) (Final) +``` + +### Key Features + +- **Memory Efficiency**: Bounded queues prevent memory explosion with large file sets +- **True Parallelism**: Multiprocessing bypasses Python GIL limitations +- **High Concurrency**: Configurable concurrent downloads per process +- **Resume Support**: Skip completed files with `--skip-completed` flag +- **Progress Tracking**: Real-time progress bars and detailed reporting + +## Usage + +### Command Line Interface + +Download multiple files using a manifest: + +```bash +gen3 --endpoint my-commons.org --auth credentials.json download-multiple \ + --manifest files.json \ + --download-path ./downloads \ + --max-concurrent-requests 10 \ + --filename-format original \ + --skip-completed \ + --no-prompt +``` + +### Python API + +The `async_download_multiple` method is available in the `Gen3File` class for programmatic use. Refer to the Python SDK documentation for the complete API reference. + +## Parameters + +For detailed parameter information and current default values, run: + +```bash +gen3 download-multiple --help +``` + +The command supports various options for customizing download behavior, including concurrency settings, file naming strategies, and progress controls. + +## Performance Characteristics + +### Throughput Optimization + +The method is optimized for high-throughput scenarios: + +- **Concurrent Downloads**: Configurable number of simultaneous downloads +- **Memory Usage**: Bounded by queue sizes (typically < 100MB) +- **CPU Utilization**: Leverages multiple CPU cores +- **Network Efficiency**: Just-in-time presigned URL generation + +### Scalability + +Performance scales with: + +- **File Count**: Linear time complexity with constant memory usage +- **File Size**: Independent of individual file sizes +- **Network Bandwidth**: Limited by available bandwidth and concurrent connections +- **System Resources**: Scales with available CPU cores and memory + +## Error Handling + +### Robust Error Recovery + +The implementation includes comprehensive error handling: + +- **Network Failures**: Automatic retry with exponential backoff +- **Authentication Errors**: Token refresh and retry +- **File System Errors**: Graceful handling of permission and space issues +- **Process Failures**: Automatic worker process restart + +### Result Reporting + +The method returns a structured result object containing lists of succeeded, failed, and skipped downloads with detailed information about each operation. + +## Best Practices + +### Configuration Recommendations + +For optimal performance, adjust the concurrency and process settings based on your specific use case: + +- **Small files**: Use higher concurrent request limits +- **Large files**: Use lower concurrent request limits to avoid overwhelming the system +- **High-bandwidth networks**: Increase the number of worker processes +- **Limited memory**: Reduce queue sizes to manage memory usage + + +## Comparison with Synchronous Downloads + +### Performance Advantages + +| Metric | Synchronous | Asynchronous | +| ------------------ | ---------------------------- | ---------------------------- | +| Memory Usage | O(n) - grows with file count | O(1) - bounded by queue size | +| CPU Utilization | Single core | Multiple cores | +| Network Efficiency | Sequential | Parallel | +| Scalability | Limited by GIL | Scales with CPU cores | + +## Troubleshooting + +### Common Issues + +**Slow Downloads:** + +- Check network bandwidth and server limits +- Reduce concurrent request limits if server is overwhelmed + +**Memory Issues:** + +- Reduce queue sizes and batch sizes +- Lower the number of worker processes if system memory is limited +- Monitor system memory usage during downloads + +**Authentication Errors:** + +- Verify credentials file is valid and not expired +- Check endpoint URL is correct +- Ensure proper permissions for target files + +**Process Failures:** + +- Check system resources (CPU, memory, file descriptors) +- Verify network connectivity to Gen3 commons +- Review logs for specific error messages + +### Debugging + +Enable verbose logging for detailed debugging: + +```bash +gen3 -vv --endpoint my-commons.org --auth credentials.json download-multiple \ + --manifest files.json \ + --download-path ./downloads +``` + +## Examples + +### Basic Usage + +```bash +# Download files with default settings +gen3 --endpoint data.commons.io --auth creds.json download-multiple \ + --manifest my_files.json \ + --download-path ./data +``` + +### High-Performance Configuration + +```bash +# Optimized for high-throughput downloads +gen3 --endpoint data.commons.io --auth creds.json download-multiple \ + --manifest large_dataset.json \ + --download-path ./large_downloads \ + --max-concurrent-requests 8 \ + --no-progress \ + --skip-completed +``` + +**Note**: The specific values shown in examples (like `--max-concurrent-requests 8`) are for demonstration only. For current parameter options and default values, always refer to the command line help: `gen3 download-multiple --help` diff --git a/gen3/cli/__main__.py b/gen3/cli/__main__.py index 5a51be11..a701efb5 100644 --- a/gen3/cli/__main__.py +++ b/gen3/cli/__main__.py @@ -15,6 +15,7 @@ import gen3.cli.drs_pull as drs_pull import gen3.cli.users as users import gen3.cli.wrap as wrap +import gen3.cli.download as download import gen3 from gen3 import logging as sdklogging from gen3.cli import nih @@ -142,6 +143,8 @@ def main( main.add_command(objects.objects) main.add_command(drs_pull.drs_pull) main.add_command(file.file) +main.add_command(download.download_single, name="download-single") +main.add_command(download.download_multiple, name="download-multiple") main.add_command(nih.nih) main.add_command(users.users) main.add_command(wrap.run) diff --git a/gen3/cli/download.py b/gen3/cli/download.py new file mode 100644 index 00000000..ab8bb91b --- /dev/null +++ b/gen3/cli/download.py @@ -0,0 +1,305 @@ +""" +Gen3 download commands for CLI. +""" + +import asyncio +import json +from datetime import datetime +from typing import List, Dict, Any +import os + +import click + +from cdislogging import get_logger +from gen3.file import Gen3File + +logging = get_logger("__name__") + +MAX_CONCURRENT_REQUESTS = 20 + + +def get_or_create_event_loop_for_thread(): + """Get or create event loop for current thread.""" + try: + loop = asyncio.get_event_loop() + except RuntimeError: + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + return loop + + +def load_manifest(manifest_path: str) -> List[Dict[str, Any]]: + """Load manifest from JSON file. + + Args: + manifest_path (str): Path to the manifest JSON file. + + Returns: + List[Dict[str, Any]]: List of dictionaries containing file information. + + Raises: + FileNotFoundError: If the manifest file does not exist. + json.JSONDecodeError: If the manifest file contains invalid JSON. + """ + try: + with open(manifest_path, "r") as f: + return json.load(f) + except (FileNotFoundError, json.JSONDecodeError) as e: + raise click.ClickException(f"Error loading manifest: {e}") + + +def validate_manifest(manifest_data: List[Dict[str, Any]]) -> bool: + """Validate manifest structure. + + Args: + manifest_data (List[Dict[str, Any]]): List of dictionaries to validate. + + Returns: + bool: True if manifest is valid, False otherwise. + """ + if not isinstance(manifest_data, list): + return False + + for item in manifest_data: + if not isinstance(item, dict): + return False + if not (item.get("guid") or item.get("object_id")): + return False + + return True + + +@click.command() +@click.argument("guid") +@click.option( + "--download-path", + default=f"./download_{datetime.now().strftime('%d_%b_%Y')}", + help="Directory to download file to (default: timestamped folder)", +) +@click.option( + "--protocol", + default=None, + help="Protocol for presigned URL (e.g., s3) (default: auto-detect)", +) +@click.option( + "--filename-format", + default="original", + type=click.Choice(["original", "guid", "combined"]), + help="Filename format (default: original)", +) +@click.option( + "--skip-completed", + is_flag=True, + default=False, + help="Skip file if it already exists (default: false)", +) +@click.option( + "--rename", + is_flag=True, + help="Rename file if it already exists (default: false)", +) +@click.option( + "--no-progress", + is_flag=True, + help="Disable progress bar (default: false)", +) +@click.pass_context +def download_single( + ctx, + guid, + download_path, + protocol, + filename_format, + skip_completed, + rename, + no_progress, +): + """Download a single file by GUID using the async download pipeline.""" + auth = ctx.obj["auth_factory"].get() + + download_path = os.path.abspath(download_path) + os.makedirs(download_path, exist_ok=True) + + # Build a single-item manifest compatible with async_download_multiple + manifest_data = [{"guid": guid}] + + try: + file_client = Gen3File(auth_provider=auth) + + loop = get_or_create_event_loop_for_thread() + result = loop.run_until_complete( + file_client.async_download_multiple( + manifest_data=manifest_data, + download_path=download_path, + filename_format=filename_format, + protocol=protocol, + max_concurrent_requests=1, + num_processes=1, + queue_size=1, + skip_completed=skip_completed, + rename=rename, + no_progress=no_progress, + ) + ) + + if result["succeeded"]: + click.echo(f"✓ Downloaded GUID {guid} to {download_path}") + elif result["skipped"]: + click.echo(f"- Skipped GUID {guid} (already exists)") + else: + click.echo(f"✗ Failed to download GUID {guid}") + if result.get("failed"): + logging.error(f"Failure details: {result['failed']}") + + except Exception as e: + logging.error(f"Download failed: {e}") + raise click.ClickException(f"Download failed: {e}") + + +@click.command() +@click.option("--manifest", required=True, help="Path to manifest JSON file") +@click.option( + "--download-path", + default=f"download_{datetime.now().strftime('%d_%b_%Y')}", + help="Directory to download files to (default: timestamped folder)", +) +@click.option( + "--filename-format", + default="original", + type=click.Choice(["original", "guid", "combined"]), + help="Filename format: 'original' uses the original filename from metadata, 'guid' uses only the file GUID, 'combined' uses original filename with GUID appended (default: original)", +) +@click.option( + "--protocol", + default=None, + help="Protocol for presigned URLs (e.g., s3) (default: auto-detect)", +) +@click.option( + "--max-concurrent-requests", + default=MAX_CONCURRENT_REQUESTS, + help="Maximum concurrent async downloads per process (default: 20)", + type=int, +) +@click.option( + "--numparallel", + default=None, + help="Same as max-concurrent-requests, for compatibility with previous gen3-client arguments", + type=int, +) +@click.option( + "--num-processes", + default=3, + help="Number of worker processes for parallel downloads (default: 3)", + type=int, +) +@click.option( + "--queue-size", + default=1000, + help="Maximum items in input queue (default: 1000)", + type=int, +) +@click.option( + "--skip-completed", + type=bool, + default=True, + help="Skip files that already exist (default: true)", +) +@click.option( + "--rename", is_flag=True, help="Rename files if they already exist (default: false)" +) +@click.option( + "--no-prompt", is_flag=True, help="Do not prompt for confirmations (default: false)" +) +@click.option( + "--no-progress", is_flag=True, help="Disable progress bar (default: false)" +) +@click.pass_context +def download_multiple( + ctx, + manifest, + download_path, + filename_format, + protocol, + max_concurrent_requests, + numparallel, + num_processes, + queue_size, + skip_completed=True, + rename=False, + no_prompt=False, + no_progress=False, +): + """ + Asynchronously download multiple files from a manifest with just-in-time presigned URL generation. + """ + auth = ctx.obj["auth_factory"].get() + + # Use numparallel as max_concurrent_requests if provided (for gen3-client compatibility) + if ( + numparallel is not None and max_concurrent_requests == MAX_CONCURRENT_REQUESTS + ): # 20 is the default + max_concurrent_requests = numparallel + + try: + manifest_data = load_manifest(manifest) + + if not validate_manifest(manifest_data): + raise click.ClickException("Invalid manifest format") + + if not manifest_data: + click.echo("No files to download") + return + + if not no_prompt: + click.echo(f"Found {len(manifest_data)} files to download") + if not click.confirm("Continue with async download?"): + click.echo("Download cancelled") + return + + file_client = Gen3File(auth_provider=auth) + + # Debug logging for input parameters + logging.debug( + f"Async download parameters: manifest_data={len(manifest_data)} items, download_path={download_path}, filename_format={filename_format}, protocol={protocol}, max_concurrent_requests={max_concurrent_requests}, skip_completed={skip_completed}, rename={rename}, no_progress={no_progress}" + ) + + loop = get_or_create_event_loop_for_thread() + result = loop.run_until_complete( + file_client.async_download_multiple( + manifest_data=manifest_data, + download_path=download_path, + filename_format=filename_format, + protocol=protocol, + max_concurrent_requests=max_concurrent_requests, + num_processes=num_processes, + queue_size=queue_size, + skip_completed=skip_completed, + rename=rename, + no_progress=no_progress, + ) + ) + + click.echo("\nAsync Download Results:") + click.echo(f"✓ Succeeded: {len(result['succeeded'])}") + + if result["skipped"] and len(result["skipped"]) > 0: + click.echo(f"- Skipped: {len(result['skipped'])}") + + if result["failed"] and len(result["failed"]) > 0: + click.echo(f"✗ Failed: {len(result['failed'])}") + + if result["failed"]: + click.echo( + "\nTo retry failed downloads, run the same command with --skip-completed true:" + ) + + success_rate = ( + (len(result["succeeded"]) + len(result["skipped"])) + / len(manifest_data) + * 100 + ) + click.echo(f"\nSuccess rate: {success_rate:.1f}%") + + except Exception as e: + logging.error(f"Async batch download failed: {e}") + raise click.ClickException(f"Async batch download failed: {e}") diff --git a/gen3/file.py b/gen3/file.py index 75161574..357d1134 100644 --- a/gen3/file.py +++ b/gen3/file.py @@ -1,26 +1,32 @@ import json import requests -import json import asyncio import aiohttp import aiofiles import time +import multiprocessing as mp +import threading from tqdm import tqdm -from types import SimpleNamespace as Namespace import os -import requests from pathlib import Path +from typing import List, Dict, Any, Optional +from urllib.parse import parse_qsl, urlencode, urlparse, urlunparse, quote +from queue import Empty from cdislogging import get_logger +from werkzeug.security import safe_join + from gen3.index import Gen3Index -from gen3.utils import DEFAULT_BACKOFF_SETTINGS, raise_for_status_and_print_error -from urllib.parse import parse_qsl, urlencode, urlparse, urlunparse +from gen3.utils import raise_for_status_and_print_error logging = get_logger("__name__") MAX_RETRIES = 3 +DEFAULT_NUM_PARALLEL = 3 +DEFAULT_MAX_CONCURRENT_REQUESTS = 300 +DEFAULT_QUEUE_SIZE = 1000 class Gen3File: @@ -45,7 +51,6 @@ def __init__(self, endpoint=None, auth_provider=None): # auth_provider legacy interface required endpoint as 1st arg self._auth_provider = auth_provider or endpoint self._endpoint = self._auth_provider.endpoint - self.unsuccessful_downloads = [] def get_presigned_url(self, guid, protocol=None): """Generates a presigned URL for a file. @@ -67,10 +72,26 @@ def get_presigned_url(self, guid, protocol=None): resp = requests.get(api_url, auth=self._auth_provider) raise_for_status_and_print_error(resp) - try: - return resp.json() - except: - return resp.text + return resp.json() + + def get_presigned_urls_batch(self, guids, protocol=None): + """Get presigned URLs for multiple files efficiently. + + Args: + guids (List[str]): List of GUIDs to get presigned URLs for + protocol (str, optional): Protocol preference for URLs + + Returns: + Dict[str, Dict]: Mapping of GUID to presigned URL response + """ + results = {} + for guid in guids: + try: + results[guid] = self.get_presigned_url(guid, protocol) + except Exception as e: + logging.error(f"Failed to get presigned URL for {guid}: {e}") + results[guid] = None + return results def delete_file(self, guid): """ @@ -140,12 +161,7 @@ def upload_file( api_url, auth=self._auth_provider, json=body, headers=headers ) raise_for_status_and_print_error(resp) - try: - data = json.loads(resp.text) - except: - return resp.text - - return data + return resp.json() def _ensure_dirpath_exists(path: Path) -> Path: """Utility to create a directory if missing. @@ -163,16 +179,19 @@ def _ensure_dirpath_exists(path: Path) -> Path: return out_path - def download_single(self, object_id, path): + def download_single(self, object_id, path, protocol=None): """ Download a single file using its GUID. Args: object_id (str): The file's unique ID path (str): Path to store the downloaded file at + + Returns: + bool: True if download successful, False otherwise """ try: - url = self.get_presigned_url(object_id) + url = self.get_presigned_url(object_id, protocol=protocol) except Exception as e: logging.critical(f"Unable to get a presigned URL for download: {e}") return False @@ -186,9 +205,9 @@ def download_single(self, object_id, path): # NOTE could be updated with exponential backoff time.sleep(1) response = requests.get(url["url"], stream=True) - if response.status == 200: + if response.status_code == 200: break - if response.status != 200: + if response.status_code != 200: logging.critical("Response status not 200, try again later") return False else: @@ -202,18 +221,18 @@ def download_single(self, object_id, path): index = Gen3Index(self._auth_provider) record = index.get_record(object_id) - filename = record["file_name"] + filename = record["file_name"] or url["url"].split("?")[0].split("/")[-1] - out_path = Gen3File._ensure_dirpath_exists(Path(path)) + out_path = safe_join(path, filename) + Gen3File._ensure_dirpath_exists(Path(os.path.dirname(out_path))) - with open(os.path.join(out_path, filename), "wb") as f: + with open(out_path, "wb") as f: for data in response.iter_content(4096): total_downloaded += len(data) f.write(data) if total_size_in_bytes == total_downloaded: logging.info(f"File {filename} downloaded successfully") - else: logging.error(f"File {filename} not downloaded successfully") return False @@ -259,3 +278,438 @@ def upload_file_to_guid( resp = requests.get(url, auth=self._auth_provider) raise_for_status_and_print_error(resp) return resp.json() + + async def async_download_multiple( + self, + manifest_data, + download_path=".", + filename_format="original", + protocol=None, + max_concurrent_requests=DEFAULT_MAX_CONCURRENT_REQUESTS, + num_processes=DEFAULT_NUM_PARALLEL, + queue_size=DEFAULT_QUEUE_SIZE, + skip_completed=False, + rename=False, + no_progress=False, + ): + """Asynchronously download multiple files using multiprocessing and queues.""" + if not manifest_data: + return {"succeeded": [], "failed": [], "skipped": []} + + guids = [] + for item in manifest_data: + guid = item.get("guid") or item.get("object_id") + if guid: + if "/" in guid: + guid = guid.split("/")[-1] + guids.append(guid) + + if not guids: + logging.error("No valid GUIDs found in manifest data") + return {"succeeded": [], "failed": [], "skipped": []} + + output_dir = Gen3File._ensure_dirpath_exists(Path(download_path)) + + input_queue = mp.Queue(maxsize=queue_size) + output_queue = mp.Queue() + + worker_config = { + "endpoint": self._endpoint, + "auth_provider": self._auth_provider, + "download_path": str(output_dir), + "filename_format": filename_format, + "protocol": protocol, + "max_concurrent": max_concurrent_requests, + "skip_completed": skip_completed, + "rename": rename, + } + + processes = [] + producer_thread = None + + try: + for i in range(num_processes): + p = mp.Process( + target=self._async_worker_process, + args=(input_queue, output_queue, worker_config, i), + ) + p.start() + processes.append(p) + + producer_thread = threading.Thread( + target=self._guid_producer, + args=(guids, input_queue, num_processes), + ) + producer_thread.start() + + results = {"succeeded": [], "failed": [], "skipped": []} + completed_count = 0 + + if not no_progress: + pbar = tqdm(total=len(guids), desc="Downloading") + + while completed_count < len(guids): + try: + batch_results = output_queue.get() + + if not batch_results: + continue + + for result in batch_results: + if result["status"] == "downloaded": + results["succeeded"].append(result["guid"]) + elif result["status"] == "skipped": + results["skipped"].append(result["guid"]) + else: + results["failed"].append(result["guid"]) + + completed_count += 1 + if not no_progress: + pbar.update(1) + + except Empty: + logging.warning( + f"No more results available ({completed_count}/{len(guids)}): Queue is empty" + ) + break + except Exception as e: + logging.warning( + f"Error waiting for results ({completed_count}/{len(guids)}): {e}" + ) + + alive_processes = [p for p in processes if p.is_alive()] + if not alive_processes: + logging.error("All worker processes have died") + break + + if not no_progress: + pbar.close() + + if producer_thread: + producer_thread.join() + + except Exception as e: + logging.error(f"Error in download: {e}") + results = {"succeeded": [], "failed": [], "skipped": [], "error": str(e)} + + finally: + for p in processes: + if p.is_alive(): + p.terminate() + + p.join() + if p.is_alive(): + p.kill() + + logging.info( + f"Download complete: {len(results['succeeded'])} succeeded, " + f"{len(results['failed'])} failed, {len(results['skipped'])} skipped" + ) + return results + + def _guid_producer(self, guids, input_queue, num_processes): + try: + for guid in guids: + input_queue.put(guid) + + except Exception as e: + logging.error(f"Error in producer: {e}") + + @staticmethod + def _async_worker_process(input_queue, output_queue, config, process_id): + try: + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + loop.run_until_complete( + Gen3File._worker_main(input_queue, output_queue, config, process_id) + ) + except Exception as e: + logging.error(f"Error in worker process {process_id}: {e}") + finally: + try: + loop.close() + except Exception as e: + logging.warning(f"Error closing event loop in worker {process_id}: {e}") + + @staticmethod + async def _worker_main(input_queue, output_queue, config, process_id): + endpoint = config["endpoint"] + auth_provider = config["auth_provider"] + download_path = Path(config["download_path"]) + filename_format = config["filename_format"] + protocol = config["protocol"] + max_concurrent = config["max_concurrent"] + skip_completed = config["skip_completed"] + rename = config["rename"] + + # Configure connector with optimized settings for large files + timeout = aiohttp.ClientTimeout(total=None, connect=3600, sock_read=3600) + connector = aiohttp.TCPConnector( + limit=max_concurrent * 2, + limit_per_host=max_concurrent, + ttl_dns_cache=300, + use_dns_cache=True, + keepalive_timeout=3600, + enable_cleanup_closed=True, + ) + semaphore = asyncio.Semaphore(max_concurrent) + + async with aiohttp.ClientSession( + connector=connector, timeout=timeout + ) as session: + while True: + try: + # Check if queue is empty with timeout + guid = input_queue.get() + except Empty: + # If queue is empty (timeout), break the loop + break + + # Process single GUID as a batch of one + try: + batch_results = await Gen3File._process_batch( + session, + [guid], + endpoint, + auth_provider, + download_path, + filename_format, + protocol, + semaphore, + skip_completed, + rename, + ) + output_queue.put(batch_results) + except Exception as e: + logging.error( + f"Worker {process_id}: Failed to process {guid} - {type(e).__name__}: {e}" + ) + error_result = [{"guid": guid, "status": "failed", "error": str(e)}] + try: + output_queue.put(error_result) + except Exception as queue_error: + logging.error( + f"Worker {process_id}: Failed to send error result for {guid} - {type(queue_error).__name__}: {queue_error}" + ) + + @staticmethod + async def _process_batch( + session, + guids, + endpoint, + auth_provider, + download_path, + filename_format, + protocol, + semaphore, + skip_completed, + rename, + ): + """Process a batch of GUIDs for downloading.""" + batch_results = [] + for guid in guids: + async with semaphore: + result = await Gen3File._download_single_async( + session, + guid, + endpoint, + auth_provider, + download_path, + filename_format, + protocol, + semaphore, + skip_completed, + rename, + ) + batch_results.append(result) + return batch_results + + @staticmethod + async def _download_single_async( + session, + guid, + endpoint, + auth_provider, + download_path, + filename_format, + protocol, + semaphore, + skip_completed, + rename, + ): + async with semaphore: + try: + metadata = await Gen3File._get_metadata( + session, guid, endpoint, auth_provider.get_access_token() + ) + + original_filename = metadata.get("file_name") + filename = Gen3File._format_filename_static( + guid, original_filename, filename_format + ) + filepath = download_path / filename + + if skip_completed and filepath.exists(): + return { + "guid": guid, + "status": "skipped", + "filepath": str(filepath), + "reason": "File already exists", + } + + filepath = Gen3File._handle_conflict_static(filepath, rename) + + presigned_data = await Gen3File._get_presigned_url_async( + session, guid, endpoint, auth_provider.get_access_token(), protocol + ) + + url = presigned_data.get("url") + if not url: + return { + "guid": guid, + "status": "failed", + "error": "No URL in presigned data", + } + + filepath.parent.mkdir(parents=True, exist_ok=True) + + success = await Gen3File._download_content(session, url, guid, filepath) + if success: + return { + "guid": guid, + "status": "downloaded", + "filepath": str(filepath), + "size": filepath.stat().st_size if filepath.exists() else 0, + } + else: + return { + "guid": guid, + "status": "failed", + "error": "Download failed", + } + + except Exception as e: + logging.error(f"Error downloading {guid}: {e}") + return { + "guid": guid, + "status": "failed", + "error": str(e), + } + + @staticmethod + async def _get_metadata(session, guid, endpoint, auth_token): + encoded_guid = quote(guid, safe="") + api_url = f"{endpoint}/index/{encoded_guid}" + headers = {"Authorization": f"Bearer {auth_token}"} + + try: + async with session.get( + api_url, headers=headers, timeout=aiohttp.ClientTimeout(total=3600) + ) as resp: + if resp.status == 200: + return await resp.json() + raise Exception( + f"Failed to get metadata for {guid}: HTTP {resp.status}" + ) + except aiohttp.ClientError as e: + raise Exception(f"Network error getting metadata for {guid}: {e}") + except asyncio.TimeoutError: + raise Exception(f"Timeout getting metadata for {guid}") + except Exception as e: + if "Failed to get metadata" not in str(e): + raise Exception(f"Unexpected error getting metadata for {guid}: {e}") + raise + + @staticmethod + async def _get_presigned_url_async( + session, guid, endpoint, auth_token, protocol=None + ): + encoded_guid = quote(guid, safe="") + api_url = f"{endpoint}/user/data/download/{encoded_guid}" + headers = {"Authorization": f"Bearer {auth_token}"} + + if protocol: + api_url += f"?protocol={protocol}" + + try: + async with session.get( + api_url, headers=headers, timeout=aiohttp.ClientTimeout(total=3600) + ) as resp: + if resp.status == 200: + return await resp.json() + raise Exception( + f"Failed to get presigned URL for {guid}: HTTP {resp.status}" + ) + except aiohttp.ClientError as e: + raise Exception(f"Network error getting presigned URL for {guid}: {e}") + except asyncio.TimeoutError: + raise Exception(f"Timeout getting presigned URL for {guid}") + except Exception as e: + if "Failed to get presigned URL" not in str(e): + raise Exception( + f"Unexpected error getting presigned URL for {guid}: {e}" + ) + raise + + @staticmethod + async def _download_content(session, url, guid, filepath): + """Download content directly to file with optimized streaming.""" + try: + async with session.get( + url, timeout=aiohttp.ClientTimeout(total=None) + ) as resp: + if resp.status == 200: + async with aiofiles.open(filepath, "wb") as f: + chunk_size = 1024 * 1024 + async for chunk in resp.content.iter_chunked(chunk_size): + await f.write(chunk) + return True + logging.error(f"Download failed for {guid}: HTTP {resp.status}") + return False + except aiohttp.ClientError as e: + logging.error(f"Network error downloading {guid}: {e}") + return False + except asyncio.TimeoutError: + logging.error(f"Timeout downloading {guid}") + return False + except OSError as e: + logging.error(f"File system error downloading {guid} to {filepath}: {e}") + return False + except Exception as e: + logging.error( + f"Unexpected error downloading {guid}: {type(e).__name__}: {e}" + ) + return False + + @staticmethod + def _format_filename_static(guid, original_filename, filename_format): + if filename_format == "guid": + return guid + elif filename_format == "combined": + if original_filename: + name, ext = os.path.splitext(original_filename) + return f"{name}_{guid}{ext}" + return guid + else: + return original_filename or guid + + @staticmethod + def _handle_conflict_static(filepath, rename): + if not rename: + if filepath.exists(): + logging.warning(f"File will be overwritten: {filepath}") + return filepath + + if not filepath.exists(): + return filepath + + counter = 1 + name = filepath.stem + ext = filepath.suffix + parent = filepath.parent + + while True: + new_path = parent / f"{name}_{counter}{ext}" + if not new_path.exists(): + return new_path + counter += 1 diff --git a/poetry.lock b/poetry.lock index 6fd94941..c7572f16 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. [[package]] name = "aiofiles" @@ -1768,7 +1768,7 @@ version = "3.0.3" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.9" -groups = ["dev"] +groups = ["main", "dev"] files = [ {file = "markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559"}, {file = "markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419"}, @@ -3118,18 +3118,18 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "werkzeug" -version = "3.1.3" +version = "3.1.8" description = "The comprehensive WSGI web application library." optional = false python-versions = ">=3.9" -groups = ["dev"] +groups = ["main", "dev"] files = [ - {file = "werkzeug-3.1.3-py3-none-any.whl", hash = "sha256:54b78bf3716d19a65be4fceccc0d1d7b89e608834989dfae50ea87564639213e"}, - {file = "werkzeug-3.1.3.tar.gz", hash = "sha256:60723ce945c19328679790e3282cc758aa4a6040e4bb330f53d30fa546d44746"}, + {file = "werkzeug-3.1.8-py3-none-any.whl", hash = "sha256:63a77fb8892bf28ebc3178683445222aa500e48ebad5ec77b0ad80f8726b1f50"}, + {file = "werkzeug-3.1.8.tar.gz", hash = "sha256:9bad61a4268dac112f1c5cd4630a56ede601b6ed420300677a869083d70a4c44"}, ] [package.dependencies] -MarkupSafe = ">=2.1.1" +markupsafe = ">=2.1.1" [package.extras] watchdog = ["watchdog (>=2.3)"] @@ -3318,4 +3318,4 @@ fhir = ["fhirclient"] [metadata] lock-version = "2.1" python-versions = ">=3.9, <4" -content-hash = "0ef223739ee0f4e9faf3bf60aa2c6ca6ea2757f7b84023e93c838109379a6915" +content-hash = "78dbea877df3184bb2e29fb27df7fa74e615204a72dd0bf98452c097783d5e63" diff --git a/pyproject.toml b/pyproject.toml index 9a2caddd..54408cab 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,6 +42,7 @@ gen3users = "*" # A list of all of the optional dependencies, some of which are included in the # below `extras`. They can be opted into by apps. fhirclient = { version = "*", optional = true } +werkzeug = "^3.1.6" [tool.poetry.extras] fhir = ["fhirclient"] diff --git a/tests/download_tests/test_async_download.py b/tests/download_tests/test_async_download.py index f42442b3..5f4b8c91 100644 --- a/tests/download_tests/test_async_download.py +++ b/tests/download_tests/test_async_download.py @@ -1,4 +1,4 @@ -from unittest.mock import patch +from unittest.mock import patch, MagicMock, AsyncMock import json import pytest from pathlib import Path @@ -264,3 +264,30 @@ def test_load_manifest_bad_format(self): manifest_list = _load_manifest(Path(DIR, "resources/bad_format.json")) assert manifest_list == None + + @pytest.mark.asyncio + async def test_async_download_multiple_empty_manifest(self, mock_gen3_auth): + """ + Test async_download_multiple with an empty manifest. + Verifies it returns empty results without errors. + """ + file_tool = Gen3File(mock_gen3_auth) + result = await file_tool.async_download_multiple(manifest_data=[]) + + assert result == {"succeeded": [], "failed": [], "skipped": []} + + @pytest.mark.asyncio + async def test_async_download_multiple_invalid_guids(self, mock_gen3_auth): + """ + Test async_download_multiple with invalid GUIDs. + Verifies it returns empty results for missing GUIDs. + """ + file_tool = Gen3File(mock_gen3_auth) + + # Manifest with missing guid/object_id fields + manifest_data = [{"file_name": "test.txt"}, {}] + + result = await file_tool.async_download_multiple(manifest_data=manifest_data) + + assert result == {"succeeded": [], "failed": [], "skipped": []} + diff --git a/tests/test_file.py b/tests/test_file.py index a02a80eb..8ea04bf2 100644 --- a/tests/test_file.py +++ b/tests/test_file.py @@ -1,9 +1,11 @@ """ Tests gen3.file.Gen3File for calls """ -from unittest.mock import patch -import json + +from unittest.mock import patch, MagicMock import pytest +import tempfile +from pathlib import Path from requests import HTTPError @@ -248,20 +250,37 @@ def test_upload_file( expected response to compare with mock """ with patch("gen3.file.requests") as mock_request: - mock_request.status_code = status_code - mock_request.post().text = response_text - res = gen3_file.upload_file( - file_name="file.txt", - authz=authz, - protocol=supported_protocol, - expires_in=expires_in, + mock_response = MagicMock() + mock_response.status_code = status_code + mock_response.text = response_text + mock_response.json.return_value = ( + expected_response if status_code == 201 else {} ) + + # Make raise_for_status() raise HTTPError for non-2xx status codes + if status_code >= 400: + mock_response.raise_for_status.side_effect = HTTPError() + + mock_request.post.return_value = mock_response + if status_code == 201: + res = gen3_file.upload_file( + file_name="file.txt", + authz=authz, + protocol=supported_protocol, + expires_in=expires_in, + ) # check that the SDK is getting fence assert res.get("url") == expected_response["url"] else: - # check the error message - assert expected_response in res + # For non-201 status codes, the method should raise an exception + with pytest.raises(HTTPError): + gen3_file.upload_file( + file_name="file.txt", + authz=authz, + protocol=supported_protocol, + expires_in=expires_in, + ) @pytest.mark.parametrize( @@ -326,7 +345,7 @@ def test_upload_file_no_refresh_token(gen3_file, supported_protocol, authz, expi def test_upload_file_no_api_key(gen3_file, supported_protocol, authz, expires_in): """ Upload files for a Gen3File given a protocol, authz, and expires_in - without an api_key in the refresh token, which should return a 401 + without an api_key in the refresh token, which should raise an HTTPError :param gen3.file.Gen3File gen3_file: Gen3File object @@ -341,15 +360,19 @@ def test_upload_file_no_api_key(gen3_file, supported_protocol, authz, expires_in gen3_file._auth_provider._refresh_token = {"not_api_key": "123"} with patch("gen3.file.requests") as mock_request: - mock_request.status_code = 401 - mock_request.post().text = "Failed to upload data file." - res = gen3_file.upload_file( - file_name="file.txt", - authz=authz, - protocol=supported_protocol, - expires_in=expires_in, - ) - assert res == "Failed to upload data file." + mock_response = MagicMock() + mock_response.status_code = 401 + mock_response.text = "Failed to upload data file." + mock_response.raise_for_status.side_effect = HTTPError() + mock_request.post.return_value = mock_response + + with pytest.raises(HTTPError): + gen3_file.upload_file( + file_name="file.txt", + authz=authz, + protocol=supported_protocol, + expires_in=expires_in, + ) @pytest.mark.parametrize( @@ -371,7 +394,7 @@ def test_upload_file_no_api_key(gen3_file, supported_protocol, authz, expires_in def test_upload_file_wrong_api_key(gen3_file, supported_protocol, authz, expires_in): """ Upload files for a Gen3File given a protocol, authz, and expires_in - with the wrong value for the api_key in the refresh token, which should return a 401 + with the wrong value for the api_key in the refresh token, which should raise an HTTPError :param gen3.file.Gen3File gen3_file: Gen3File object @@ -386,12 +409,187 @@ def test_upload_file_wrong_api_key(gen3_file, supported_protocol, authz, expires gen3_file._auth_provider._refresh_token = {"api_key": "wrong_value"} with patch("gen3.file.requests") as mock_request: - mock_request.status_code = 401 - mock_request.post().text = "Failed to upload data file." - res = gen3_file.upload_file( - file_name="file.txt", - authz=authz, - protocol=supported_protocol, - expires_in=expires_in, + mock_response = MagicMock() + mock_response.status_code = 401 + mock_response.text = "Failed to upload data file." + mock_response.raise_for_status.side_effect = HTTPError() + mock_request.post.return_value = mock_response + + with pytest.raises(HTTPError): + gen3_file.upload_file( + file_name="file.txt", + authz=authz, + protocol=supported_protocol, + expires_in=expires_in, + ) + + +@pytest.fixture +def mock_manifest_data(): + return [ + {"guid": "test-guid-1", "file_name": "file1.txt"}, + {"guid": "test-guid-2", "file_name": "file2.txt"}, + {"object_id": "test-guid-3", "file_name": "file3.txt"}, + ] + + +def test_download_single_success(gen3_file): + """ + Test download_single basic functionality with synchronous download. + + Verifies that download_single downloads a file successfully using + synchronous requests and returns True. + """ + gen3_file._auth_provider._refresh_token = {"api_key": "123"} + + with patch.object(gen3_file, "get_presigned_url") as mock_presigned, patch( + "gen3.file.requests.get" + ) as mock_get, patch("gen3.index.Gen3Index.get_record") as mock_index: + mock_presigned.return_value = {"url": "https://fake-url.com/file"} + mock_index.return_value = {"file_name": "test-file.txt"} + mock_response = MagicMock() + mock_response.status_code = 200 + mock_response.headers = {"content-length": "12"} + mock_response.iter_content = lambda size: [b"test content"] + mock_get.return_value = mock_response + + result = gen3_file.download_single( + object_id="test-guid", path="/tmp", protocol="s3" + ) + + assert result == True + mock_presigned.assert_called_once_with("test-guid", protocol="s3") + mock_index.assert_called_once_with("test-guid") + + +def test_download_single_failed(gen3_file): + """ + Test download_single basic functionality with synchronous download. + + Verifies that download_single downloads a file successfully using + synchronous requests and returns True. + """ + gen3_file._auth_provider._refresh_token = {"api_key": "123"} + + with patch.object(gen3_file, "get_presigned_url") as mock_presigned, patch( + "gen3.file.requests.get" + ) as mock_get, patch("gen3.index.Gen3Index.get_record") as mock_index: + mock_presigned.return_value = {"url": "https://fake-url.com/file"} + mock_index.return_value = {"file_name": "test-file.txt"} + mock_response = MagicMock() + mock_response.status_code = 404 + mock_response.headers = {"content-length": "12"} + mock_response.iter_content = lambda size: [b"test content"] + mock_get.return_value = mock_response + + result = gen3_file.download_single( + object_id="test-guid", path="/tmp", protocol="s3" ) - assert res == "Failed to upload data file." + + assert result == False + mock_presigned.assert_called_once_with("test-guid", protocol="s3") + mock_index.assert_not_called() + + +@pytest.mark.asyncio +async def test_async_download_multiple_empty_manifest(gen3_file): + """ + Test async_download_multiple with an empty manifest. + + Verifies that calling async_download_multiple with an empty manifest + returns empty succeeded, failed, and skipped lists. + """ + result = await gen3_file.async_download_multiple(manifest_data=[]) + assert result == {"succeeded": [], "failed": [], "skipped": []} + + +@pytest.mark.asyncio +async def test_async_download_multiple_success(gen3_file, mock_manifest_data): + """ + Test successful async download of multiple files. + + Verifies that async_download_multiple correctly processes a manifest with + multiple files and returns all downloads as successful. + """ + gen3_file._auth_provider._refresh_token = {"api_key": "123"} + gen3_file._auth_provider.get_access_token = MagicMock(return_value="fake_token") + + with ( + patch("gen3.file.mp.Process"), + patch("gen3.file.mp.Queue") as mock_queue, + patch("threading.Thread"), + ): + mock_input_queue = MagicMock() + mock_output_queue = MagicMock() + mock_queue.side_effect = [mock_input_queue, mock_output_queue] + + mock_output_queue.get.side_effect = [ + [{"guid": "test-guid-1", "status": "downloaded"}], + [{"guid": "test-guid-2", "status": "downloaded"}], + [{"guid": "test-guid-3", "status": "downloaded"}], + ] + + result = await gen3_file.async_download_multiple( + manifest_data=mock_manifest_data, download_path="/tmp" + ) + + assert len(result["succeeded"]) == 3 + + +def test_get_presigned_urls_batch(gen3_file): + """ + Test batch retrieval of presigned URLs for multiple GUIDs. + + Verifies that get_presigned_urls_batch correctly calls get_presigned_url + for each GUID and returns a mapping of results. + """ + gen3_file._auth_provider._refresh_token = {"api_key": "123"} + + with patch.object(gen3_file, "get_presigned_url") as mock_get_url: + mock_get_url.return_value = {"url": "https://example.com/presigned"} + + results = gen3_file.get_presigned_urls_batch(["guid1", "guid2"]) + + assert len(results) == 2 + assert mock_get_url.call_count == 2 + + +def test_format_filename_static(): + """ + Test the static _format_filename_static method with different filename formats. + + Verifies that files can be formatted as original, guid-only, or combined + (filename_guidXXX.ext) based on the format parameter. + """ + from gen3.file import Gen3File + + assert ( + Gen3File._format_filename_static("guid123", "test.txt", "original") + == "test.txt" + ) + assert Gen3File._format_filename_static("guid123", "test.txt", "guid") == "guid123" + assert ( + Gen3File._format_filename_static("guid123", "test.txt", "combined") + == "test_guid123.txt" + ) + + +def test_handle_conflict_static(): + """ + Test the static _handle_conflict_static method for file conflict resolution. + + Verifies that existing files can be either kept or renamed with a numeric + suffix based on the rename parameter. + """ + from gen3.file import Gen3File + + with tempfile.TemporaryDirectory() as temp_dir: + temp_path = Path(temp_dir) + existing_file = temp_path / "existing.txt" + existing_file.write_text("test") + + result = Gen3File._handle_conflict_static(existing_file, rename=False) + assert result == existing_file + + result = Gen3File._handle_conflict_static(existing_file, rename=True) + assert result.name == "existing_1.txt"