|  | 
|  | 1 | +"""Utilities for Google Cloud Healthcare DICOMweb API URI manipulation. | 
|  | 2 | +
 | 
|  | 3 | +For details, visit: https://cloud.google.com/healthcare | 
|  | 4 | +""" | 
|  | 5 | +import dataclasses | 
|  | 6 | +import re | 
|  | 7 | + | 
|  | 8 | + | 
|  | 9 | +# Used for Project ID and Location validation in `GoogleCloudHealthcareURL`. | 
|  | 10 | +_REGEX_ID_1 = re.compile(r'[\w-]+') | 
|  | 11 | +# Used for Dataset ID and DICOM Store ID validation in | 
|  | 12 | +# `GoogleCloudHealthcareURL`. | 
|  | 13 | +_REGEX_ID_2 = re.compile(r'[\w.-]+') | 
|  | 14 | +# Regex for the DICOM Store suffix for the Google Cloud Healthcare API endpoint. | 
|  | 15 | +_STORE_REGEX = re.compile( | 
|  | 16 | +    (r'projects/(%s)/locations/(%s)/datasets/(%s)/' | 
|  | 17 | +     r'dicomStores/(%s)/dicomWeb$') % (_REGEX_ID_1.pattern, | 
|  | 18 | +                                       _REGEX_ID_1.pattern, | 
|  | 19 | +                                       _REGEX_ID_2.pattern, | 
|  | 20 | +                                       _REGEX_ID_2.pattern)) | 
|  | 21 | +# The URL for the Google Cloud Healthcare API endpoint. | 
|  | 22 | +_CHC_API_URL = 'https://healthcare.googleapis.com/v1' | 
|  | 23 | +# GCP resource name validation error. | 
|  | 24 | +_GCP_RESOURCE_ERROR_TMPL = ('`{attribute}` must match regex {regex}. Actual ' | 
|  | 25 | +                            'value: {value!r}') | 
|  | 26 | + | 
|  | 27 | + | 
|  | 28 | +@dataclasses.dataclass(eq=True, frozen=True) | 
|  | 29 | +class GoogleCloudHealthcareURL: | 
|  | 30 | +    """Base URL container for DICOM Stores under the `Google Cloud Healthcare API`_. | 
|  | 31 | +
 | 
|  | 32 | +    This class facilitates the parsing and creation of :py:attr:`URI.base_url` | 
|  | 33 | +    corresponding to DICOMweb API Service URLs under the v1_ API. The URLs are | 
|  | 34 | +    of the form: | 
|  | 35 | +    ``https://healthcare.googleapis.com/v1/projects/{project_id}/locations/{location}/datasets/{dataset_id}/dicomStores/{dicom_store_id}/dicomWeb`` | 
|  | 36 | +
 | 
|  | 37 | +    .. _Google Cloud Healthcare API: https://cloud.google.com/healthcare | 
|  | 38 | +    .. _v1: https://cloud.google.com/healthcare/docs/how-tos/transition-guide | 
|  | 39 | +
 | 
|  | 40 | +    Attributes: | 
|  | 41 | +        project_id: str | 
|  | 42 | +            The ID of the `GCP Project | 
|  | 43 | +            <https://cloud.google.com/healthcare/docs/concepts/projects-datasets-data-stores#projects>`_ | 
|  | 44 | +            that contains the DICOM Store. | 
|  | 45 | +        location: str | 
|  | 46 | +            The `Region name | 
|  | 47 | +            <https://cloud.google.com/healthcare/docs/concepts/regions>`_ of the | 
|  | 48 | +            geographic location configured for the Dataset that contains the | 
|  | 49 | +            DICOM Store. | 
|  | 50 | +        dataset_id: str | 
|  | 51 | +            The ID of the `Dataset | 
|  | 52 | +            <https://cloud.google.com/healthcare/docs/concepts/projects-datasets-data-stores#datasets_and_data_stores>`_ | 
|  | 53 | +            that contains the DICOM Store. | 
|  | 54 | +        dicom_store_id: str | 
|  | 55 | +            The ID of the `DICOM Store | 
|  | 56 | +            <https://cloud.google.com/healthcare/docs/concepts/dicom#dicom_stores>`_. | 
|  | 57 | +    """ | 
|  | 58 | +    project_id: str | 
|  | 59 | +    location: str | 
|  | 60 | +    dataset_id: str | 
|  | 61 | +    dicom_store_id: str | 
|  | 62 | + | 
|  | 63 | +    def __post_init__(self) -> None: | 
|  | 64 | +        """Performs input sanity checks.""" | 
|  | 65 | +        for regex, attribute, value in ( | 
|  | 66 | +                (_REGEX_ID_1, 'project_id', self.project_id), | 
|  | 67 | +                (_REGEX_ID_1, 'location', self.location), | 
|  | 68 | +                (_REGEX_ID_2, 'dataset_id', self.dataset_id), | 
|  | 69 | +                (_REGEX_ID_2, 'dicom_store_id', self.dicom_store_id)): | 
|  | 70 | +            if regex.fullmatch(value) is None: | 
|  | 71 | +                raise ValueError(_GCP_RESOURCE_ERROR_TMPL.format( | 
|  | 72 | +                    attribute=attribute, regex=regex, value=value)) | 
|  | 73 | + | 
|  | 74 | +    def __str__(self) -> str: | 
|  | 75 | +        """Returns a string URL for use as :py:attr:`URI.base_url`. | 
|  | 76 | +
 | 
|  | 77 | +        See class docstring for the returned URL format. | 
|  | 78 | +        """ | 
|  | 79 | +        return (f'{_CHC_API_URL}/' | 
|  | 80 | +                f'projects/{self.project_id}/' | 
|  | 81 | +                f'locations/{self.location}/' | 
|  | 82 | +                f'datasets/{self.dataset_id}/' | 
|  | 83 | +                f'dicomStores/{self.dicom_store_id}/dicomWeb') | 
|  | 84 | + | 
|  | 85 | +    @classmethod | 
|  | 86 | +    def from_string(cls, base_url: str) -> 'GoogleCloudHealthcareURL': | 
|  | 87 | +        """Creates an instance from ``base_url``. | 
|  | 88 | +
 | 
|  | 89 | +        Parameters | 
|  | 90 | +        ---------- | 
|  | 91 | +        base_url: str | 
|  | 92 | +            The URL for the DICOMweb API Service endpoint corresponding to a | 
|  | 93 | +            `CHC API DICOM Store | 
|  | 94 | +            <https://cloud.google.com/healthcare/docs/concepts/dicom#dicom_stores>`_. | 
|  | 95 | +            See class docstring for supported formats. | 
|  | 96 | +
 | 
|  | 97 | +        Raises | 
|  | 98 | +        ------ | 
|  | 99 | +        ValueError | 
|  | 100 | +            If ``base_url`` does not match the specifications in the class | 
|  | 101 | +            docstring. | 
|  | 102 | +        """ | 
|  | 103 | +        if not base_url.startswith(f'{_CHC_API_URL}/'): | 
|  | 104 | +            raise ValueError('Invalid CHC API v1 URL: {base_url!r}') | 
|  | 105 | +        resource_suffix = base_url[len(_CHC_API_URL) + 1:] | 
|  | 106 | + | 
|  | 107 | +        store_match = _STORE_REGEX.match(resource_suffix) | 
|  | 108 | +        if store_match is None: | 
|  | 109 | +            raise ValueError( | 
|  | 110 | +                'Invalid CHC API v1 DICOM Store name: {resource_suffix!r}') | 
|  | 111 | + | 
|  | 112 | +        return cls(store_match.group(1), store_match.group(2), | 
|  | 113 | +                   store_match.group(3), store_match.group(4)) | 
0 commit comments