Loading src/nova/galaxy/data_store.py +7 −3 Original line number Diff line number Diff line Loading @@ -3,7 +3,7 @@ from typing import TYPE_CHECKING if TYPE_CHECKING: from .nova import Nova # Only imports for type checking from .nova import NovaConnection # Only imports for type checking class Datastore: Loading @@ -12,7 +12,11 @@ class Datastore: The constructor is not intended for external use. Use nova.galaxy.Nova.create_data_store() instead. """ def __init__(self, name: str, nova_instance: "Nova", history_id: str): def __init__(self, name: str, nova_connection: "NovaConnection", history_id: str) -> None: self.name = name self.nova = nova_instance self.nova_connection = nova_connection self.history_id = history_id self.persist_store = False def persist(self) -> None: self.persist_store = True src/nova/galaxy/dataset.py +31 −7 Original line number Diff line number Diff line Loading @@ -53,7 +53,11 @@ class AbstractData(ABC): raise NotImplementedError() @abstractmethod def download(self, local_path: str) -> None: def download(self, local_path: str) -> Any: raise NotImplementedError() @abstractmethod def get_content(self) -> Any: raise NotImplementedError() def cancel_upload(self) -> None: Loading @@ -70,7 +74,7 @@ class Dataset(AbstractData): self.store: Datastore def upload(self, store: Datastore) -> None: galaxy_instance = store.nova.galaxy_instance galaxy_instance = store.nova_connection.galaxy_instance dataset_client = DatasetClient(galaxy_instance) history_id = galaxy_instance.histories.get_histories(name=store.name)[0]["id"] dataset_id = galaxy_instance.tools.upload_file(path=self.path, history_id=history_id) Loading @@ -78,11 +82,19 @@ class Dataset(AbstractData): self.store = store dataset_client.wait_for_dataset(self.id) def download(self, local_path: str) -> None: def download(self, local_path: str) -> AbstractData: """Downloads this dataset to the local path given.""" if self.store and self.id: dataset_client = DatasetClient(self.store.nova.galaxy_instance) dataset_client = DatasetClient(self.store.nova_connection.galaxy_instance) dataset_client.download_dataset(self.id, use_default_filename=False, file_path=local_path) return self else: raise Exception("Dataset is not present in Galaxy.") def get_content(self) -> Any: if self.store and self.id: dataset_client = DatasetClient(self.store.nova_connection.galaxy_instance) return dataset_client.download_dataset(self.id, use_default_filename=False, file_path=None).decode("utf-8") else: raise Exception("Dataset is not present in Galaxy.") Loading @@ -100,18 +112,30 @@ class DatasetCollection(AbstractData): """Will need to handle this differently than single datasets.""" raise NotImplementedError def download(self, local_path: str) -> None: def download(self, local_path: str) -> AbstractData: """Downloads this dataset collection to the local path given.""" if self.store and self.id: dataset_client = DatasetCollectionClient(self.store.nova.galaxy_instance) dataset_client = DatasetCollectionClient(self.store.nova_connection.galaxy_instance) dataset_client.download_dataset_collection(self.id, file_path=local_path) return self else: raise Exception("Dataset collection is not present in Galaxy.") def get_content(self) -> Any: if self.store and self.id: dataset_client = DatasetCollectionClient(self.store.nova_connection.galaxy_instance) info = dataset_client.show_dataset_collection(self.id) output = "" for element in info["elements"]: output += f"{element['element_identifier']}\n" return output else: raise Exception("Dataset collection is not present in Galaxy.") def upload_datasets(store: Datastore, datasets: Dict[str, AbstractData]) -> Dict[str, str]: """Helper method to upload multiple datasets or collections in parallel.""" galaxy_instance = store.nova.galaxy_instance galaxy_instance = store.nova_connection.galaxy_instance dataset_client = DatasetClient(galaxy_instance) history_id = galaxy_instance.histories.get_histories(name=store.name)[0]["id"] dataset_ids = {} Loading src/nova/galaxy/nova.py +39 −15 Original line number Diff line number Diff line """The NOVA class is responsible for managing interactions with a Galaxy server instance.""" from typing import Optional from contextlib import contextmanager from typing import Generator, List, Optional from bioblend import galaxy Loading @@ -20,6 +21,35 @@ class GalaxyConnectionError(Exception): super().__init__(self.message) class NovaConnection: """Manages datastore for current connection. Should not be instantiated manually. Use Nova.connect() instead. Any stores created using the connection will be automatically purged after connection is closed, unless Datastore.persist() is called for that store. """ def __init__(self, galaxy_instance: galaxy.GalaxyInstance): self.galaxy_instance = galaxy_instance self.datastores: List[Datastore] = [] def create_data_store(self, name: str) -> Datastore: """Creates a datastore with the given name.""" histories = self.galaxy_instance.histories.get_histories(name=name) if len(histories) > 0: store = Datastore(name, self, histories[0]["id"]) self.datastores.append(store) return store history_id = self.galaxy_instance.histories.create_history(name=name)["id"] store = Datastore(name, self, history_id) self.datastores.append(store) return store def remove_data_store(self, store: Datastore) -> None: """Permanently deletes the data store with the given name.""" history = self.galaxy_instance.histories.get_histories(name=store.name)[0]["id"] self.galaxy_instance.histories.delete_history(history_id=history, purge=True) class Nova: """ Class to manage a Galaxy connection. Loading @@ -46,7 +76,8 @@ class Nova: self.galaxy_api_key = galaxy_key self.galaxy_instance: galaxy.GalaxyInstance def connect(self) -> None: @contextmanager def connect(self) -> Generator: """ Connects to the Galaxy instance using the provided URL and API key. Loading @@ -62,16 +93,9 @@ class Nova: raise ValueError("Galaxy URL must be a string") self.galaxy_instance = galaxy.GalaxyInstance(url=self.galaxy_url, key=self.galaxy_api_key) self.galaxy_instance.config.get_version() def create_data_store(self, name: str) -> Datastore: """Creates a datastore with the given name.""" histories = self.galaxy_instance.histories.get_histories(name=name) if len(histories) > 0: return Datastore(name, self, histories[0]["id"]) history_id = self.galaxy_instance.histories.create_history(name=name)["id"] return Datastore(name, self, history_id) def remove_data_store(self, name: str) -> None: """Permanently deletes the data store with the given name.""" history = self.galaxy_instance.histories.get_histories(name=name)[0]["id"] self.galaxy_instance.histories.delete_history(history_id=history, purge=True) conn = NovaConnection(self.galaxy_instance) yield conn # Remove all data stores after execution for store in conn.datastores: if not store.persist_store: conn.remove_data_store(store) src/nova/galaxy/outputs.py +8 −2 Original line number Diff line number Diff line Loading @@ -28,7 +28,13 @@ class Outputs: self.data.append(data) def get_dataset(self, name: str) -> AbstractData: try: return next(filter(lambda x: isinstance(x, Dataset) and x.name == name, self.data)) except StopIteration as e: raise Exception(f"There is no dataset: {name}") from e def get_collection(self, name: str) -> AbstractData: try: return next(filter(lambda x: isinstance(x, DatasetCollection) and x.name == name, self.data)) except StopIteration as e: raise Exception(f"There is no dataset collection: {name}") from e src/nova/galaxy/tool.py +1 −1 Original line number Diff line number Diff line Loading @@ -35,7 +35,7 @@ class Tool(AbstractWork): def run(self, data_store: Datastore, params: Parameters) -> Outputs: """Runs this tool in a blocking manner and returns a map of the output datasets and collections.""" outputs = Outputs() galaxy_instance = data_store.nova.galaxy_instance galaxy_instance = data_store.nova_connection.galaxy_instance datasets_to_upload = {} # Set Tool Inputs Loading Loading
src/nova/galaxy/data_store.py +7 −3 Original line number Diff line number Diff line Loading @@ -3,7 +3,7 @@ from typing import TYPE_CHECKING if TYPE_CHECKING: from .nova import Nova # Only imports for type checking from .nova import NovaConnection # Only imports for type checking class Datastore: Loading @@ -12,7 +12,11 @@ class Datastore: The constructor is not intended for external use. Use nova.galaxy.Nova.create_data_store() instead. """ def __init__(self, name: str, nova_instance: "Nova", history_id: str): def __init__(self, name: str, nova_connection: "NovaConnection", history_id: str) -> None: self.name = name self.nova = nova_instance self.nova_connection = nova_connection self.history_id = history_id self.persist_store = False def persist(self) -> None: self.persist_store = True
src/nova/galaxy/dataset.py +31 −7 Original line number Diff line number Diff line Loading @@ -53,7 +53,11 @@ class AbstractData(ABC): raise NotImplementedError() @abstractmethod def download(self, local_path: str) -> None: def download(self, local_path: str) -> Any: raise NotImplementedError() @abstractmethod def get_content(self) -> Any: raise NotImplementedError() def cancel_upload(self) -> None: Loading @@ -70,7 +74,7 @@ class Dataset(AbstractData): self.store: Datastore def upload(self, store: Datastore) -> None: galaxy_instance = store.nova.galaxy_instance galaxy_instance = store.nova_connection.galaxy_instance dataset_client = DatasetClient(galaxy_instance) history_id = galaxy_instance.histories.get_histories(name=store.name)[0]["id"] dataset_id = galaxy_instance.tools.upload_file(path=self.path, history_id=history_id) Loading @@ -78,11 +82,19 @@ class Dataset(AbstractData): self.store = store dataset_client.wait_for_dataset(self.id) def download(self, local_path: str) -> None: def download(self, local_path: str) -> AbstractData: """Downloads this dataset to the local path given.""" if self.store and self.id: dataset_client = DatasetClient(self.store.nova.galaxy_instance) dataset_client = DatasetClient(self.store.nova_connection.galaxy_instance) dataset_client.download_dataset(self.id, use_default_filename=False, file_path=local_path) return self else: raise Exception("Dataset is not present in Galaxy.") def get_content(self) -> Any: if self.store and self.id: dataset_client = DatasetClient(self.store.nova_connection.galaxy_instance) return dataset_client.download_dataset(self.id, use_default_filename=False, file_path=None).decode("utf-8") else: raise Exception("Dataset is not present in Galaxy.") Loading @@ -100,18 +112,30 @@ class DatasetCollection(AbstractData): """Will need to handle this differently than single datasets.""" raise NotImplementedError def download(self, local_path: str) -> None: def download(self, local_path: str) -> AbstractData: """Downloads this dataset collection to the local path given.""" if self.store and self.id: dataset_client = DatasetCollectionClient(self.store.nova.galaxy_instance) dataset_client = DatasetCollectionClient(self.store.nova_connection.galaxy_instance) dataset_client.download_dataset_collection(self.id, file_path=local_path) return self else: raise Exception("Dataset collection is not present in Galaxy.") def get_content(self) -> Any: if self.store and self.id: dataset_client = DatasetCollectionClient(self.store.nova_connection.galaxy_instance) info = dataset_client.show_dataset_collection(self.id) output = "" for element in info["elements"]: output += f"{element['element_identifier']}\n" return output else: raise Exception("Dataset collection is not present in Galaxy.") def upload_datasets(store: Datastore, datasets: Dict[str, AbstractData]) -> Dict[str, str]: """Helper method to upload multiple datasets or collections in parallel.""" galaxy_instance = store.nova.galaxy_instance galaxy_instance = store.nova_connection.galaxy_instance dataset_client = DatasetClient(galaxy_instance) history_id = galaxy_instance.histories.get_histories(name=store.name)[0]["id"] dataset_ids = {} Loading
src/nova/galaxy/nova.py +39 −15 Original line number Diff line number Diff line """The NOVA class is responsible for managing interactions with a Galaxy server instance.""" from typing import Optional from contextlib import contextmanager from typing import Generator, List, Optional from bioblend import galaxy Loading @@ -20,6 +21,35 @@ class GalaxyConnectionError(Exception): super().__init__(self.message) class NovaConnection: """Manages datastore for current connection. Should not be instantiated manually. Use Nova.connect() instead. Any stores created using the connection will be automatically purged after connection is closed, unless Datastore.persist() is called for that store. """ def __init__(self, galaxy_instance: galaxy.GalaxyInstance): self.galaxy_instance = galaxy_instance self.datastores: List[Datastore] = [] def create_data_store(self, name: str) -> Datastore: """Creates a datastore with the given name.""" histories = self.galaxy_instance.histories.get_histories(name=name) if len(histories) > 0: store = Datastore(name, self, histories[0]["id"]) self.datastores.append(store) return store history_id = self.galaxy_instance.histories.create_history(name=name)["id"] store = Datastore(name, self, history_id) self.datastores.append(store) return store def remove_data_store(self, store: Datastore) -> None: """Permanently deletes the data store with the given name.""" history = self.galaxy_instance.histories.get_histories(name=store.name)[0]["id"] self.galaxy_instance.histories.delete_history(history_id=history, purge=True) class Nova: """ Class to manage a Galaxy connection. Loading @@ -46,7 +76,8 @@ class Nova: self.galaxy_api_key = galaxy_key self.galaxy_instance: galaxy.GalaxyInstance def connect(self) -> None: @contextmanager def connect(self) -> Generator: """ Connects to the Galaxy instance using the provided URL and API key. Loading @@ -62,16 +93,9 @@ class Nova: raise ValueError("Galaxy URL must be a string") self.galaxy_instance = galaxy.GalaxyInstance(url=self.galaxy_url, key=self.galaxy_api_key) self.galaxy_instance.config.get_version() def create_data_store(self, name: str) -> Datastore: """Creates a datastore with the given name.""" histories = self.galaxy_instance.histories.get_histories(name=name) if len(histories) > 0: return Datastore(name, self, histories[0]["id"]) history_id = self.galaxy_instance.histories.create_history(name=name)["id"] return Datastore(name, self, history_id) def remove_data_store(self, name: str) -> None: """Permanently deletes the data store with the given name.""" history = self.galaxy_instance.histories.get_histories(name=name)[0]["id"] self.galaxy_instance.histories.delete_history(history_id=history, purge=True) conn = NovaConnection(self.galaxy_instance) yield conn # Remove all data stores after execution for store in conn.datastores: if not store.persist_store: conn.remove_data_store(store)
src/nova/galaxy/outputs.py +8 −2 Original line number Diff line number Diff line Loading @@ -28,7 +28,13 @@ class Outputs: self.data.append(data) def get_dataset(self, name: str) -> AbstractData: try: return next(filter(lambda x: isinstance(x, Dataset) and x.name == name, self.data)) except StopIteration as e: raise Exception(f"There is no dataset: {name}") from e def get_collection(self, name: str) -> AbstractData: try: return next(filter(lambda x: isinstance(x, DatasetCollection) and x.name == name, self.data)) except StopIteration as e: raise Exception(f"There is no dataset collection: {name}") from e
src/nova/galaxy/tool.py +1 −1 Original line number Diff line number Diff line Loading @@ -35,7 +35,7 @@ class Tool(AbstractWork): def run(self, data_store: Datastore, params: Parameters) -> Outputs: """Runs this tool in a blocking manner and returns a map of the output datasets and collections.""" outputs = Outputs() galaxy_instance = data_store.nova.galaxy_instance galaxy_instance = data_store.nova_connection.galaxy_instance datasets_to_upload = {} # Set Tool Inputs Loading