Warning
This document is for an in-development version of Galaxy. You can alternatively view this page in the latest release if it exists or view the top of the latest release's documentation.
Source code for galaxy.webapps.galaxy.services.dataset_collections
from logging import getLogger
from typing import (
List,
Optional,
Set,
TYPE_CHECKING,
Union,
)
from pydantic import (
Extra,
Field,
ValidationError,
)
from typing_extensions import Literal
from galaxy import exceptions
from galaxy.datatypes.registry import Registry
from galaxy.managers.collections import DatasetCollectionManager
from galaxy.managers.collections_util import (
api_payload_to_create_params,
dictify_dataset_collection_instance,
dictify_element_reference,
)
from galaxy.managers.context import ProvidesHistoryContext
from galaxy.managers.hdcas import HDCAManager
from galaxy.managers.histories import HistoryManager
from galaxy.model import DatasetCollectionElement
from galaxy.schema.fields import (
DecodedDatabaseIdField,
ModelClassField,
)
from galaxy.schema.schema import (
CreateNewCollectionPayload,
DatasetCollectionInstanceType,
DCESummary,
DCEType,
HDCADetailed,
Model,
TagCollection,
)
from galaxy.security.idencoding import IdEncodingHelper
from galaxy.webapps.base.controller import UsesLibraryMixinItems
from galaxy.webapps.galaxy.services.base import ServiceBase
if TYPE_CHECKING:
from galaxy.model import (
HistoryDatasetCollectionAssociation,
LibraryDatasetCollectionAssociation,
)
log = getLogger(__name__)
[docs]class UpdateCollectionAttributePayload(Model):
"""Contains attributes that can be updated for all elements in a dataset collection."""
dbkey: str = Field(..., description="TODO")
[docs] class Config:
extra = Extra.forbid # will cause validation to fail if extra attributes are included,
[docs]class DatasetCollectionAttributesResult(Model):
dbkey: str = Field(..., description="TODO")
# Are the following fields really used/needed?
extension: str = Field(..., description="The dataset file extension.", example="txt")
model_class: Literal["HistoryDatasetCollectionAssociation"] = ModelClassField("HistoryDatasetCollectionAssociation")
dbkeys: Optional[Set[str]]
extensions: Optional[Set[str]]
tags: TagCollection
[docs]class SuitableConverter(Model):
tool_id: str = Field(..., description="The ID of the tool that can perform the type conversion.")
name: str = Field(..., description="The name of the converter.")
target_type: str = Field(..., description="The type to convert to.")
original_type: str = Field(..., description="The type to convert from.")
[docs]class SuitableConverters(Model):
"""Collection of converters that can be used on a particular dataset collection."""
__root__: List[SuitableConverter]
[docs]class DatasetCollectionContentElements(Model):
"""Represents a collection of elements contained in the dataset collection."""
__root__: List[DCESummary]
[docs]class DatasetCollectionsService(ServiceBase, UsesLibraryMixinItems):
[docs] def __init__(
self,
security: IdEncodingHelper,
history_manager: HistoryManager,
hdca_manager: HDCAManager,
collection_manager: DatasetCollectionManager,
datatypes_registry: Registry,
):
super().__init__(security)
self.history_manager = history_manager
self.hdca_manager = hdca_manager
self.collection_manager = collection_manager
self.datatypes_registry = datatypes_registry
[docs] def create(self, trans: ProvidesHistoryContext, payload: CreateNewCollectionPayload) -> HDCADetailed:
"""
Create a new dataset collection instance.
:type payload: dict
:param payload: (optional) dictionary structure containing:
* collection_type: dataset collection type to create.
* instance_type: Instance type - 'history' or 'library'.
* name: the new dataset collections's name
* datasets: object describing datasets for collection
:rtype: dict
:returns: element view of new dataset collection
"""
# TODO: Error handling...
create_params = api_payload_to_create_params(payload.dict(exclude_unset=True))
if payload.instance_type == "history":
if payload.history_id is None:
raise exceptions.RequestParameterInvalidException("Parameter history_id is required.")
history = self.history_manager.get_mutable(payload.history_id, trans.user, current_history=trans.history)
create_params["parent"] = history
create_params["history"] = history
elif payload.instance_type == "library" and payload.folder_id:
library_folder = self.get_library_folder(trans, payload.folder_id, check_accessible=True)
self.check_user_can_add_to_library_item(trans, library_folder, check_accessible=False)
create_params["parent"] = library_folder
else:
raise exceptions.RequestParameterInvalidException()
dataset_collection_instance = self.collection_manager.create(trans=trans, **create_params)
rval = dictify_dataset_collection_instance(
dataset_collection_instance,
security=trans.security,
url_builder=trans.url_builder,
parent=create_params["parent"],
)
return rval
[docs] def copy(
self, trans: ProvidesHistoryContext, id: DecodedDatabaseIdField, payload: UpdateCollectionAttributePayload
):
"""
Iterate over all datasets of a collection and copy datasets with new attributes to a new collection.
e.g attributes = {'dbkey': 'dm3'}
"""
self.collection_manager.copy(
trans, trans.history, "hdca", id, copy_elements=True, dataset_instance_attributes=payload.dict()
)
[docs] def attributes(
self,
trans: ProvidesHistoryContext,
id: DecodedDatabaseIdField,
instance_type: DatasetCollectionInstanceType = "history",
) -> DatasetCollectionAttributesResult:
"""
Returns dbkey/extension for collection elements
"""
dataset_collection_instance = self.collection_manager.get_dataset_collection_instance(
trans, instance_type, id, check_ownership=True
)
rval = dataset_collection_instance.to_dict(view="dbkeysandextensions")
return rval
[docs] def suitable_converters(
self,
trans: ProvidesHistoryContext,
id: DecodedDatabaseIdField,
instance_type: DatasetCollectionInstanceType = "history",
) -> SuitableConverters:
"""
Returns suitable converters for all datatypes in collection
"""
rval = self.collection_manager.get_converters_for_collection(trans, id, self.datatypes_registry, instance_type)
return rval
[docs] def show(
self,
trans: ProvidesHistoryContext,
id: DecodedDatabaseIdField,
instance_type: DatasetCollectionInstanceType = "history",
) -> HDCADetailed:
"""
Returns information about a particular dataset collection.
"""
dataset_collection_instance: Union[HistoryDatasetCollectionAssociation, LibraryDatasetCollectionAssociation]
if instance_type == "history":
dataset_collection_instance = self.collection_manager.get_dataset_collection_instance(trans, "history", id)
parent = dataset_collection_instance.history
elif instance_type == "library":
dataset_collection_instance = self.collection_manager.get_dataset_collection_instance(trans, "library", id)
parent = dataset_collection_instance.folder
else:
raise exceptions.RequestParameterInvalidException()
rval = dictify_dataset_collection_instance(
dataset_collection_instance,
security=trans.security,
url_builder=trans.url_builder,
parent=parent,
view="element",
)
return rval
[docs] def dce_content(self, trans: ProvidesHistoryContext, dce_id: DecodedDatabaseIdField) -> DCESummary:
dce: Optional[DatasetCollectionElement] = trans.model.session.get(DatasetCollectionElement, dce_id)
if not dce:
raise exceptions.ObjectNotFound("No DatasetCollectionElement found")
if not trans.user_is_admin:
collection = dce.child_collection or dce.collection
if not trans.app.security_agent.can_access_collection(trans.get_current_user_roles(), collection):
raise exceptions.ItemAccessibilityException("Collection not accessible by user.")
serialized_dce = dictify_element_reference(dce, recursive=False, security=trans.security)
return trans.security.encode_all_ids(serialized_dce, recursive=True)
[docs] def contents(
self,
trans: ProvidesHistoryContext,
hdca_id: DecodedDatabaseIdField,
parent_id: DecodedDatabaseIdField,
instance_type: DatasetCollectionInstanceType = "history",
limit: Optional[int] = None,
offset: Optional[int] = None,
) -> DatasetCollectionContentElements:
"""
Shows direct child contents of indicated dataset collection parent id
:type string: encoded string id
:param id: HDCA.id
:type string: encoded string id
:param parent_id: parent dataset_collection.id for the dataset contents to be viewed
:type integer: int
:param limit: pagination limit for returned dataset collection elements
:type integer: int
:param offset: pagination offset for returned dataset collection elements
:rtype: list
:returns: list of dataset collection elements and contents
"""
# validate HDCA for current user, will throw error if not permitted
# TODO: refactor get_dataset_collection_instance
if instance_type != "history":
raise exceptions.RequestParameterInvalidException(
"Parameter instance_type not being 'history' is not yet implemented."
)
hdca: HistoryDatasetCollectionAssociation = self.collection_manager.get_dataset_collection_instance(
trans, "history", hdca_id, check_ownership=True
)
# check to make sure the dsc is part of the validated hdca
if not hdca.contains_collection(parent_id):
raise exceptions.ObjectNotFound(
"Requested dataset collection is not contained within indicated history content"
)
# retrieve contents
contents = self.collection_manager.get_collection_contents(trans, parent_id, limit=limit, offset=offset)
# dictify and tack on a collection_url for drilling down into nested collections
def serialize_element(dsc_element) -> DCESummary:
result = dictify_element_reference(dsc_element, recursive=False, security=trans.security)
if result["element_type"] == DCEType.dataset_collection:
assert trans.url_builder
result["object"]["contents_url"] = trans.url_builder(
"contents_dataset_collection",
hdca_id=self.encode_id(hdca.id),
parent_id=self.encode_id(result["object"]["id"]),
)
trans.security.encode_all_ids(result, recursive=True)
return result
rval = [serialize_element(el) for el in contents]
try:
return DatasetCollectionContentElements.construct(__root__=rval)
except ValidationError:
log.exception(
f"Serializing DatasetCollectionContentsElements failed. Collection is populated: {hdca.collection.populated}"
)
raise