ilm/ilm_types.py (77 lines of code) (raw):
# Copyright 2023 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# ==============================================================================
"""Types used in ILM pipeline."""
import bisect
import dataclasses
import datetime
from typing import List, Optional
import dataclasses_json
from ilm import ilm_config
@dataclasses.dataclass(frozen=True)
class AccessMetadata:
"""Instance access metadata."""
# Total (cumulative) access count, sorted by number of days passed.
cumulative_access_counts: List[ilm_config.AccessCount]
def __post_init__(self):
for i in range(len(self.cumulative_access_counts) - 1):
if (
self.cumulative_access_counts[i + 1].num_days
<= self.cumulative_access_counts[i].num_days
):
raise ValueError(
'Cumulative access count list should be sorted:'
f' {self.cumulative_access_counts}'
)
def get_access_count(self, num_days: int) -> float:
"""Returns total number of accesses for the past num_days."""
i = (
bisect.bisect_right(
self.cumulative_access_counts, num_days, key=lambda x: x.num_days
)
- 1
)
if i < 0:
# No access in past num_days.
return 0
return self.cumulative_access_counts[i].count
@dataclasses.dataclass(frozen=True)
class LogAccessCount:
frames_access_count: Optional[ilm_config.AccessCount] = None
instance_access_count: Optional[ilm_config.AccessCount] = None
@dataclasses.dataclass(frozen=True)
class LogAccessMetadata:
frames_access_counts: List[ilm_config.AccessCount]
instance_access_counts: List[ilm_config.AccessCount]
@dataclasses.dataclass(frozen=True)
class InstanceMetadata:
"""Instance metadata to be considered when moving between storage classes."""
# DICOM metadata
instance: str # Instance DICOM web path.
modality: Optional[str]
num_frames: int
pixel_spacing: Optional[float]
sop_class_uid: str
acquisition_date: Optional[datetime.date]
content_date: Optional[datetime.date]
series_date: Optional[datetime.date]
study_date: Optional[datetime.date]
image_type: Optional[str]
# Storage metadata
size_bytes: int
storage_class: ilm_config.StorageClass
num_days_in_current_storage_class: int
# Access metadata
access_metadata: Optional[AccessMetadata] = None
@dataclasses.dataclass(frozen=True)
class MoveRuleId:
rule_index: int
condition_index: int
@dataclasses.dataclass(frozen=True)
class StorageClassChange:
instance: str
new_storage_class: ilm_config.StorageClass
move_rule_id: MoveRuleId
@dataclasses_json.dataclass_json
@dataclasses.dataclass(frozen=True)
class SetStorageClassRequestMetadata:
move_rule_ids: List[MoveRuleId]
# Only populated if generating detailed report of updates. See report config.
instances: List[str]
filter_file_gcs_uri: str
new_storage_class: ilm_config.StorageClass
@dataclasses.dataclass(frozen=False)
class SetStorageClassOperationMetadata:
move_rule_ids: List[MoveRuleId]
instances: List[str]
operation_name: str
filter_file_gcs_uri: Optional[str] = None
start_time: Optional[float] = None
# None indicates unfinished operation.
succeeded: Optional[bool] = None