716 lines
27 KiB
Python
716 lines
27 KiB
Python
"""
|
|
Silique (https://www.silique.fr)
|
|
Copyright (C) 2024-2025
|
|
|
|
This program is free software: you can redistribute it and/or modify it
|
|
under the terms of the GNU Lesser General Public License as published by the
|
|
Free Software Foundation, either version 3 of the License, or (at your
|
|
option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful, but WITHOUT
|
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
|
details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
"""
|
|
|
|
from typing import Tuple, List, Optional
|
|
|
|
from io import BytesIO
|
|
from ruamel.yaml import YAML
|
|
import tabulate as tabulate_module
|
|
from tiramisu.error import display_list
|
|
from tabulate import tabulate
|
|
|
|
from rougail.tiramisu import normalize_family
|
|
from tiramisu import undefined
|
|
|
|
from .i18n import _
|
|
|
|
|
|
ROUGAIL_VARIABLE_TYPE = (
|
|
"https://rougail.readthedocs.io/en/latest/variable.html#variables-types"
|
|
)
|
|
|
|
|
|
ENTER = "\n\n"
|
|
|
|
|
|
DocTypes = {
|
|
"domainname": {
|
|
"params": {
|
|
"allow_startswith_dot": _("the domain name can starts by a dot"),
|
|
"allow_without_dot": _("the domain name can be a hostname"),
|
|
"allow_ip": _("the domain name can be an IP"),
|
|
"allow_cidr_network": _("the domain name can be network in CIDR format"),
|
|
},
|
|
},
|
|
"number": {
|
|
"params": {
|
|
"min_number": _("the minimum value is {0}"),
|
|
"max_number": _("the maximum value is {0}"),
|
|
},
|
|
},
|
|
"integer": {
|
|
"params": {
|
|
"min_integer": _("the minimum value is {0}"),
|
|
"max_integer": _("the maximum value is {0}"),
|
|
},
|
|
},
|
|
"ip": {
|
|
"msg": "IP",
|
|
"params": {
|
|
"cidr": _("IP must be in CIDR format"),
|
|
"private_only": _("private IP are allowed"),
|
|
"allow_reserved": _("reserved IP are allowed"),
|
|
},
|
|
},
|
|
"network": {
|
|
"params": {
|
|
"cidr": _("network must be in CIDR format"),
|
|
},
|
|
},
|
|
"hostname": {
|
|
"params": {
|
|
"allow_ip": _("the host name can be an IP"),
|
|
},
|
|
},
|
|
"web_address": {
|
|
"params": {
|
|
"allow_ip": _("the domain name in web address can be an IP"),
|
|
"allow_without_dot": _(
|
|
"the domain name in web address can be only a hostname"
|
|
),
|
|
},
|
|
},
|
|
"port": {
|
|
"params": {
|
|
"allow_range": _("can be range of port"),
|
|
"allow_protocol": _("can have the protocol"),
|
|
"allow_zero": _("port 0 is allowed"),
|
|
"allow_wellknown": _("well-known ports (1 to 1023) are allowed"),
|
|
"allow_registred": _("registred ports (1024 to 49151) are allowed"),
|
|
"allow_private": _("private ports (greater than 49152) are allowed"),
|
|
},
|
|
},
|
|
"secret": {
|
|
"params": {
|
|
"min_len": _("minimum length for the secret is {0} characters"),
|
|
"max_len": _("maximum length for the secret is {0} characters"),
|
|
"forbidden_char": _("forbidden characters: {0}"),
|
|
},
|
|
},
|
|
"unix_filename": {
|
|
"params": {
|
|
"allow_relative": _("this filename could be a relative path"),
|
|
"test_existence": _("this file must exists"),
|
|
"types": _("file type allowed: {0}"),
|
|
},
|
|
},
|
|
}
|
|
|
|
|
|
_yaml = YAML()
|
|
_yaml.indent(mapping=2, sequence=4, offset=2)
|
|
|
|
|
|
def dump(informations):
|
|
"""Dump variable, means transform bool, ... to yaml string"""
|
|
with BytesIO() as ymlfh:
|
|
_yaml.dump(informations, ymlfh)
|
|
ret = ymlfh.getvalue().decode("utf-8").strip()
|
|
if ret.endswith("..."):
|
|
ret = ret[:-3].strip()
|
|
return ret
|
|
|
|
|
|
def to_phrase(msg, type_="variable"):
|
|
"""Add maj for the first character and ends with dot
|
|
"""
|
|
if not msg:
|
|
# replace None to empty string
|
|
return ""
|
|
msg = str(msg).strip()
|
|
# a phrase must ends with a dot
|
|
if type_ == 'variable':
|
|
if not msg.endswith("."):
|
|
msg += "."
|
|
elif type_ == 'family':
|
|
if msg.endswith("."):
|
|
msg = msg[:-1]
|
|
else:
|
|
raise Exception('unknown type')
|
|
# and start with a maj
|
|
return msg[0].upper() + msg[1:]
|
|
|
|
|
|
class CommonFormater:
|
|
"""Class with common function for formater"""
|
|
|
|
enter_table = "\n"
|
|
# tabulate module name
|
|
name = None
|
|
|
|
def __init__(self, with_family: bool):
|
|
tabulate_module.PRESERVE_WHITESPACE = True
|
|
self.header_setted = False
|
|
self.with_family = with_family
|
|
|
|
# Class you needs implement to your Formater
|
|
def title(
|
|
self,
|
|
title: str,
|
|
level: int,
|
|
) -> str:
|
|
"""Display family name as a title"""
|
|
raise NotImplementedError()
|
|
|
|
def join(
|
|
self,
|
|
lst: List[str],
|
|
) -> str:
|
|
"""Display line in table from a list"""
|
|
raise NotImplementedError()
|
|
|
|
def bold(
|
|
self,
|
|
msg: str,
|
|
) -> str:
|
|
"""Set a text to bold"""
|
|
raise NotImplementedError()
|
|
|
|
def stripped(
|
|
self,
|
|
text: str,
|
|
) -> str:
|
|
"""Return stripped text (as help)"""
|
|
raise NotImplementedError()
|
|
|
|
def list(
|
|
self,
|
|
choices: list,
|
|
) -> str:
|
|
"""Display a liste of element"""
|
|
raise NotImplementedError()
|
|
|
|
def prop(
|
|
self,
|
|
prop: str,
|
|
italic: bool,
|
|
) -> str:
|
|
"""Display property"""
|
|
raise NotImplementedError()
|
|
|
|
def link(
|
|
self,
|
|
comment: str,
|
|
link: str,
|
|
) -> str:
|
|
"""Add a link"""
|
|
raise NotImplementedError()
|
|
|
|
##################
|
|
|
|
def family_informations(self) -> str:
|
|
return ''
|
|
|
|
def end_family_informations(self) -> str:
|
|
return ''
|
|
|
|
def display_paths(
|
|
self,
|
|
informations: dict,
|
|
modified_attributes: dict,
|
|
) -> str:
|
|
ret_paths = []
|
|
path = informations["path"]
|
|
if "identifiers" in modified_attributes:
|
|
name, previous, new = modified_attributes["identifiers"]
|
|
ret_paths.extend([self.bold(self.delete(calc_path(path, self, identifier))) for identifier in previous])
|
|
else:
|
|
new = []
|
|
if "identifiers" in informations:
|
|
for identifier in informations["identifiers"]:
|
|
path_ = calc_path(path, self, identifier)
|
|
if identifier in new:
|
|
path_ = self.underline(path_)
|
|
ret_paths.append(self.bold(path_))
|
|
else:
|
|
ret_paths.append(self.bold(path))
|
|
return ret_paths
|
|
|
|
def after_family_paths(self) -> str:
|
|
return ENTER
|
|
|
|
def after_family_properties(self) -> str:
|
|
return ENTER
|
|
|
|
def table_header(
|
|
self,
|
|
lst: list,
|
|
) -> tuple:
|
|
"""Manage the header of a table"""
|
|
return lst
|
|
|
|
def run(self, informations: dict, level: int, *, dico_is_already_treated=False) -> str:
|
|
"""Transform to string"""
|
|
if informations:
|
|
return self._run(informations, level, dico_is_already_treated)
|
|
return ""
|
|
|
|
def _run(self, dico: dict, level: int, dico_is_already_treated: bool) -> str:
|
|
"""Parse the dict to transform to dict"""
|
|
if dico_is_already_treated:
|
|
return "".join(dico)
|
|
return "".join([msg for msg in self.dict_to_dict(dico, level, init=True)])
|
|
|
|
def dict_to_dict(self, dico: dict, level: int, *, ori_table_datas: list = None, init: bool = False) -> str:
|
|
"""Parse the dict to transform to dict"""
|
|
msg = []
|
|
if ori_table_datas is not None:
|
|
table_datas = ori_table_datas
|
|
else:
|
|
table_datas = []
|
|
ori_level = None
|
|
for value in dico.values():
|
|
if value["type"] == "variable":
|
|
self.variable_to_string(value, table_datas)
|
|
else:
|
|
if self.with_family:
|
|
if value["type"] == "namespace":
|
|
if ori_level is None:
|
|
ori_level = level
|
|
level += 1
|
|
informations = value["informations"]
|
|
msg.append(self.namespace_to_title(informations, ori_level))
|
|
msg.append(self.family_informations())
|
|
msg.extend(self.display_paths(informations, {}))
|
|
msg.append(self.after_family_paths())
|
|
msg.append(self.property_to_string(informations, {}, {})[1] + ENTER)
|
|
msg.append(self.end_family_informations())
|
|
msg.extend(self.dict_to_dict(value["children"], level))
|
|
msg.append(self.end_namespace(ori_level))
|
|
else:
|
|
if table_datas:
|
|
msg.append(self.table(table_datas))
|
|
table_datas = []
|
|
msg.extend(self.family_to_string(value["informations"], level))
|
|
msg.extend(self.dict_to_dict(value["children"], level + 1))
|
|
msg.append(self.end_family(level))
|
|
else:
|
|
self.dict_to_dict(value["children"], level + 1, ori_table_datas=table_datas)
|
|
if (init or ori_table_datas is None) and table_datas:
|
|
msg.append(self.table(table_datas))
|
|
return msg
|
|
|
|
# FAMILY
|
|
def namespace_to_title(self, informations: dict, level: int) -> str:
|
|
"""manage namespace family"""
|
|
return self.title(
|
|
_('Variables for "{0}"').format(self.get_description("family", informations)),
|
|
level,
|
|
)
|
|
|
|
def end_namespace(self, level: int) -> str:
|
|
return self.end_family(level)
|
|
|
|
def family_to_string(self, informations: dict, level: int) -> str:
|
|
"""manage other family type"""
|
|
msg = [self.title(self.get_description("family", informations), level)]
|
|
helps = informations.get("help")
|
|
if helps:
|
|
for help_ in helps:
|
|
msg.append(self.display_family_help(help_.strip()))
|
|
msg.append(self.family_informations())
|
|
msg.append(self.join(self.display_paths(informations, {})
|
|
) + self.after_family_paths()
|
|
)
|
|
calculated_properties = []
|
|
msg.append(self.property_to_string(informations, calculated_properties, {})[1] + ENTER)
|
|
if calculated_properties:
|
|
msg.append(self.join(calculated_properties) + self.after_family_properties())
|
|
if "identifier" in informations:
|
|
msg.append(
|
|
self.section(_("Identifiers"), informations["identifier"]) + self.after_family_properties()
|
|
)
|
|
msg.append(self.end_family_informations())
|
|
return msg
|
|
|
|
def end_family(self, level: int) -> str:
|
|
return ''
|
|
|
|
def convert_list_to_string(self, attribute: str, informations: dict, modified_attributes: dict) -> str():
|
|
datas = []
|
|
if attribute in modified_attributes:
|
|
name, previous, new = modified_attributes[attribute]
|
|
for data in previous:
|
|
datas.append(self.delete(self.to_phrase(data)))
|
|
else:
|
|
new = []
|
|
if attribute in informations:
|
|
for data in informations[attribute]:
|
|
if isinstance(data, dict):
|
|
if attribute.endswith('s'):
|
|
attr = attribute[:-1]
|
|
else:
|
|
attr = attribute
|
|
data = data[attr].replace('{{ identifier }}', self.italic(data["identifier"]))
|
|
if data in new:
|
|
data = self.underline(data)
|
|
datas.append(self.to_phrase(data))
|
|
return self.stripped(self.join(datas))
|
|
|
|
def get_description(self, type_: str, informations: dict, modified_attributes: dict={}) -> str():
|
|
def _get_description(description, identifiers, delete=False, new=[]):
|
|
if "{{ identifier }}" in description:
|
|
if type_ == "variable":
|
|
identifiers_text = display_list([self.italic(i[-1]) for i in identifiers], separator="or")
|
|
description = description.replace('{{ identifier }}', identifiers_text)
|
|
else:
|
|
d = []
|
|
for i in identifiers:
|
|
new_description = description.replace('{{ identifier }}', self.italic(i[-1]))
|
|
if new_description not in d:
|
|
d.append(self.to_phrase(new_description))
|
|
description = display_list(d, separator="or")
|
|
else:
|
|
description = self.to_phrase(description)
|
|
if description in new:
|
|
description = self.underline(description)
|
|
if delete:
|
|
description = self.delete(description)
|
|
return description
|
|
if "description" in modified_attributes:
|
|
name, previous, new = modified_attributes["description"]
|
|
modified_description = _get_description(previous, modified_attributes.get("identifiers", []), delete=True)
|
|
else:
|
|
modified_description = None
|
|
new = []
|
|
description = _get_description(informations["description"], informations.get("identifiers"), new=new)
|
|
if modified_description:
|
|
if description:
|
|
description = self.join([modified_description, description])
|
|
else:
|
|
description = modified_description
|
|
if not description:
|
|
return None
|
|
return self.stripped(description)
|
|
|
|
|
|
# VARIABLE
|
|
def variable_to_string(self, informations: dict, table_datas: list, modified_attributes: dict={}) -> None:
|
|
"""Manage variable"""
|
|
calculated_properties = []
|
|
multi, first_column = self.variable_first_column(informations, calculated_properties, modified_attributes)
|
|
table_datas.append(
|
|
[
|
|
self.join(first_column),
|
|
self.join(
|
|
self.variable_second_column(informations, calculated_properties, modified_attributes, multi=multi)
|
|
),
|
|
]
|
|
)
|
|
|
|
def variable_first_column(
|
|
self, informations: dict, calculated_properties: list, modified_attributes: Optional[dict]
|
|
) -> list:
|
|
"""Collect string for the first column"""
|
|
multi, properties = self.property_to_string(informations, calculated_properties, modified_attributes)
|
|
first_col = [
|
|
self.join(
|
|
self.display_paths(informations, modified_attributes)
|
|
), properties
|
|
]
|
|
self.columns(first_col)
|
|
return multi, first_col
|
|
|
|
def variable_second_column(
|
|
self, informations: dict, calculated_properties: list, modified_attributes: dict, multi: bool
|
|
) -> list:
|
|
"""Collect string for the second column"""
|
|
second_col = []
|
|
#
|
|
if "description" in informations:
|
|
description = self.get_description("variable", informations, modified_attributes)
|
|
second_col.append(description)
|
|
#
|
|
help_ = self.convert_list_to_string("help", informations, modified_attributes)
|
|
if help_:
|
|
second_col.append(help_)
|
|
#
|
|
validators = self.convert_section_to_string("validators", informations, modified_attributes, multi=True)
|
|
if validators:
|
|
second_col.append(validators)
|
|
default_is_already_set, choices = self.convert_choices_to_string(informations, modified_attributes)
|
|
if choices:
|
|
second_col.append(choices)
|
|
if not default_is_already_set and "default" in informations:
|
|
self.convert_section_to_string("default", informations, modified_attributes, multi=multi)
|
|
second_col.append(self.convert_section_to_string("default", informations, modified_attributes, multi=multi))
|
|
examples = self.convert_section_to_string("examples", informations, modified_attributes, multi=True)
|
|
if examples:
|
|
second_col.append(examples)
|
|
second_col.extend(calculated_properties)
|
|
self.columns(second_col)
|
|
return second_col
|
|
|
|
def convert_section_to_string(self, attribute: str, informations: dict, modified_attributes: dict, multi: bool) -> str():
|
|
values = []
|
|
submessage = ""
|
|
if modified_attributes and attribute in modified_attributes:
|
|
name, previous, new = modified_attributes[attribute]
|
|
# if "identifiers" in modified_attributes:
|
|
# iname, iprevious, inew = modified_attributes["identifiers"]
|
|
# identifiers = iprevious.copy()
|
|
# for identifier in informations.get("identifiers", []):
|
|
# if identifier not in inew:
|
|
# identifiers.append(identifier)
|
|
#
|
|
# else:
|
|
# identifiers = informations.get("identifiers", [])
|
|
if isinstance(previous, list):
|
|
for p in previous:
|
|
submessage, m = self.message_to_string(p, submessage)
|
|
values.append(self.delete(m))
|
|
else:
|
|
submessage, old_values = self.message_to_string(previous, submessage)
|
|
values.append(self.delete(old_values))
|
|
else:
|
|
new = []
|
|
if attribute in informations:
|
|
old = informations[attribute]
|
|
name = old["name"]
|
|
if isinstance(old["values"], list):
|
|
for value in old["values"]:
|
|
submessage, old_value = self.message_to_string(value, submessage)
|
|
if value in new:
|
|
old_value = self.underline(old_value)
|
|
values.append(old_value)
|
|
if multi:
|
|
values = self.list(values)
|
|
else:
|
|
values = self.join(values)
|
|
elif values:
|
|
old_values = old["values"]
|
|
submessage, old_values = self.message_to_string(old_values, submessage)
|
|
if old["values"] in new:
|
|
old_values = self.underline(old_values)
|
|
values.append(old_values)
|
|
values = self.join(values)
|
|
else:
|
|
submessage, values = self.message_to_string(old["values"], submessage)
|
|
if old["values"] in new:
|
|
values = self.underline(values)
|
|
if values != []:
|
|
return self.section(name, values, submessage=submessage)
|
|
|
|
def convert_choices_to_string(self, informations: dict, modified_attributes: dict) -> str():
|
|
default_is_already_set = False
|
|
if "choices" in informations:
|
|
choices = informations["choices"]
|
|
choices_values = choices["values"]
|
|
if not isinstance(choices_values, list):
|
|
choices_values = [choices_values]
|
|
default_is_a_list = False
|
|
else:
|
|
default_is_a_list = True
|
|
if "default" in modified_attributes:
|
|
name, old_default, new_default = modified_attributes["default"]
|
|
if not old_default:
|
|
old_default = [None]
|
|
if not isinstance(old_default, list):
|
|
old_default = [old_default]
|
|
for value in old_default.copy():
|
|
if isinstance(value, str) and value.endswith(".") and value not in choices_values:
|
|
old_default.remove(value)
|
|
old_default.append(value[:-1])
|
|
else:
|
|
old_default = new_default = []
|
|
# check if all default values are in choices (could be from a calculation)
|
|
if "default" in informations:
|
|
default = informations["default"]["values"]
|
|
else:
|
|
default = []
|
|
if not isinstance(default, list):
|
|
default = [default]
|
|
default_value_not_in_choices = set(default) - set(choices_values)
|
|
if default_value_not_in_choices:
|
|
default_is_changed = False
|
|
for val in default_value_not_in_choices.copy():
|
|
if isinstance(val, str) and val.endswith('.') and val[:-1] in choices_values:
|
|
default.remove(val)
|
|
default.append(val[:-1])
|
|
default_is_changed = True
|
|
if val in new_default:
|
|
new_default.remove(val)
|
|
new_default.append(val[:-1])
|
|
if default_is_changed:
|
|
default_value_not_in_choices = set(default) - set(choices_values)
|
|
if default_value_not_in_choices:
|
|
old_default = []
|
|
new_default = []
|
|
default = []
|
|
else:
|
|
default_is_already_set = True
|
|
if "choices" in modified_attributes:
|
|
name, previous, new = modified_attributes["choices"]
|
|
for choice in reversed(previous):
|
|
if choice in old_default:
|
|
choices_values.insert(0, self.delete(dump(choice) + " ← " + _("(default)")))
|
|
else:
|
|
choices_values.insert(0, self.delete(dump(choice)))
|
|
else:
|
|
new = []
|
|
for idx, val in enumerate(choices_values):
|
|
if val in old_default:
|
|
choices_values[idx] = dump(val) + " " + self.delete("← " + _("(default)"))
|
|
elif val in default:
|
|
if val in new_default:
|
|
if val in new:
|
|
choices_values[idx] = self.underline(dump(val) + " " + self.bold("← " + _("(default)")))
|
|
else:
|
|
choices_values[idx] = dump(val) + " " + self.underline(self.bold("← " + _("(default)")))
|
|
else:
|
|
choices_values[idx] = dump(val) + " " + self.bold("← " + _("(default)"))
|
|
elif val in new:
|
|
choices_values[idx] = self.underline(dump(val))
|
|
# if old value and new value is a list, display a list
|
|
if not default_is_a_list and len(choices_values) == 1:
|
|
choices_values = choices_values[0]
|
|
return default_is_already_set, self.section(choices["name"], choices_values)
|
|
return default_is_already_set, None
|
|
|
|
# OTHERs
|
|
def to_phrase(self, text: str) -> str:
|
|
return text
|
|
|
|
def display_family_help(self, help_):
|
|
return self.to_phrase(help_) + ENTER
|
|
|
|
def property_to_string(
|
|
self, informations: dict, calculated_properties: list, modified_attributes: dict,
|
|
) -> str:
|
|
"""Transform properties to string"""
|
|
properties = []
|
|
local_calculated_properties = {}
|
|
multi = False
|
|
if "properties" in modified_attributes:
|
|
previous, new = self.get_modified_properties(*modified_attributes["properties"][1:])
|
|
for p, annotation in previous.items():
|
|
if p not in new:
|
|
properties.append(self.prop(self.delete(p), italic=False))
|
|
if annotation is not None:
|
|
local_calculated_properties[p] = [self.delete(annotation)]
|
|
|
|
else:
|
|
previous = new = []
|
|
for prop in informations.get("properties", []):
|
|
if prop["type"] == "type":
|
|
properties.append(self.link(prop["name"], ROUGAIL_VARIABLE_TYPE))
|
|
else:
|
|
if prop["type"] == "multiple":
|
|
multi = True
|
|
prop_name = prop["name"]
|
|
if "annotation" in prop:
|
|
italic = True
|
|
prop_annotation = prop["annotation"]
|
|
if prop_name in new and (prop_name not in previous or new[prop_name] != previous[prop_name]):
|
|
prop_annotation = self.underline(prop_annotation)
|
|
local_calculated_properties.setdefault(prop["name"], []).append(prop_annotation)
|
|
else:
|
|
italic = False
|
|
if prop_name not in previous and prop_name in new:
|
|
prop_name = self.underline(prop_name)
|
|
properties.append(self.prop(prop_name, italic=italic))
|
|
if local_calculated_properties:
|
|
for calculated_property_name, calculated_property in local_calculated_properties.items():
|
|
if len(calculated_property) > 1:
|
|
calculated_property = self.join(calculated_property)
|
|
else:
|
|
calculated_property = calculated_property[0]
|
|
calculated_properties.append(
|
|
self.section(calculated_property_name.capitalize(), calculated_property)
|
|
)
|
|
if not properties:
|
|
return multi, ""
|
|
return multi, " ".join(properties)
|
|
|
|
def get_modified_properties(self, previous: List[dict], new: List[dict]) -> Tuple[dict, dict]:
|
|
def modified_properties_parser(dico):
|
|
return {d["name"]: d.get("annotation") for d in dico}
|
|
return modified_properties_parser(previous), modified_properties_parser(new)
|
|
|
|
def columns(
|
|
self,
|
|
col: List[str], # pylint: disable=unused-argument
|
|
) -> None:
|
|
"""Manage column"""
|
|
return
|
|
|
|
def table(self, datas: list) -> str:
|
|
"""Transform list to a table in string format"""
|
|
msg = (
|
|
tabulate(
|
|
datas,
|
|
headers=self.table_header([_("Variable"), _("Description")]),
|
|
tablefmt=self._table_name,
|
|
)
|
|
+ "\n\n"
|
|
)
|
|
datas.clear()
|
|
return msg
|
|
|
|
def message_to_string(self, msg, ret, identifiers=[]):
|
|
if isinstance(msg, dict):
|
|
if "submessage" in msg:
|
|
ret += msg["submessage"]
|
|
msg = msg["values"]
|
|
elif "message" in msg:
|
|
path = calc_path(msg["path"], self, identifiers)
|
|
msg = msg["message"].format(path)
|
|
return ret, msg
|
|
|
|
def section(
|
|
self,
|
|
name: str,
|
|
msg: str,
|
|
submessage: str = "",
|
|
) -> str:
|
|
"""Return something like Name: msg"""
|
|
submessage, msg = self.message_to_string(msg, submessage)
|
|
if isinstance(msg, list):
|
|
if len(msg) == 1:
|
|
msg = calc_path(msg[0], self)
|
|
else:
|
|
lst = []
|
|
for p in msg:
|
|
submessage, elt = self.message_to_string(p, submessage)
|
|
lst.append(elt)
|
|
submessage += self.list(lst)
|
|
msg = ""
|
|
if not isinstance(msg, str):
|
|
submessage += dump(msg)
|
|
else:
|
|
submessage += msg
|
|
return _("{0}: {1}").format(self.bold(name), submessage)
|
|
|
|
|
|
def calc_path(path, formater=None, identifiers: List[str]=None) -> str:
|
|
def _path_with_identifier(path, identifier):
|
|
identifier = normalize_family(str(identifier))
|
|
if formater:
|
|
identifier = formater.italic(identifier)
|
|
return path.replace('{{ identifier }}', identifier, 1)
|
|
if isinstance(path, dict):
|
|
path_ = path["path"]
|
|
for identifier in path["identifiers"]:
|
|
path_ = _path_with_identifier(path_, identifier)
|
|
elif identifiers:
|
|
path_ = path
|
|
for identifier in identifiers:
|
|
path_ = _path_with_identifier(path_, identifier)
|
|
else:
|
|
path_ = path
|
|
return path_
|