Skip to main content

protocol

Data-access seam for the patient-data API.

Defines the PatientDataRepository Protocol the API depends on. The cache-backed implementation lives in repository.py.

Classes

PatientDataRepository

class PatientDataRepository(*args, **kwargs):

Read-facade over patient-eligibility data.

Methods speak the API's vocabulary (trial/project and patient IDs) and return pydantic response models. Implementations hide any underlying storage detail (e.g. the cache's task_hash keying).

Methods


get_eligibility_counts

def get_eligibility_counts(    self, trial_ids: list[str],)> dict[str, EligibilityCounts]:

Return eligibility counts keyed by trial (project) ID.

Arguments

  • trial_ids: The trial (project) IDs to return counts for.

Returns A mapping of each requested trial ID to its status counts.

get_eligibility_evidence

def get_eligibility_evidence(    self, bitfount_patient_id: str, project_id: str,)> EligibilityEvidence | None:

Return eligibility evidence for a patient on a trial, or None.

Arguments

  • bitfount_patient_id: The Bitfount patient ID.
  • project_id: The trial (project) ID.

Returns The eligibility evidence, or None if the (patient, trial) pair is not found.

get_patient

def get_patient(    self, bitfount_patient_id: str,)> PatientSummary | None:

Return a single patient summary, or None if not found.

Arguments

  • bitfount_patient_id: The Bitfount patient ID.

Returns The patient summary, or None if no patient with that ID exists.

get_scan_image

def get_scan_image(    self,    *,    scan_id: str | None,    path: str | None,    modality: str | None,    laterality: str | None,    width: int | None,    refresh: bool,    include_masks: bool = False,    include_vectors: bool = False,    segmentation_classes: list[str] | None = None,)> bytes:

Resolve a scan reference and return its frames as a WebP ZIP.

Resolves scan_id/path to a validated file (the allowlist check), then renders the file to a ZIP of scaled WebP frames.

Arguments

  • scan_id: The scan identifier, or None (exactly one of scan_id/path).
  • path: An explicit, allowlisted file path, or None.
  • modality: Optional series selector for multi-series files.
  • laterality: Optional "L"/"R" series selector.
  • width: Optional output width (px); None uses the configured default.
  • refresh: When True, bypass and overwrite any cached archive.
  • include_masks: Whether the archive should carry a rendered mask PNG per segmentation class. Callers pass an already-resolved value (resolving the query params into a selection is the route's job, not this seam's).
  • include_vectors: Whether the served index should carry each class's raw drawable instances. Also already-resolved.
  • segmentation_classes: Optional class-name filter, applied only when an overlay output was requested. A name that matches no class the model produced yields no output for it, never an error.

Returns The application/zip archive bytes: frame_NNN.webp per frame, a manifest.json, and — when an overlay output was requested and segmentation is available — a segmentations.json index, plus one masks/ PNG per class when include_masks is True.

Raises

  • ScanNotFoundError: The reference does not resolve to an authorised file, or the scan-image feature is not enabled.
  • ScanRequestError: Bad inputs, non-positive width, or undecodable file.

list_patients

def list_patients(    self,    *,    search: str | None = None,    eligible_trials: list[str] | None = None,    page: int = 1,    page_size: int = 50,)> PatientList:

Return one page of patients, filtered and searched server-side.

Arguments

  • search: Case-insensitive substring matched against a patient's name or EHR id; None/blank matches everyone.
  • eligible_trials: If given, restrict to patients eligible for any of these trials (union). Each returned patient's eligible_trials list stays complete regardless of the filter.
  • page: 1-indexed page number.
  • page_size: Number of items per page.

Returns A PatientList whose items are the requested page and whose total is the count of all matching patients before pagination.

resolve_scan_path

def resolve_scan_path(self, *, scan_id: str | None, path: str | None)> str | None:

Resolve a scan reference to a validated absolute file path.

Exactly one of scan_id/path must be supplied. A scan_id is looked up in the scan_eligibility store; an explicit path is accepted only if its real path matches a stored scan's file_path (allowlist), which blocks path traversal and symlink escape.

Arguments

  • scan_id: The scan identifier to resolve, or None.
  • path: An explicit filesystem path to validate, or None.

Returns The validated absolute (real) path, or None if unknown/unauthorised.

Raises

  • ValueError: If not exactly one of scan_id/path is supplied.