Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ with DataConnectClient.connect(

studies = client.get_studies(search_study_name="ACME")

datasets = client.get_datasets(study_environment_uuid=UUID("cec9f2a7-07ba-4fa8-bfcf-34fbc5d56793"))
pagination = client.get_datasets(study_environment_uuid=UUID("cec9f2a7-07ba-4fa8-bfcf-34fbc5d56793"))
datasets = pagination.items
Comment thread
butsyk-mdsol marked this conversation as resolved.

```
## Development
Expand Down
4 changes: 3 additions & 1 deletion dataconnect/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
ServerError,
ValidationError,
)
from dataconnect.models import DatasetVersion, Study, StudyEnvironment
from dataconnect.models import DatasetVersion, PaginatedResponse, Pagination, Study, StudyEnvironment

__all__ = [
# Client
Expand All @@ -20,6 +20,8 @@
"Study",
"StudyEnvironment",
"DatasetVersion",
"PaginatedResponse",
"Pagination",
# Exceptions — catch these in user application code
"DataConnectError",
"AuthenticationError",
Expand Down
6 changes: 3 additions & 3 deletions dataconnect/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

import pandas as pd

from dataconnect.models import Dataset, DatasetVersion, Study
from dataconnect.models import Dataset, DatasetVersion, PaginatedResponse, Study
from dataconnect.service import DataConnectService, DefaultDataConnectService

_DEFAULT_HOST = "enodia-gateway.platform.imedidata.com"
Expand Down Expand Up @@ -67,7 +67,7 @@ def get_datasets(
search_dataset_name: str = "",
page: int = 1,
page_size: int = 50,
) -> list[Dataset]:
) -> PaginatedResponse[Dataset]:
"""List datasets for a study environment.

Args:
Expand All @@ -77,7 +77,7 @@ def get_datasets(
page_size: Number of results per page.

Returns:
A list of :class:`Dataset` items matching the criteria.
A :class:`PaginatedResponse` of :class:`Dataset` items matching the criteria.
"""
return self._service.get_datasets(
study_environment_uuid=study_environment_uuid,
Expand Down
4 changes: 2 additions & 2 deletions dataconnect/service/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import pandas as pd

from dataconnect.models import Dataset, DatasetVersion, Study
from dataconnect.models import Dataset, DatasetVersion, PaginatedResponse, Study


class DataConnectService(ABC):
Expand All @@ -23,7 +23,7 @@ def get_datasets(
search_dataset_name: str = "",
page: int = 1,
page_size: int = 50,
) -> list[Dataset]: ...
) -> PaginatedResponse[Dataset]: ...

@abstractmethod
def get_dataset_versions(self, dataset_uuid: UUID) -> list[DatasetVersion]: ...
Expand Down
49 changes: 16 additions & 33 deletions dataconnect/service/default.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,11 @@

from __future__ import annotations

from datetime import UTC, datetime
from uuid import UUID

import pandas as pd

from dataconnect.exceptions import ValidationError
from dataconnect.models import Dataset, DatasetVersion, Study
from dataconnect.models import Dataset, DatasetVersion, PaginatedResponse, Pagination, Study
from dataconnect.service.base import DataConnectService
from dataconnect.service.error_handler import translate_error
from dataconnect.service.mappers import (
Expand All @@ -17,6 +15,7 @@
resource_to_fetched_data,
resource_to_study,
)
from dataconnect.service.validators import validate_positive_int, validate_uuid
from dataconnect.transport.base import Transport
from dataconnect.transport.errors import TransportError
from dataconnect.transport.models import ResourceQuery
Expand Down Expand Up @@ -69,20 +68,7 @@ def get_dataset_versions(self, dataset_uuid: UUID) -> list[DatasetVersion]:
Raises:
ValidationError: If *dataset_uuid* is not a valid, non-zero UUID.
"""
# Input validation: ensure callers pass a UUID
if not isinstance(dataset_uuid, UUID):
raise ValidationError(
error_code="VAL_C_DATASET_UUID",
message="dataset_uuid must be a valid UUID",
timestamp=datetime.now(UTC).strftime("%Y-%m-%dT%H:%M:%SZ"),
)

if dataset_uuid.int == 0:
raise ValidationError(
error_code="VAL_C_DATASET_UUID",
message="dataset_uuid must not be empty",
timestamp=datetime.now(UTC).strftime("%Y-%m-%dT%H:%M:%SZ"),
)
validate_uuid(dataset_uuid, field_name="dataset_uuid", error_code="VAL_C_DATASET_UUID")

request = ResourceQuery(action=_ACTION_LIST_DATASET_VERSIONS).append_body({"dataset_uuid": str(dataset_uuid)})

Expand Down Expand Up @@ -124,7 +110,7 @@ def get_datasets(
search_dataset_name: str = "",
page: int = 1,
page_size: int = 50,
) -> list[Dataset]:
) -> PaginatedResponse[Dataset]:
"""List datasets for a study environment.

Args:
Expand All @@ -134,21 +120,11 @@ def get_datasets(
page_size: Number of results per page.

Returns:
A list of :class:`Dataset` items matching the criteria.
A :class:`PaginatedResponse` of :class:`Dataset` items matching the criteria.
"""
if not isinstance(study_environment_uuid, UUID):
raise ValidationError(
error_code="VAL_C_STUDY_ENV_UUID",
message="study_environment_uuid must be a valid UUID.",
timestamp=datetime.now(UTC).strftime("%Y-%m-%dT%H:%M:%SZ"),
)

if study_environment_uuid.int == 0:
raise ValidationError(
error_code="VAL_C_STUDY_ENV_UUID",
message="study_environment_uuid must not be empty.",
timestamp=datetime.now(UTC).strftime("%Y-%m-%dT%H:%M:%SZ"),
)
validate_uuid(study_environment_uuid, field_name="study_environment_uuid", error_code="VAL_C_STUDY_ENV_UUID")
validate_positive_int(page, field_name="page", error_code="VAL_C_PAGE")
validate_positive_int(page_size, field_name="page_size", error_code="VAL_C_PAGE_SIZE")

request = ResourceQuery(action=_ACTION_LIST_DATASETS).append_body(
{
Expand All @@ -161,7 +137,14 @@ def get_datasets(

try:
resources = self._transport.list_resources(request)
return [resource_to_dataset(r) for r in resources]
items = [resource_to_dataset(r) for r in resources]
total_records = resources[0].total_records if resources else 0
Comment thread
butsyk-mdsol marked this conversation as resolved.
total_pages = (total_records + page_size - 1) // page_size if page_size > 0 else 0
return PaginatedResponse(
total_records=total_records,
pagination=Pagination(page=page, page_size=page_size, total_pages=total_pages),
items=items,
Comment thread
butsyk-mdsol marked this conversation as resolved.
)
except TransportError as ex:
raise translate_error(ex) from ex

Expand Down
38 changes: 38 additions & 0 deletions dataconnect/service/validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

from __future__ import annotations

from datetime import UTC, datetime
from uuid import UUID

from dataconnect.exceptions import ValidationError


Expand All @@ -13,3 +16,38 @@ def validate_search_study_name(search_study_name: str | None) -> None:

if not isinstance(search_study_name, str):
raise ValidationError("search_study_name must be a string")


def validate_uuid(value: object, *, field_name: str, error_code: str) -> None:
"""Ensure *value* is a non-zero UUID.

Raises:
ValidationError: If *value* is not a UUID instance or is the nil UUID.
"""
if not isinstance(value, UUID):
raise ValidationError(
error_code=error_code,
message=f"{field_name} must be a valid UUID.",
timestamp=datetime.now(UTC).strftime("%Y-%m-%dT%H:%M:%SZ"),
)

if value.int == 0:
raise ValidationError(
error_code=error_code,
message=f"{field_name} must not be empty.",
timestamp=datetime.now(UTC).strftime("%Y-%m-%dT%H:%M:%SZ"),
)


def validate_positive_int(value: object, *, field_name: str, error_code: str) -> None:
"""Ensure *value* is an integer >= 1.

Raises:
ValidationError: If *value* is not an int or is less than 1.
"""
if not isinstance(value, int) or value < 1:
raise ValidationError(
error_code=error_code,
message=f"{field_name} must be a positive integer.",
timestamp=datetime.now(UTC).strftime("%Y-%m-%dT%H:%M:%SZ"),
)
Loading
Loading