data/data_item.py
"""Data item class definition."""
import typing
import yaml
class DataItem:
"""Data item configuration from yaml."""
def __init__(self, name, data) -> None:
"""Initialize item config."""
self._name = name
self._data = data
self._rendered = None
assert "type" in data
assert "description" in data
@classmethod
def load_all(cls, root) -> typing.List["DataItem"]:
"""Load all data item objects."""
data = (root / "data_items.yaml").read_text(encoding="utf8")
yaml_data = yaml.safe_load(data)
return [cls(data_item, data_item_data) for data_item, data_item_data in yaml_data.items()]
@staticmethod
def render_list(data_items, env, functions, target_path):
"""Render a list of data items."""
last = None
data_item_template = env.get_template('data_items.py.j2')
data_item_init_template = env.get_template('data_items_init.py.j2')
data_item_md_template = env.get_template('data_items.md.j2')
for data_item in data_items:
print(f"# generate data item {data_item.name}")
used_by = [function for function in functions if data_item in function.data_items]
last = data_item.render(data_item_template, target_path, used_by)
init_code = data_item_init_template.render(
data_items=data_items,
)
out_path = target_path / "__init__.py"
out_path.write_text(init_code)
md_code = data_item_md_template.render(
data_items=data_items,
)
out_path = target_path.parent.parent.parent / "docs" / "reference" / "secs" / "data_items.md"
out_path.write_text(md_code)
return last
def render(self, data_item_template, target_path, used_by):
"""Render the data item file."""
self._rendered = data_item_template.render(
data=self,
used_by=used_by
)
out_path = target_path / self.file_name
out_path.write_text(self._rendered)
return self.file_name
@property
def name(self) -> str:
"""Get the name of the data item."""
return self._name
@property
def type(self) -> typing.List[str]:
"""Get the type of the data item."""
if not isinstance(self._data["type"], list):
return [self._data["type"]]
return self._data["type"]
@property
def linter_message(self) -> str:
"""Get the linter message of the data item."""
if "linter_message" not in self._data:
return ""
return self._data["linter_message"]
@property
def single_type(self) -> bool:
"""Check if this is a single type object."""
if not isinstance(self._data["type"], list):
return True
if len(self._data["type"]) == 1:
return True
return False
@property
def description(self) -> str:
"""Get the description of the data item."""
return self._data["description"]
@property
def help(self) -> str:
"""Get the help of the data item."""
if "help" not in self._data:
return ""
return self._data["help"]
@property
def length(self) -> int:
"""Get the length of the data item."""
return self._data["length"] if "length" in self._data else -1
@property
def values(self) -> str:
"""Get the values of the data item."""
if len(self.type) == 1:
if self.type[0] == "Boolean":
return self._values_boolean
return self._values_binary
@property
def extra_variables(self) -> str:
"""Get the extra variables of the data item."""
if len(self.type) == 1:
if self.type[0] in ("Boolean"):
return ""
return self._extra_variables_binary
@property
def _values_boolean(self) -> str:
if "values" not in self._data:
return ""
table_data = [
["Value", ""],
["True", self._data["values"][True]],
["False", self._data["values"][False]],
]
table = self._markdown_table(table_data, 8)
text = f" **Values**\n{table}\n"
return text
@property
def _values_binary(self) -> str:
if "values" not in self._data:
return ""
table_data = [
["Value", "Description", "Constant"],
]
for value_name, value in self._data["values"].items():
constant = f":const:`secsgem.secs.data_items.{self.name}.{value['constant']}`" \
if "constant" in value else ""
table_data.append([str(value_name), value["description"], constant])
table = self._markdown_table(table_data, 8)
text = f" **Values**\n{table}\n"
return text
@property
def _extra_variables_binary(self) -> str:
if "values" not in self._data:
return ""
variables = []
for name, value in self._data["values"].items():
if "constant" not in value:
continue
val = str(name)
if "-" in val:
val = val.split("-", maxsplit=1)[0]
variables.append(f"{value['constant']} = {val}")
if len(variables) < 1:
return ""
join_text = '\n '
text = f"\n {join_text.join(variables)}\n"
return text
@property
def file_name(self) -> str:
"""Get the file name."""
return f"{self.module_name}.py"
@property
def module_name(self) -> str:
"""Get the file name."""
return self.name.lower()
def _markdown_line_separator(self, lengths, separator="-"):
line = "+"
for length in lengths:
line += separator * (length + 2)
line += "+"
return line
def _markdown_line(self, lengths, values):
line = "|"
for index, length in enumerate(lengths):
line += " "
line += f"{values[index]:{length}}"
line += " |"
return line
def _markdown_table(self, data, indent=4):
lengths = [0] * len(data[0])
for line, line_value in enumerate(data):
for col, _ in enumerate(lengths):
length = len(data[line][col])
lengths[col] = max(lengths[col], length)
indent_text = " " * indent
text = f"{indent_text}{self._markdown_line_separator(lengths)}"
for line, line_value in enumerate(data):
text += f"\n{indent_text}{self._markdown_line(lengths, line_value)}"
text += f"\n{indent_text}{self._markdown_line_separator(lengths, '-' if line != 0 else '=')}"
text += "\n"
return text