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
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ with DataConnectClient.connect(
token="your-bearer-token",
) as client:

studies = client.get_studies(search_study_name="ACME")
result = client.get_studies(search_study_name="ACME")
print(result.total) # total number of studies accessible to the user
print(result.studies) # list of Study objects

pagination = client.get_datasets(study_environment_uuid=UUID("cec9f2a7-07ba-4fa8-bfcf-34fbc5d56793"))
datasets = pagination.items
Expand Down
4 changes: 2 additions & 2 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, PaginatedResponse, Study
from dataconnect.models import Dataset, DatasetVersion, PaginatedResponse, StudiesResult
from dataconnect.service import DataConnectService, DefaultDataConnectService

_DEFAULT_HOST = "enodia-gateway.platform.imedidata.com"
Expand Down Expand Up @@ -45,7 +45,7 @@ def connect(

# Public API

def get_studies(self, search_study_name: str | None = None) -> list[Study]:
def get_studies(self, search_study_name: str | None = None) -> StudiesResult:
"""List the studies the client is authorized to access."""
return self._service.get_studies(search_study_name=search_study_name)

Expand Down
6 changes: 6 additions & 0 deletions dataconnect/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ class Study:
environments: list[StudyEnvironment] = field(default_factory=list)


@dataclass(frozen=True)
class StudiesResult:
total: int
Comment thread
afieraru-mdsol marked this conversation as resolved.
studies: list[Study]


@dataclass(frozen=True)
class DatasetVersion:
study_uuid: 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,14 +7,14 @@

import pandas as pd

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


class DataConnectService(ABC):
"""Abstract service interface — defines all operations available to the client."""

@abstractmethod
def get_studies(self, search_study_name: str | None = None) -> list[Study]: ...
def get_studies(self, search_study_name: str | None = None) -> StudiesResult: ...

@abstractmethod
def get_datasets(
Expand Down
13 changes: 8 additions & 5 deletions dataconnect/service/default.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import pandas as pd

from dataconnect.exceptions import ErrorDetail
from dataconnect.models import Dataset, DatasetVersion, PaginatedResponse, Pagination, Study
from dataconnect.models import Dataset, DatasetVersion, PaginatedResponse, Pagination, StudiesResult
from dataconnect.service.base import DataConnectService
from dataconnect.service.error_handler import translate_error
from dataconnect.service.mappers import (
Expand Down Expand Up @@ -35,24 +35,27 @@ def __init__(self, transport: Transport) -> None:

# DataConnectService

def get_studies(self, search_study_name: str | None = None) -> list[Study]:
def get_studies(self, search_study_name: str | None = None) -> StudiesResult:
"""List studies the authenticated user can access.

Args:
Comment thread
afieraru-mdsol marked this conversation as resolved.
search_study_name: Optional full or partial study name filter.

Returns:
A list of :class:`Study` objects matching the criteria.
A :class:`StudiesResult` containing:
- ``total``: total number of studies accessible to the authenticated user.
- ``studies``: list of :class:`Study` objects matching the criteria.
"""
# validate_search_study_name(search_study_name)

request = ResourceQuery(action=_ACTION_LIST_STUDIES)
if search_study_name and search_study_name.strip() != "":
request = request.append_body({"search_study_name": search_study_name})

try:
resources = self._transport.list_resources(request)
return [resource_to_study(r) for r in resources]
total = resources[0].total_records if resources else 0
studies = [resource_to_study(r) for r in resources]
return StudiesResult(total=total, studies=studies)
Comment thread
afieraru-mdsol marked this conversation as resolved.
Comment thread
afieraru-mdsol marked this conversation as resolved.
except Exception as ex:
raise translate_error(ex) from ex

Expand Down
26 changes: 15 additions & 11 deletions tests/test_client.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
# from __future__ import annotations

# import sys
# from types import ModuleType
# # import sys
# # from types import ModuleType
# from uuid import UUID

# import pandas as pd
# import pytest
# # import pandas as pd
# # import pytest

# from dataconnect.client import DataConnectClient
# from dataconnect.models import Dataset, DatasetVersion, Study
# from dataconnect.models import Dataset, DatasetVersion, Study, StudiesResult


# def _make_dataframe() -> pd.DataFrame:
Expand Down Expand Up @@ -160,9 +160,9 @@
# self.search_study_name: str | None = None
# self.was_closed = False

# def get_studies(self, search_study_name: str | None = None) -> list[Study]:
# def get_studies(self, search_study_name: str | None = None) -> StudiesResult:
# self.search_study_name = search_study_name
# return []
# return StudiesResult(total=0, studies=[])

# def close(self) -> None:
# self.was_closed = True
Expand All @@ -172,19 +172,23 @@
# service = StubService()
# client = DataConnectClient(service)

# studies = client.get_studies()
# result = client.get_studies()

# assert studies == []
# assert isinstance(result, StudiesResult)
# assert result.total == 0
# assert result.studies == []
# assert service.search_study_name is None


# def test_get_studies_with_filter_delegates_to_service() -> None:
# service = StubService()
# client = DataConnectClient(service)

# studies = client.get_studies(search_study_name="cardio")
# result = client.get_studies(search_study_name="cardio")

# assert studies == []
# assert isinstance(result, StudiesResult)
# assert result.total == 0
# assert result.studies == []
# assert service.search_study_name == "cardio"


Expand Down
48 changes: 22 additions & 26 deletions tests/test_service_default.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# import pytest

# from dataconnect.exceptions import ValidationError
# from dataconnect.models import StudiesResult
# from dataconnect.service.default import DefaultDataConnectService
# from dataconnect.transport.models import DataRef, ResourceInfo, ResourceQuery

Expand All @@ -20,13 +21,13 @@
# return None


# def _study_resource(name: str = "Study A") -> ResourceInfo:
# def _study_resource(name: str = "Study A", total_records: int = 1) -> ResourceInfo:
# payload = (f'{{"uuid":"12345678-1234-1234-1234-123456789abc","name":"{name}","environments":[]}}').encode()

# return ResourceInfo(
# descriptor=b"",
# endpoints=[DataRef(ticket=payload)],
# total_records=1,
# total_records=total_records,
# schema_bytes=b"",
# )

Expand All @@ -35,54 +36,49 @@
# transport = StubTransport(resources=[_study_resource()])
# service = DefaultDataConnectService(transport)

# studies = service.get_studies()
# result = service.get_studies()

# assert len(studies) == 1
# assert studies[0].name == "Study A"
# assert isinstance(result, StudiesResult)
# assert result.total == 1
# assert len(result.studies) == 1
# assert result.studies[0].name == "Study A"
# assert transport.last_request is not None
# assert transport.last_request.action == "studies.list"
# assert transport.last_request.body == ""


# def test_get_studies_with_search_name_sets_request_body() -> None:
# transport = StubTransport(resources=[_study_resource("Cardio Study")])
# transport = StubTransport(resources=[_study_resource("Cardio Study", total_records=5)])
# service = DefaultDataConnectService(transport)

# studies = service.get_studies(search_study_name="Cardio")
# result = service.get_studies(search_study_name="Cardio")

# assert len(studies) == 1
# assert studies[0].name == "Cardio Study"
# assert isinstance(result, StudiesResult)
# assert result.total == 5
# assert len(result.studies) == 1
# assert result.studies[0].name == "Cardio Study"
# assert transport.last_request is not None
# assert transport.last_request.body == '{"search_study_name":"Cardio"}'


# def test_get_studies_rejects_non_string_search_name() -> None:
# def test_get_studies_returns_zero_total_when_no_resources() -> None:
# transport = StubTransport(resources=[])
# service = DefaultDataConnectService(transport)

# with pytest.raises(ValidationError, match="search_study_name must be a string"):
# service.get_studies(search_study_name=123) # type: ignore[arg-type]
# result = service.get_studies()

# assert transport.last_request is None
# assert isinstance(result, StudiesResult)
# assert result.total == 0
# assert result.studies == []


# def test_get_studies_accepts_none_search_name() -> None:
# transport = StubTransport(resources=[_study_resource()])
# service = DefaultDataConnectService(transport)

# studies = service.get_studies(search_study_name=None)
# result = service.get_studies(search_study_name=None)

# assert len(studies) == 1
# assert isinstance(result, StudiesResult)
# assert len(result.studies) == 1
# assert transport.last_request is not None
# assert transport.last_request.body == ""

# def test_get_dataset_versions_sorted(monkeypatch):
# service = DefaultDataConnectService(transport=DummyTransport())
# # Patch the mapping function to our dummy
# monkeypatch.setattr(
# "dataconnect.service.default.resource_to_dataset_version",
# dummy_resource_to_dataset_version,
# )
# result = service.get_dataset_versions(UUID("073410b6-79be-3e7d-ae37-92f6e054013e"))
# versions = [dv.dataset_version for dv in result]
# assert versions == sorted(versions, reverse=True)
Loading