rostran/core/metadata.py (184 lines of code) (raw):
from rostran.core.exceptions import InvalidTemplateMetaDataItem, InvalidTemplateMetaData
from rostran.core.utils import sorted_data
class MetaItem:
PREDEFINED_PARAMETERS = "PredefinedParameters"
ROS_INTERFACE = "ALIYUN::ROS::Interface"
ROS_DESIGNER = "ALIYUN::ROS::Designer"
NAME = "Name"
PARAMETER_GROUPS = "ParameterGroups"
PARAMETERS = "Parameters"
LABEL = "Label"
LABEL_KEYS = (DEFAULT, EN, ZH_CN) = ("default", "en", "zh-cn")
TEMPLATE_TAGS = "TemplateTags"
GROUP_TYPE = "GroupType"
PREDEFINED_PARAMETERS_KEY_SCORES = {NAME: 0, PARAMETERS: 1}
ROS_INTERFACE_KEY_SCORES = {PARAMETER_GROUPS: 0, TEMPLATE_TAGS: 1}
PARAMETER_GROUP_KEY_SCORES = {GROUP_TYPE: 0, PARAMETERS: 1, LABEL: 2}
def __init__(self, type: str, value: dict):
self.type = type
self.value = value
@classmethod
def initialize(cls, type: str, value: dict):
meta_item = cls(type, value)
meta_item.validate()
return meta_item
def validate(self):
if not isinstance(self.type, str):
raise InvalidTemplateMetaDataItem(
name=self.type,
reason=f"The type should be str",
)
if self.type in (self.ROS_INTERFACE, self.ROS_DESIGNER) and not isinstance(
self.value, dict
):
raise InvalidTemplateMetaDataItem(
name=self.type,
reason=f"The type of value ({self.value}) should be dict",
)
# validate PredefinedParameters
if self.type == self.PREDEFINED_PARAMETERS:
if not isinstance(self.value, list):
raise InvalidTemplateMetaDataItem(
name=self.type,
reason=f"The type of value ({self.value}) should be list",
)
for i, pp in enumerate(self.value):
if not isinstance(pp, dict):
raise InvalidTemplateMetaDataItem(
name=f"{self.type}[{i}]",
reason=f"The type of value ({pp}) should be dict",
)
if self.NAME not in pp:
raise InvalidTemplateMetaDataItem(
name=f"{self.type}[{i}]",
reason=f"{self.NAME} is missing",
)
name = pp[self.NAME]
if not isinstance(name, str):
raise InvalidTemplateMetaDataItem(
name=f"{self.type}[{i}].{self.NAME}",
reason=f"The type of value ({name}) should be str",
)
if self.PARAMETERS not in pp:
raise InvalidTemplateMetaDataItem(
name=f"{self.type}[{i}]",
reason=f"{self.PARAMETERS} is missing",
)
parameters = pp[self.PARAMETERS]
if not isinstance(parameters, dict):
raise InvalidTemplateMetaDataItem(
name=f"{self.type}[{i}].{self.PARAMETERS}",
reason=f"The type of value ({parameters}) should be dict",
)
# validate ALIYUN::ROS::Interface
if self.type == self.ROS_INTERFACE:
# validate ParameterGroups
if self.PARAMETER_GROUPS in self.value:
param_groups = self.value[self.PARAMETER_GROUPS]
if not isinstance(param_groups, list):
raise InvalidTemplateMetaDataItem(
name=f"{self.ROS_INTERFACE}.{self.PARAMETER_GROUPS}",
reason=f"The type of value ({param_groups}) should be list",
)
for i, param_group in enumerate(param_groups):
if not isinstance(param_group, dict):
raise InvalidTemplateMetaDataItem(
name=f"{self.ROS_INTERFACE}.{self.PARAMETER_GROUPS}[{i}]",
reason=f"The type of value ({param_group}) should be dict",
)
# validate Parameters
if self.PARAMETERS not in param_group:
raise InvalidTemplateMetaDataItem(
name=f"{self.ROS_INTERFACE}.{self.PARAMETER_GROUPS}[{i}]",
reason=f"{self.PARAMETERS} is missing",
)
parameters = param_group[self.PARAMETERS]
if not isinstance(parameters, list):
raise InvalidTemplateMetaDataItem(
name=f"{self.ROS_INTERFACE}.{self.PARAMETER_GROUPS}[{i}].{self.PARAMETERS}",
reason=f"The type of value ({parameters}) should be list",
)
for j, parameter in enumerate(parameters):
if isinstance(parameter, dict):
continue
if not isinstance(parameter, str):
raise InvalidTemplateMetaDataItem(
name=f"{self.ROS_INTERFACE}.{self.PARAMETER_GROUPS}[{i}].{self.PARAMETERS}[{j}]",
reason=f"The type of value ({parameter}) should be str",
)
# validate Label
if self.LABEL in param_group:
label = param_group[self.LABEL]
if not isinstance(label, dict):
raise InvalidTemplateMetaDataItem(
name=f"{self.ROS_INTERFACE}.{self.PARAMETER_GROUPS}[{i}].{self.LABEL}",
reason=f"The type of value ({label}) should be dict",
)
if not any(key in label for key in self.LABEL_KEYS):
label_keys = ", ".join(self.LABEL_KEYS)
raise InvalidTemplateMetaDataItem(
name=f"{self.ROS_INTERFACE}.{self.PARAMETER_GROUPS}[{i}].{self.LABEL}",
reason=f"one of {label_keys} is missing",
)
# validate TemplateTags
if self.TEMPLATE_TAGS in self.value:
template_tags = self.value[self.TEMPLATE_TAGS]
if not isinstance(template_tags, list):
raise InvalidTemplateMetaDataItem(
name=f"{self.ROS_INTERFACE}.{self.TEMPLATE_TAGS}",
reason=f"The type of value ({template_tags}) should be list",
)
for i, template_tag in enumerate(template_tags):
if not isinstance(template_tag, str):
raise InvalidTemplateMetaDataItem(
name=f"{self.ROS_INTERFACE}.{self.TEMPLATE_TAGS}[{i}]",
reason=f"The type of value ({template_tag}) should be str",
)
def as_dict(self, format=False):
data = {self.type: self.value}
if not format:
return data
if self.type == self.PREDEFINED_PARAMETERS:
sorted_pps = []
for i, pp in enumerate(self.value):
sorted_pp = sorted_data(
pp, scores=self.PREDEFINED_PARAMETERS_KEY_SCORES
)
sorted_pps.append(sorted_pp)
data[self.type] = sorted_pps
if self.type == self.ROS_INTERFACE:
data[self.type] = sorted_data(
self.value, scores=self.ROS_INTERFACE_KEY_SCORES
)
param_groups = self.value.get(self.PARAMETER_GROUPS)
if param_groups:
for i, param_group in enumerate(param_groups):
param_groups[i] = sorted_data(
param_group, scores=self.PARAMETER_GROUP_KEY_SCORES
)
template_tags = self.value.get(self.TEMPLATE_TAGS)
if template_tags:
template_tags.sort()
return data
class MetaData(dict):
ROS_INTERFACE = "ALIYUN::ROS::Interface"
ROS_DESIGNER = "ALIYUN::ROS::Designer"
PREDEFINED_PARAMETERS = "PredefinedParameters"
META_DATA_KEY_SCORES = {PREDEFINED_PARAMETERS: 0, ROS_INTERFACE: 1, ROS_DESIGNER: 2}
@classmethod
def initialize(cls, data: dict):
if not isinstance(data, dict):
raise InvalidTemplateMetaData(
reason=f"The type of data ({data}) should be dict"
)
meta_data = cls()
for type, value in data.items():
meta_item = MetaItem.initialize(type, value)
meta_data.add(meta_item)
return meta_data
def add(self, meta_item: MetaItem):
self[meta_item.type] = meta_item
def as_dict(self, format=False) -> dict:
data = {}
keys = self.keys()
if format:
keys = sorted_data(keys, scores=self.META_DATA_KEY_SCORES)
for key in keys:
meta: MetaItem = self[key]
if meta.value is not None:
data.update(meta.as_dict(format))
return data