Skip to content
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ See the following set of Jupyter notebooks that contain examples on the followin

## Advanced Usage

View the API documentation for this library [here](https://cirrobio.github.io/Cirro-client/).

### Supported environment variables

| Name | Description | Default |
Expand Down
14 changes: 12 additions & 2 deletions cirro/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
from cirro.cirro_client import Cirro
import cirro.file_utils # noqa
from cirro.cirro_client import CirroAPI
from cirro.sdk.dataset import DataPortalDataset
from cirro.sdk.portal import DataPortal
from cirro.sdk.process import DataPortalProcess
from cirro.sdk.project import DataPortalProject
from cirro.sdk.reference import DataPortalReference

__all__ = [
'DataPortal',
'Cirro'
'DataPortalProject',
'DataPortalProcess',
'DataPortalDataset',
'DataPortalReference',
'CirroAPI',
'file_utils'
]
64 changes: 51 additions & 13 deletions cirro/cirro_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,33 @@
from cirro.auth import get_auth_info_from_config
from cirro.auth.base import AuthInfo
from cirro.config import AppConfig
from cirro.services.billing import BillingService
from cirro.services.dataset import DatasetService
from cirro.services.execution import ExecutionService
from cirro.services.file import FileService
from cirro.services.metadata import MetadataService
from cirro.services.metrics import MetricsService
from cirro.services.process import ProcessService
from cirro.services.projects import ProjectService
from cirro.services.references import ReferenceService


class Cirro:
from cirro.services import FileService, DatasetService, ProjectService, ProcessService, ExecutionService, \
MetricsService, MetadataService, BillingService, ReferenceService


class CirroAPI:
"""
Client for interacting with Cirro
Client for interacting directly with the Cirro API
"""
def __init__(self, auth_info: AuthInfo = None, base_url: str = None):
"""
Instantiates the Cirro API object

Args:
auth_info (cirro.auth.base.AuthInfo):
base_url (str): Optional base URL for connection (default: `CIRRO_HOME` or 'cirro.bio')

Returns:
Authenticated Cirro API object which can be used to call endpoint functions.
For example:

```python
from cirro.cirro_client import CirroAPI
cirro = CirroAPI()
print(cirro.projects.list())
```
"""

self._configuration = AppConfig(base_url=base_url)
if not auth_info:
auth_info = get_auth_info_from_config(self._configuration, auth_io=None)
Expand All @@ -45,38 +56,65 @@ def __init__(self, auth_info: AuthInfo = None, base_url: str = None):

@property
def datasets(self) -> DatasetService:
"""
Create, list, delete, and modify Datasets
"""
return self._dataset_service

@property
def projects(self) -> ProjectService:
"""
Create, list, delete, and modify Projects
"""
return self._project_service

@property
def processes(self) -> ProcessService:
"""
List and retrieve detailed information about Processes
"""
return self._process_service

@property
def execution(self) -> ExecutionService:
"""
List, run, stop, and describe the analysis jobs (executing Processes to create new Datasets)
"""
return self._execution_service

@property
def metrics(self) -> MetricsService:
"""
Project-level summary metrics
"""
return self._metrics_service

@property
def metadata(self) -> MetadataService:
"""
List and modify Sample metadata or metadata schemas
"""
return self._metadata_service

@property
def billing(self) -> BillingService:
"""
List and update billing accounts
"""
return self._billing_service

@property
def references(self) -> ReferenceService:
"""
List References and Reference types
"""
return self._references_service

@property
def file(self) -> FileService:
"""
Read, download, and create file objects
"""
return self._file_service

@property
Expand Down
8 changes: 4 additions & 4 deletions cirro/cli/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import pandas as pd
from cirro_api_client.v1.models import UploadDatasetRequest, Status

from cirro.cirro_client import Cirro
from cirro.cirro_client import CirroAPI
from cirro.cli.interactive.auth_args import gather_auth_config
from cirro.cli.interactive.download_args import gather_download_arguments, ask_dataset_files
from cirro.cli.interactive.download_args import gather_download_arguments_dataset
Expand All @@ -19,7 +19,7 @@
def run_list_datasets(input_params: ListArguments, interactive=False):
"""List the datasets available in a particular project."""
_check_configure()
cirro = Cirro()
cirro = CirroAPI()
logger = _get_logger()
logger.info(f"Collecting data from {cirro._configuration.base_url}")

Expand Down Expand Up @@ -47,7 +47,7 @@ def run_list_datasets(input_params: ListArguments, interactive=False):

def run_ingest(input_params: UploadArguments, interactive=False):
_check_configure()
cirro = Cirro()
cirro = CirroAPI()
logger = _get_logger()
logger.info(f"Collecting data from {cirro._configuration.base_url}")
processes = cirro.processes.list()
Expand Down Expand Up @@ -100,7 +100,7 @@ def run_ingest(input_params: UploadArguments, interactive=False):

def run_download(input_params: DownloadArguments, interactive=False):
_check_configure()
cirro = Cirro()
cirro = CirroAPI()
logger = _get_logger()
logger.info(f"Collecting data from {cirro._configuration.base_url}")

Expand Down
40 changes: 24 additions & 16 deletions cirro/file_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,17 @@
import win32api
import win32con

DEFAULT_TRANSFER_SPEED = 160


def filter_files_by_pattern(files: Union[List[File], List[str]], pattern: str) -> List[File]:
def filter_files_by_pattern(files: Union[List[File], List[str]], pattern: str) -> Union[List[File], List[str]]:
"""
Filters a list of files by a glob pattern

Args:
files (Union[List[File], List[str]]): List of Files or file paths
pattern (str): Glob pattern (i.e., *.fastq)

Returns:
The filtered list of files
"""
def matches_glob(file: Union[File, str]):
return PurePath(file if isinstance(file, str) else file.relative_path).match(pattern)
Expand All @@ -40,15 +45,19 @@ def _is_hidden_file(file_path: Path):


def get_files_in_directory(
directory,
directory: Union[str, Path],
include_hidden=False
) -> List[str]:
"""
Returns a list of strings containing the relative path of
each file within the indicated directory.

include_hidden: bool
Include hidden files in the returned list
Args:
directory (Union[str, Path]): The path to the directory
include_hidden (bool): include hidden files in the returned list

Returns:
List of files in the directory
"""
path = Path(directory).expanduser()
path_posix = str(path.as_posix())
Expand All @@ -74,6 +83,9 @@ def get_files_in_directory(


def get_files_stats(files: List[Path]) -> DirectoryStatistics:
"""
@private
"""
sizes = [f.stat().st_size for f in files]
total_size = sum(sizes) / float(1 << 30)
return {
Expand All @@ -84,6 +96,9 @@ def get_files_stats(files: List[Path]) -> DirectoryStatistics:


def upload_directory(directory: str, files: List[str], s3_client: S3Client, bucket: str, prefix: str, max_retries=10):
"""
@private
"""
for file in files:
key = f'{prefix}/{file}'
local_path = Path(directory, file)
Expand Down Expand Up @@ -115,6 +130,9 @@ def upload_directory(directory: str, files: List[str], s3_client: S3Client, buck


def download_directory(directory: str, files: List[str], s3_client: S3Client, bucket: str, prefix: str):
"""
@private
"""
for file in files:
key = f'{prefix}/{file}'.lstrip('/')
local_path = Path(directory, file)
Expand All @@ -123,13 +141,3 @@ def download_directory(directory: str, files: List[str], s3_client: S3Client, bu
s3_client.download_file(local_path=local_path,
bucket=bucket,
key=key)


def estimate_token_lifetime(data_size_gb: float, speed_mbps: float = DEFAULT_TRANSFER_SPEED) -> int:
"""
:param data_size_gb: Gigabytes
:param speed_mbps: Megabits per second
"""
transfer_time_seconds = (data_size_gb * 8 * 1000) / speed_mbps
transfer_time_hours = transfer_time_seconds / 60 / 60
return max(round(transfer_time_hours), 1)
10 changes: 6 additions & 4 deletions cirro/models/file.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ class DirectoryStatistics(TypedDict):

class FileAccessContext:
"""
Context holder for accessing various files in Cirro and abstracting out their location
Prefer to use the class methods to instantiate
Context holder for accessing various files in Cirro and abstracting out their location.
Prefer to use the class methods to instantiate.
"""
def __init__(self,
file_access_request: FileAccessRequest,
Expand Down Expand Up @@ -72,11 +72,13 @@ def upload_sample_sheet(cls, project_id: str, dataset_id: str, base_url: str):
)

@property
def bucket(self):
def bucket(self) -> str:
""" S3 Bucket """
return self._s3_path.bucket

@property
def prefix(self):
def prefix(self) -> str:
""" S3 Prefix """
return self._s3_path.key


Expand Down
3 changes: 2 additions & 1 deletion cirro/sdk/asset.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class DataPortalAsset:
@property
@abstractmethod
def name(self):
"""Asset name"""
pass

def __repr__(self):
Expand All @@ -22,7 +23,7 @@ def __repr__(self):

class DataPortalAssets(List[T]):
"""
Generic class with helper functions for any group of assets (projects, datasets, etc.) in the Data Portal.
Generic class with helper functions for any group of assets (projects, datasets, etc.)
"""

# Overridden by child classes
Expand Down
Loading