|
1 | 1 | """Utilities for DICOMweb URI manipulation.""" |
2 | | -import attr |
| 2 | +import dataclasses |
3 | 3 | import enum |
4 | 4 | import re |
5 | 5 | from typing import Optional, Sequence, Tuple |
@@ -27,14 +27,22 @@ class URISuffix(enum.Enum): |
27 | 27 | _REGEX_UID = re.compile(r'[0-9]+([.][0-9]+)*') |
28 | 28 | _REGEX_PERMISSIVE_UID = re.compile(r'[^/@]+') |
29 | 29 | # Used for Project ID and Location validation in `GoogleCloudHealthcareURL`. |
30 | | -_REGEX_ID_1 = r'[\w-]+' |
31 | | -_ATTR_VALIDATOR_ID_1 = attr.validators.matches_re(_REGEX_ID_1) |
| 30 | +_REGEX_ID_1 = re.compile(r'[\w-]+') |
32 | 31 | # Used for Dataset ID and DICOM Store ID validation in |
33 | 32 | # `GoogleCloudHealthcareURL`. |
34 | | -_REGEX_ID_2 = r'[\w.-]+' |
35 | | -_ATTR_VALIDATOR_ID_2 = attr.validators.matches_re(_REGEX_ID_2) |
| 33 | +_REGEX_ID_2 = re.compile(r'[\w.-]+') |
| 34 | +# Regex for the DICOM Store suffix for the Google Cloud Healthcare API endpoint. |
| 35 | +_STORE_REGEX = re.compile( |
| 36 | + (r'projects/(%s)/locations/(%s)/datasets/(%s)/' |
| 37 | + r'dicomStores/(%s)/dicomWeb$') % (_REGEX_ID_1.pattern, |
| 38 | + _REGEX_ID_1.pattern, |
| 39 | + _REGEX_ID_2.pattern, |
| 40 | + _REGEX_ID_2.pattern)) |
36 | 41 | # The URL for the Google Cloud Healthcare API endpoint. |
37 | 42 | _CHC_API_URL = 'https://healthcare.googleapis.com/v1' |
| 43 | +# Cloud Healthcare validation error. |
| 44 | +_CHC_API_ERROR_TMPL = ('`{attribute}` must match regex {regex}. Actual value: ' |
| 45 | + '{value!r}') |
38 | 46 |
|
39 | 47 |
|
40 | 48 | class URI: |
@@ -476,7 +484,7 @@ def from_string(cls, |
476 | 484 | return uri |
477 | 485 |
|
478 | 486 |
|
479 | | -@attr.s(frozen=True) |
| 487 | +@dataclasses.dataclass(eq=True, frozen=True) |
480 | 488 | class GoogleCloudHealthcareURL: |
481 | 489 | """Base URL container for DICOM Stores under the `Google Cloud Healthcare API`_. |
482 | 490 |
|
@@ -506,10 +514,27 @@ class GoogleCloudHealthcareURL: |
506 | 514 | The ID of the `DICOM Store |
507 | 515 | <https://cloud.google.com/healthcare/docs/concepts/dicom#dicom_stores>`_. |
508 | 516 | """ |
509 | | - project_id = attr.ib(type=str, validator=_ATTR_VALIDATOR_ID_1) |
510 | | - location = attr.ib(type=str, validator=_ATTR_VALIDATOR_ID_1) |
511 | | - dataset_id = attr.ib(type=str, validator=_ATTR_VALIDATOR_ID_2) |
512 | | - dicom_store_id = attr.ib(type=str, validator=_ATTR_VALIDATOR_ID_2) |
| 517 | + project_id: str |
| 518 | + location: str |
| 519 | + dataset_id: str |
| 520 | + dicom_store_id: str |
| 521 | + |
| 522 | + def __post_init__(self) -> None: |
| 523 | + """Performs input sanity checks.""" |
| 524 | + if _REGEX_ID_1.fullmatch(self.project_id) is None: |
| 525 | + raise ValueError(_CHC_API_ERROR_TMPL.format( |
| 526 | + attribute='project_id', regex=_REGEX_ID_1, value=self.project_id)) |
| 527 | + if _REGEX_ID_1.fullmatch(self.location) is None: |
| 528 | + raise ValueError(_CHC_API_ERROR_TMPL.format( |
| 529 | + attribute='location', regex=_REGEX_ID_1, value=self.location)) |
| 530 | + if _REGEX_ID_2.fullmatch(self.dataset_id) is None: |
| 531 | + raise ValueError(_CHC_API_ERROR_TMPL.format( |
| 532 | + attribute='dataset_id', regex=_REGEX_ID_2, value=self.dataset_id)) |
| 533 | + if _REGEX_ID_2.fullmatch(self.dicom_store_id) is None: |
| 534 | + raise ValueError(_CHC_API_ERROR_TMPL.format( |
| 535 | + attribute='dicom_store_id', |
| 536 | + regex=_REGEX_ID_2, |
| 537 | + value=self.dicom_store_id)) |
513 | 538 |
|
514 | 539 | def __str__(self) -> str: |
515 | 540 | """Returns a string URL for use as :py:attr:`URI.base_url`. |
@@ -544,10 +569,7 @@ def from_string(cls, base_url: str) -> 'GoogleCloudHealthcareURL': |
544 | 569 | raise ValueError('Invalid CHC API v1 URL: {base_url!r}') |
545 | 570 | resource_suffix = base_url[len(_CHC_API_URL) + 1:] |
546 | 571 |
|
547 | | - store_regex = (r'projects/(%s)/locations/(%s)/datasets/(%s)/' |
548 | | - r'dicomStores/(%s)/dicomWeb$') % ( |
549 | | - _REGEX_ID_1, _REGEX_ID_1, _REGEX_ID_2, _REGEX_ID_2) |
550 | | - store_match = re.match(store_regex, resource_suffix) |
| 572 | + store_match = _STORE_REGEX.match(resource_suffix) |
551 | 573 | if store_match is None: |
552 | 574 | raise ValueError( |
553 | 575 | 'Invalid CHC API v1 DICOM Store name: {resource_suffix!r}') |
|
0 commit comments