2024-11-15 08:13:45 +01:00
|
|
|
"""
|
|
|
|
|
Silique (https://www.silique.fr)
|
2025-02-10 09:52:12 +01:00
|
|
|
Copyright (C) 2024-2025
|
2024-11-15 08:13:45 +01:00
|
|
|
|
|
|
|
|
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/>.
|
|
|
|
|
"""
|
|
|
|
|
|
2025-10-14 12:58:39 +02:00
|
|
|
from typing import Tuple, List, Optional
|
2024-11-15 08:13:45 +01:00
|
|
|
|
|
|
|
|
from io import BytesIO
|
|
|
|
|
from ruamel.yaml import YAML
|
|
|
|
|
import tabulate as tabulate_module
|
|
|
|
|
from tiramisu.error import display_list
|
|
|
|
|
from tabulate import tabulate
|
|
|
|
|
|
2025-10-14 12:58:39 +02:00
|
|
|
from rougail.tiramisu import normalize_family
|
|
|
|
|
from tiramisu import undefined
|
|
|
|
|
|
2024-11-15 08:13:45 +01:00
|
|
|
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}"),
|
|
|
|
|
},
|
|
|
|
|
},
|
2025-09-23 22:09:33 +02:00
|
|
|
"integer": {
|
|
|
|
|
"params": {
|
|
|
|
|
"min_integer": _("the minimum value is {0}"),
|
|
|
|
|
"max_integer": _("the maximum value is {0}"),
|
|
|
|
|
},
|
|
|
|
|
},
|
2024-11-15 08:13:45 +01:00
|
|
|
"ip": {
|
|
|
|
|
"msg": "IP",
|
|
|
|
|
"params": {
|
|
|
|
|
"cidr": _("IP must be in CIDR format"),
|
|
|
|
|
"private_only": _("private IP are allowed"),
|
|
|
|
|
"allow_reserved": _("reserved IP are allowed"),
|
|
|
|
|
},
|
2025-09-29 21:26:12 +02:00
|
|
|
},
|
|
|
|
|
"network": {
|
|
|
|
|
"params": {
|
|
|
|
|
"cidr": _("network must be in CIDR format"),
|
|
|
|
|
},
|
2024-11-15 08:13:45 +01:00
|
|
|
},
|
|
|
|
|
"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"),
|
2025-03-29 14:37:45 +01:00
|
|
|
"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"),
|
2024-11-15 08:13:45 +01:00
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
"secret": {
|
|
|
|
|
"params": {
|
2025-03-29 14:37:45 +01:00
|
|
|
"min_len": _("minimum length for the secret is {0} characters"),
|
|
|
|
|
"max_len": _("maximum length for the secret is {0} characters"),
|
2025-03-31 10:25:08 +02:00
|
|
|
"forbidden_char": _("forbidden characters: {0}"),
|
2024-11-15 08:13:45 +01:00
|
|
|
},
|
|
|
|
|
},
|
2025-03-30 19:47:56 +02:00
|
|
|
"unix_filename": {
|
|
|
|
|
"params": {
|
|
|
|
|
"allow_relative": _("this filename could be a relative path"),
|
|
|
|
|
"test_existence": _("this file must exists"),
|
|
|
|
|
"types": _("file type allowed: {0}"),
|
|
|
|
|
},
|
|
|
|
|
},
|
2024-11-15 08:13:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_yaml = YAML()
|
|
|
|
|
_yaml.indent(mapping=2, sequence=4, offset=2)
|
|
|
|
|
|
|
|
|
|
|
2025-03-02 13:38:14 +01:00
|
|
|
def dump(informations):
|
2024-11-15 08:13:45 +01:00
|
|
|
"""Dump variable, means transform bool, ... to yaml string"""
|
|
|
|
|
with BytesIO() as ymlfh:
|
2025-03-02 13:38:14 +01:00
|
|
|
_yaml.dump(informations, ymlfh)
|
2024-11-15 08:13:45 +01:00
|
|
|
ret = ymlfh.getvalue().decode("utf-8").strip()
|
|
|
|
|
if ret.endswith("..."):
|
|
|
|
|
ret = ret[:-3].strip()
|
|
|
|
|
return ret
|
|
|
|
|
|
|
|
|
|
|
2025-10-14 12:58:39 +02:00
|
|
|
def to_phrase(msg, type_="variable"):
|
2025-10-15 09:44:22 +02:00
|
|
|
"""Add maj for the first character and ends with dot"""
|
2024-11-15 08:13:45 +01:00
|
|
|
if not msg:
|
|
|
|
|
# replace None to empty string
|
|
|
|
|
return ""
|
|
|
|
|
msg = str(msg).strip()
|
|
|
|
|
# a phrase must ends with a dot
|
2025-10-15 09:44:22 +02:00
|
|
|
if type_ == "variable":
|
2025-10-14 12:58:39 +02:00
|
|
|
if not msg.endswith("."):
|
|
|
|
|
msg += "."
|
2025-10-15 09:44:22 +02:00
|
|
|
elif type_ == "family":
|
2025-10-14 12:58:39 +02:00
|
|
|
if msg.endswith("."):
|
|
|
|
|
msg = msg[:-1]
|
|
|
|
|
else:
|
2025-10-15 09:44:22 +02:00
|
|
|
raise Exception("unknown type")
|
2024-11-15 08:13:45 +01:00
|
|
|
# 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
|
|
|
|
|
|
2025-10-14 12:58:39 +02:00
|
|
|
def __init__(self, with_family: bool):
|
2024-11-15 08:13:45 +01:00
|
|
|
tabulate_module.PRESERVE_WHITESPACE = True
|
|
|
|
|
self.header_setted = False
|
2025-10-14 12:58:39 +02:00
|
|
|
self.with_family = with_family
|
2024-11-15 08:13:45 +01:00
|
|
|
|
|
|
|
|
# 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()
|
|
|
|
|
|
|
|
|
|
##################
|
|
|
|
|
|
2025-10-02 08:19:18 +02:00
|
|
|
def family_informations(self) -> str:
|
2025-10-15 09:44:22 +02:00
|
|
|
return ""
|
2025-10-02 08:19:18 +02:00
|
|
|
|
|
|
|
|
def end_family_informations(self) -> str:
|
2025-10-15 09:44:22 +02:00
|
|
|
return ""
|
2025-10-02 08:19:18 +02:00
|
|
|
|
2025-10-14 12:58:39 +02:00
|
|
|
def display_paths(
|
2025-10-02 08:19:18 +02:00
|
|
|
self,
|
2025-10-14 12:58:39 +02:00
|
|
|
informations: dict,
|
|
|
|
|
modified_attributes: dict,
|
2025-10-15 09:53:54 +02:00
|
|
|
force_identifiers: Optional[str],
|
2025-10-02 08:19:18 +02:00
|
|
|
) -> str:
|
2025-10-14 12:58:39 +02:00
|
|
|
ret_paths = []
|
|
|
|
|
path = informations["path"]
|
|
|
|
|
if "identifiers" in modified_attributes:
|
|
|
|
|
name, previous, new = modified_attributes["identifiers"]
|
2025-10-15 09:44:22 +02:00
|
|
|
ret_paths.extend(
|
|
|
|
|
[
|
|
|
|
|
self.bold(self.delete(calc_path(path, self, identifier)))
|
|
|
|
|
for identifier in previous
|
|
|
|
|
]
|
|
|
|
|
)
|
2025-10-14 12:58:39 +02:00
|
|
|
else:
|
|
|
|
|
new = []
|
|
|
|
|
if "identifiers" in informations:
|
|
|
|
|
for identifier in informations["identifiers"]:
|
2025-10-15 09:53:54 +02:00
|
|
|
if force_identifiers and identifier != force_identifiers:
|
|
|
|
|
continue
|
2025-10-14 12:58:39 +02:00
|
|
|
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
|
2025-10-02 08:19:18 +02:00
|
|
|
|
|
|
|
|
def after_family_paths(self) -> str:
|
|
|
|
|
return ENTER
|
|
|
|
|
|
|
|
|
|
def after_family_properties(self) -> str:
|
|
|
|
|
return ENTER
|
|
|
|
|
|
2024-11-15 08:13:45 +01:00
|
|
|
def table_header(
|
|
|
|
|
self,
|
|
|
|
|
lst: list,
|
|
|
|
|
) -> tuple:
|
|
|
|
|
"""Manage the header of a table"""
|
|
|
|
|
return lst
|
|
|
|
|
|
2025-10-15 09:44:22 +02:00
|
|
|
def run(
|
|
|
|
|
self, informations: dict, level: int, *, dico_is_already_treated=False
|
|
|
|
|
) -> str:
|
2024-11-15 08:13:45 +01:00
|
|
|
"""Transform to string"""
|
2025-03-02 13:38:14 +01:00
|
|
|
if informations:
|
2025-10-14 12:58:39 +02:00
|
|
|
return self._run(informations, level, dico_is_already_treated)
|
2025-03-29 15:10:03 +01:00
|
|
|
return ""
|
2024-11-15 08:13:45 +01:00
|
|
|
|
2025-10-14 12:58:39 +02:00
|
|
|
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)])
|
|
|
|
|
|
2025-10-15 09:44:22 +02:00
|
|
|
def dict_to_dict(
|
|
|
|
|
self,
|
|
|
|
|
dico: dict,
|
|
|
|
|
level: int,
|
|
|
|
|
*,
|
|
|
|
|
ori_table_datas: list = None,
|
|
|
|
|
init: bool = False,
|
|
|
|
|
) -> str:
|
2024-11-15 08:13:45 +01:00
|
|
|
"""Parse the dict to transform to dict"""
|
2025-03-29 15:10:03 +01:00
|
|
|
msg = []
|
2025-10-14 12:58:39 +02:00
|
|
|
if ori_table_datas is not None:
|
|
|
|
|
table_datas = ori_table_datas
|
|
|
|
|
else:
|
|
|
|
|
table_datas = []
|
2025-02-19 08:39:44 +01:00
|
|
|
ori_level = None
|
2025-03-29 15:10:03 +01:00
|
|
|
for value in dico.values():
|
2025-10-14 12:58:39 +02:00
|
|
|
if value["type"] == "variable":
|
|
|
|
|
self.variable_to_string(value, table_datas)
|
2024-11-15 08:13:45 +01:00
|
|
|
else:
|
2025-10-14 12:58:39 +02:00
|
|
|
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())
|
2025-10-15 09:53:54 +02:00
|
|
|
msg.extend(self.display_paths(informations, {}, None))
|
2025-10-14 12:58:39 +02:00
|
|
|
msg.append(self.after_family_paths())
|
2025-10-15 09:44:22 +02:00
|
|
|
msg.append(
|
|
|
|
|
self.property_to_string(informations, {}, {})[1] + ENTER
|
|
|
|
|
)
|
2025-10-14 12:58:39 +02:00
|
|
|
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))
|
2024-11-15 08:13:45 +01:00
|
|
|
else:
|
2025-10-15 09:44:22 +02:00
|
|
|
self.dict_to_dict(
|
|
|
|
|
value["children"], level + 1, ori_table_datas=table_datas
|
|
|
|
|
)
|
2025-10-14 12:58:39 +02:00
|
|
|
if (init or ori_table_datas is None) and table_datas:
|
2025-03-29 15:10:03 +01:00
|
|
|
msg.append(self.table(table_datas))
|
2024-11-15 08:13:45 +01:00
|
|
|
return msg
|
|
|
|
|
|
|
|
|
|
# FAMILY
|
2025-03-29 15:10:03 +01:00
|
|
|
def namespace_to_title(self, informations: dict, level: int) -> str:
|
2024-11-15 08:13:45 +01:00
|
|
|
"""manage namespace family"""
|
|
|
|
|
return self.title(
|
2025-10-15 09:44:22 +02:00
|
|
|
_('Variables for "{0}"').format(
|
2025-10-15 09:53:54 +02:00
|
|
|
self.get_description("family", informations, {}, None)
|
2025-10-15 09:44:22 +02:00
|
|
|
),
|
2025-03-02 13:38:14 +01:00
|
|
|
level,
|
2024-11-15 08:13:45 +01:00
|
|
|
)
|
|
|
|
|
|
2025-10-02 22:18:22 +02:00
|
|
|
def end_namespace(self, level: int) -> str:
|
|
|
|
|
return self.end_family(level)
|
2025-10-02 08:19:18 +02:00
|
|
|
|
2025-03-02 13:38:14 +01:00
|
|
|
def family_to_string(self, informations: dict, level: int) -> str:
|
2024-11-15 08:13:45 +01:00
|
|
|
"""manage other family type"""
|
2025-10-15 09:53:54 +02:00
|
|
|
msg = [self.title(self.get_description("family", informations, {}, None), level)]
|
2025-03-02 13:38:14 +01:00
|
|
|
helps = informations.get("help")
|
2024-11-15 08:13:45 +01:00
|
|
|
if helps:
|
|
|
|
|
for help_ in helps:
|
2025-10-02 08:19:18 +02:00
|
|
|
msg.append(self.display_family_help(help_.strip()))
|
|
|
|
|
msg.append(self.family_informations())
|
2025-10-15 09:44:22 +02:00
|
|
|
msg.append(
|
2025-10-15 09:53:54 +02:00
|
|
|
self.join(self.display_paths(informations, {}, None)) + self.after_family_paths()
|
2025-10-02 08:19:18 +02:00
|
|
|
)
|
|
|
|
|
calculated_properties = []
|
2025-10-15 09:44:22 +02:00
|
|
|
msg.append(
|
|
|
|
|
self.property_to_string(informations, calculated_properties, {})[1] + ENTER
|
|
|
|
|
)
|
2025-10-02 08:19:18 +02:00
|
|
|
if calculated_properties:
|
2025-10-15 09:44:22 +02:00
|
|
|
msg.append(
|
|
|
|
|
self.join(calculated_properties) + self.after_family_properties()
|
|
|
|
|
)
|
2025-10-14 12:58:39 +02:00
|
|
|
if "identifier" in informations:
|
2025-05-11 19:13:12 +02:00
|
|
|
msg.append(
|
2025-10-15 09:44:22 +02:00
|
|
|
self.section(_("Identifiers"), informations["identifier"])
|
|
|
|
|
+ self.after_family_properties()
|
2025-05-11 19:13:12 +02:00
|
|
|
)
|
2025-10-02 08:19:18 +02:00
|
|
|
msg.append(self.end_family_informations())
|
2024-11-15 08:13:45 +01:00
|
|
|
return msg
|
|
|
|
|
|
2025-10-02 22:18:22 +02:00
|
|
|
def end_family(self, level: int) -> str:
|
2025-10-15 09:44:22 +02:00
|
|
|
return ""
|
2025-10-02 08:19:18 +02:00
|
|
|
|
2025-10-15 09:44:22 +02:00
|
|
|
def convert_list_to_string(
|
|
|
|
|
self, attribute: str, informations: dict, modified_attributes: dict
|
|
|
|
|
) -> str():
|
2025-10-14 12:58:39 +02:00
|
|
|
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):
|
2025-10-15 09:44:22 +02:00
|
|
|
if attribute.endswith("s"):
|
2025-10-14 12:58:39 +02:00
|
|
|
attr = attribute[:-1]
|
|
|
|
|
else:
|
|
|
|
|
attr = attribute
|
2025-10-15 09:44:22 +02:00
|
|
|
data = data[attr].replace(
|
|
|
|
|
"{{ identifier }}", self.italic(data["identifier"])
|
|
|
|
|
)
|
2025-10-14 13:50:02 +02:00
|
|
|
data = self.to_phrase(data)
|
2025-10-14 12:58:39 +02:00
|
|
|
if data in new:
|
|
|
|
|
data = self.underline(data)
|
2025-10-14 13:50:02 +02:00
|
|
|
datas.append(data)
|
2025-10-14 12:58:39 +02:00
|
|
|
return self.stripped(self.join(datas))
|
|
|
|
|
|
2025-10-15 09:44:22 +02:00
|
|
|
def get_description(
|
2025-10-15 09:53:54 +02:00
|
|
|
self, type_: str, informations: dict, modified_attributes: dict, force_identifiers: Optional[str]
|
2025-10-15 09:44:22 +02:00
|
|
|
) -> str():
|
2025-10-14 12:58:39 +02:00
|
|
|
def _get_description(description, identifiers, delete=False, new=[]):
|
|
|
|
|
if "{{ identifier }}" in description:
|
|
|
|
|
if type_ == "variable":
|
2025-10-15 09:44:22 +02:00
|
|
|
identifiers_text = display_list(
|
2025-10-15 09:53:54 +02:00
|
|
|
[self.italic(i[-1]) for i in identifiers if not force_identifiers or i == force_identifiers], separator="or"
|
2025-10-15 09:44:22 +02:00
|
|
|
)
|
|
|
|
|
description = description.replace(
|
|
|
|
|
"{{ identifier }}", identifiers_text
|
|
|
|
|
)
|
2025-10-14 12:58:39 +02:00
|
|
|
else:
|
|
|
|
|
d = []
|
|
|
|
|
for i in identifiers:
|
2025-10-15 09:53:54 +02:00
|
|
|
if force_identifiers and i != force_identifiers:
|
|
|
|
|
continue
|
2025-10-15 09:44:22 +02:00
|
|
|
new_description = description.replace(
|
|
|
|
|
"{{ identifier }}", self.italic(i[-1])
|
|
|
|
|
)
|
2025-10-14 12:58:39 +02:00
|
|
|
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
|
2025-10-15 09:44:22 +02:00
|
|
|
|
2025-10-14 12:58:39 +02:00
|
|
|
if "description" in modified_attributes:
|
|
|
|
|
name, previous, new = modified_attributes["description"]
|
2025-10-15 09:44:22 +02:00
|
|
|
modified_description = _get_description(
|
|
|
|
|
previous, modified_attributes.get("identifiers", []), delete=True
|
|
|
|
|
)
|
2025-10-14 12:58:39 +02:00
|
|
|
else:
|
|
|
|
|
modified_description = None
|
|
|
|
|
new = []
|
2025-10-15 09:44:22 +02:00
|
|
|
description = _get_description(
|
|
|
|
|
informations["description"], informations.get("identifiers"), new=new
|
|
|
|
|
)
|
2025-10-14 12:58:39 +02:00
|
|
|
if modified_description:
|
|
|
|
|
if description:
|
|
|
|
|
description = self.join([modified_description, description])
|
|
|
|
|
else:
|
|
|
|
|
description = modified_description
|
|
|
|
|
if not description:
|
|
|
|
|
return None
|
|
|
|
|
return self.stripped(description)
|
|
|
|
|
|
2024-11-15 08:13:45 +01:00
|
|
|
# VARIABLE
|
2025-10-15 09:44:22 +02:00
|
|
|
def variable_to_string(
|
2025-10-15 09:53:54 +02:00
|
|
|
self, informations: dict, table_datas: list, modified_attributes: dict = {}, force_identifiers: Optional[str]=None
|
2025-10-15 09:44:22 +02:00
|
|
|
) -> None:
|
2024-11-15 08:13:45 +01:00
|
|
|
"""Manage variable"""
|
|
|
|
|
calculated_properties = []
|
2025-10-15 09:44:22 +02:00
|
|
|
multi, first_column = self.variable_first_column(
|
2025-10-15 09:53:54 +02:00
|
|
|
informations, calculated_properties, modified_attributes, force_identifiers
|
2025-10-15 09:44:22 +02:00
|
|
|
)
|
2024-11-15 08:13:45 +01:00
|
|
|
table_datas.append(
|
|
|
|
|
[
|
2025-10-14 12:58:39 +02:00
|
|
|
self.join(first_column),
|
2025-03-02 13:38:14 +01:00
|
|
|
self.join(
|
2025-10-15 09:44:22 +02:00
|
|
|
self.variable_second_column(
|
|
|
|
|
informations,
|
|
|
|
|
calculated_properties,
|
|
|
|
|
modified_attributes,
|
2025-10-15 09:53:54 +02:00
|
|
|
multi,
|
|
|
|
|
force_identifiers,
|
2025-10-15 09:44:22 +02:00
|
|
|
)
|
2025-03-02 13:38:14 +01:00
|
|
|
),
|
2024-11-15 08:13:45 +01:00
|
|
|
]
|
|
|
|
|
)
|
|
|
|
|
|
2025-03-02 13:38:14 +01:00
|
|
|
def variable_first_column(
|
2025-10-15 09:44:22 +02:00
|
|
|
self,
|
|
|
|
|
informations: dict,
|
|
|
|
|
calculated_properties: list,
|
|
|
|
|
modified_attributes: Optional[dict],
|
2025-10-15 09:53:54 +02:00
|
|
|
force_identifiers: Optional[str],
|
2025-03-02 13:38:14 +01:00
|
|
|
) -> list:
|
2024-11-15 08:13:45 +01:00
|
|
|
"""Collect string for the first column"""
|
2025-10-15 09:44:22 +02:00
|
|
|
multi, properties = self.property_to_string(
|
|
|
|
|
informations, calculated_properties, modified_attributes
|
|
|
|
|
)
|
2024-11-15 08:13:45 +01:00
|
|
|
first_col = [
|
2025-10-15 09:53:54 +02:00
|
|
|
self.join(self.display_paths(informations, modified_attributes, force_identifiers)),
|
2025-10-15 09:44:22 +02:00
|
|
|
properties,
|
2024-11-15 08:13:45 +01:00
|
|
|
]
|
|
|
|
|
self.columns(first_col)
|
2025-10-14 12:58:39 +02:00
|
|
|
return multi, first_col
|
2024-11-15 08:13:45 +01:00
|
|
|
|
2025-03-02 13:38:14 +01:00
|
|
|
def variable_second_column(
|
2025-10-15 09:44:22 +02:00
|
|
|
self,
|
|
|
|
|
informations: dict,
|
|
|
|
|
calculated_properties: list,
|
|
|
|
|
modified_attributes: dict,
|
|
|
|
|
multi: bool,
|
2025-10-15 09:53:54 +02:00
|
|
|
force_identifiers: Optional[str],
|
2025-03-02 13:38:14 +01:00
|
|
|
) -> list:
|
2024-11-15 08:13:45 +01:00
|
|
|
"""Collect string for the second column"""
|
2025-10-14 12:58:39 +02:00
|
|
|
second_col = []
|
|
|
|
|
#
|
|
|
|
|
if "description" in informations:
|
2025-10-15 09:44:22 +02:00
|
|
|
description = self.get_description(
|
2025-10-15 09:53:54 +02:00
|
|
|
"variable", informations, modified_attributes, force_identifiers,
|
2025-10-15 09:44:22 +02:00
|
|
|
)
|
2025-10-14 12:58:39 +02:00
|
|
|
second_col.append(description)
|
|
|
|
|
#
|
|
|
|
|
help_ = self.convert_list_to_string("help", informations, modified_attributes)
|
|
|
|
|
if help_:
|
|
|
|
|
second_col.append(help_)
|
|
|
|
|
#
|
2025-10-15 09:44:22 +02:00
|
|
|
validators = self.convert_section_to_string(
|
|
|
|
|
"validators", informations, modified_attributes, multi=True
|
|
|
|
|
)
|
2025-10-14 12:58:39 +02:00
|
|
|
if validators:
|
|
|
|
|
second_col.append(validators)
|
2025-10-15 09:44:22 +02:00
|
|
|
default_is_already_set, choices = self.convert_choices_to_string(
|
|
|
|
|
informations, modified_attributes
|
|
|
|
|
)
|
2025-10-14 12:58:39 +02:00
|
|
|
if choices:
|
|
|
|
|
second_col.append(choices)
|
|
|
|
|
if not default_is_already_set and "default" in informations:
|
2025-10-15 09:44:22 +02:00
|
|
|
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
|
|
|
|
|
)
|
2025-10-14 12:58:39 +02:00
|
|
|
if examples:
|
|
|
|
|
second_col.append(examples)
|
|
|
|
|
second_col.extend(calculated_properties)
|
|
|
|
|
self.columns(second_col)
|
|
|
|
|
return second_col
|
|
|
|
|
|
2025-10-15 09:44:22 +02:00
|
|
|
def convert_section_to_string(
|
|
|
|
|
self, attribute: str, informations: dict, modified_attributes: dict, multi: bool
|
|
|
|
|
) -> str():
|
2025-10-14 12:58:39 +02:00
|
|
|
values = []
|
|
|
|
|
submessage = ""
|
|
|
|
|
if modified_attributes and attribute in modified_attributes:
|
|
|
|
|
name, previous, new = modified_attributes[attribute]
|
2025-10-15 09:44:22 +02:00
|
|
|
# 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", [])
|
2025-10-14 12:58:39 +02:00
|
|
|
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))
|
2024-11-15 08:13:45 +01:00
|
|
|
else:
|
2025-10-14 12:58:39 +02:00
|
|
|
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)
|
2024-11-15 08:13:45 +01:00
|
|
|
else:
|
2025-10-14 12:58:39 +02:00
|
|
|
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)
|
|
|
|
|
|
2025-10-15 09:44:22 +02:00
|
|
|
def convert_choices_to_string(
|
|
|
|
|
self, informations: dict, modified_attributes: dict
|
|
|
|
|
) -> str():
|
2025-10-14 12:58:39 +02:00
|
|
|
default_is_already_set = False
|
2025-03-02 13:38:14 +01:00
|
|
|
if "choices" in informations:
|
2025-09-22 09:42:46 +02:00
|
|
|
choices = informations["choices"]
|
2025-10-14 12:58:39 +02:00
|
|
|
choices_values = choices["values"]
|
|
|
|
|
if not isinstance(choices_values, list):
|
|
|
|
|
choices_values = [choices_values]
|
|
|
|
|
default_is_a_list = False
|
2024-11-15 08:13:45 +01:00
|
|
|
else:
|
2025-10-14 12:58:39 +02:00
|
|
|
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():
|
2025-10-15 09:44:22 +02:00
|
|
|
if (
|
|
|
|
|
isinstance(value, str)
|
|
|
|
|
and value.endswith(".")
|
|
|
|
|
and value not in choices_values
|
|
|
|
|
):
|
2025-10-14 12:58:39 +02:00
|
|
|
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():
|
2025-10-15 09:44:22 +02:00
|
|
|
if (
|
|
|
|
|
isinstance(val, str)
|
|
|
|
|
and val.endswith(".")
|
|
|
|
|
and val[:-1] in choices_values
|
|
|
|
|
):
|
2025-10-14 12:58:39 +02:00
|
|
|
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:
|
2025-10-15 09:44:22 +02:00
|
|
|
choices_values.insert(
|
|
|
|
|
0, self.delete(dump(choice) + " ← " + _("(default)"))
|
|
|
|
|
)
|
2025-10-14 12:58:39 +02:00
|
|
|
else:
|
|
|
|
|
choices_values.insert(0, self.delete(dump(choice)))
|
|
|
|
|
else:
|
|
|
|
|
new = []
|
|
|
|
|
for idx, val in enumerate(choices_values):
|
|
|
|
|
if val in old_default:
|
2025-10-15 09:44:22 +02:00
|
|
|
choices_values[idx] = (
|
|
|
|
|
dump(val) + " " + self.delete("← " + _("(default)"))
|
|
|
|
|
)
|
2025-10-14 12:58:39 +02:00
|
|
|
elif val in default:
|
|
|
|
|
if val in new_default:
|
|
|
|
|
if val in new:
|
2025-10-15 09:44:22 +02:00
|
|
|
choices_values[idx] = self.underline(
|
|
|
|
|
dump(val) + " " + self.bold("← " + _("(default)"))
|
|
|
|
|
)
|
2025-10-14 12:58:39 +02:00
|
|
|
else:
|
2025-10-15 09:44:22 +02:00
|
|
|
choices_values[idx] = (
|
|
|
|
|
dump(val)
|
|
|
|
|
+ " "
|
|
|
|
|
+ self.underline(self.bold("← " + _("(default)")))
|
|
|
|
|
)
|
2025-10-14 12:58:39 +02:00
|
|
|
else:
|
2025-10-15 09:44:22 +02:00
|
|
|
choices_values[idx] = (
|
|
|
|
|
dump(val) + " " + self.bold("← " + _("(default)"))
|
|
|
|
|
)
|
2025-10-14 12:58:39 +02:00
|
|
|
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
|
2024-11-15 08:13:45 +01:00
|
|
|
|
|
|
|
|
# OTHERs
|
2025-10-14 12:58:39 +02:00
|
|
|
def to_phrase(self, text: str) -> str:
|
|
|
|
|
return text
|
|
|
|
|
|
2025-10-02 08:19:18 +02:00
|
|
|
def display_family_help(self, help_):
|
2025-10-14 12:58:39 +02:00
|
|
|
return self.to_phrase(help_) + ENTER
|
2025-10-02 08:19:18 +02:00
|
|
|
|
2025-03-02 13:38:14 +01:00
|
|
|
def property_to_string(
|
2025-10-15 09:44:22 +02:00
|
|
|
self,
|
|
|
|
|
informations: dict,
|
|
|
|
|
calculated_properties: list,
|
|
|
|
|
modified_attributes: dict,
|
2025-03-02 13:38:14 +01:00
|
|
|
) -> str:
|
2024-11-15 08:13:45 +01:00
|
|
|
"""Transform properties to string"""
|
|
|
|
|
properties = []
|
2025-10-14 12:58:39 +02:00
|
|
|
local_calculated_properties = {}
|
|
|
|
|
multi = False
|
|
|
|
|
if "properties" in modified_attributes:
|
2025-10-15 09:44:22 +02:00
|
|
|
previous, new = self.get_modified_properties(
|
|
|
|
|
*modified_attributes["properties"][1:]
|
|
|
|
|
)
|
2025-10-14 12:58:39 +02:00
|
|
|
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 = []
|
2025-03-02 13:38:14 +01:00
|
|
|
for prop in informations.get("properties", []):
|
2024-11-20 21:12:56 +01:00
|
|
|
if prop["type"] == "type":
|
2024-11-15 08:13:45 +01:00
|
|
|
properties.append(self.link(prop["name"], ROUGAIL_VARIABLE_TYPE))
|
|
|
|
|
else:
|
2025-10-14 12:58:39 +02:00
|
|
|
if prop["type"] == "multiple":
|
|
|
|
|
multi = True
|
|
|
|
|
prop_name = prop["name"]
|
2024-11-15 08:13:45 +01:00
|
|
|
if "annotation" in prop:
|
|
|
|
|
italic = True
|
2025-10-14 12:58:39 +02:00
|
|
|
prop_annotation = prop["annotation"]
|
2025-10-15 09:44:22 +02:00
|
|
|
if prop_name in new and (
|
|
|
|
|
prop_name not in previous
|
|
|
|
|
or new[prop_name] != previous[prop_name]
|
|
|
|
|
):
|
2025-10-14 12:58:39 +02:00
|
|
|
prop_annotation = self.underline(prop_annotation)
|
2025-10-15 09:44:22 +02:00
|
|
|
local_calculated_properties.setdefault(prop["name"], []).append(
|
|
|
|
|
prop_annotation
|
|
|
|
|
)
|
2024-11-15 08:13:45 +01:00
|
|
|
else:
|
|
|
|
|
italic = False
|
2025-10-14 12:58:39 +02:00
|
|
|
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:
|
2025-10-15 09:44:22 +02:00
|
|
|
for (
|
|
|
|
|
calculated_property_name,
|
|
|
|
|
calculated_property,
|
|
|
|
|
) in local_calculated_properties.items():
|
2025-10-14 12:58:39 +02:00
|
|
|
if len(calculated_property) > 1:
|
|
|
|
|
calculated_property = self.join(calculated_property)
|
|
|
|
|
else:
|
|
|
|
|
calculated_property = calculated_property[0]
|
|
|
|
|
calculated_properties.append(
|
2025-10-15 09:44:22 +02:00
|
|
|
self.section(
|
|
|
|
|
calculated_property_name.capitalize(), calculated_property
|
|
|
|
|
)
|
2025-10-14 12:58:39 +02:00
|
|
|
)
|
2024-11-15 08:13:45 +01:00
|
|
|
if not properties:
|
2025-10-14 12:58:39 +02:00
|
|
|
return multi, ""
|
|
|
|
|
return multi, " ".join(properties)
|
|
|
|
|
|
2025-10-15 09:44:22 +02:00
|
|
|
def get_modified_properties(
|
|
|
|
|
self, previous: List[dict], new: List[dict]
|
|
|
|
|
) -> Tuple[dict, dict]:
|
2025-10-14 12:58:39 +02:00
|
|
|
def modified_properties_parser(dico):
|
|
|
|
|
return {d["name"]: d.get("annotation") for d in dico}
|
2025-10-15 09:44:22 +02:00
|
|
|
|
2025-10-14 12:58:39 +02:00
|
|
|
return modified_properties_parser(previous), modified_properties_parser(new)
|
2024-11-15 08:13:45 +01:00
|
|
|
|
|
|
|
|
def columns(
|
|
|
|
|
self,
|
|
|
|
|
col: List[str], # pylint: disable=unused-argument
|
|
|
|
|
) -> None:
|
|
|
|
|
"""Manage column"""
|
|
|
|
|
return
|
|
|
|
|
|
2025-10-15 09:44:22 +02:00
|
|
|
def table(self, datas: list, with_header: bool = True) -> str:
|
2024-11-15 08:13:45 +01:00
|
|
|
"""Transform list to a table in string format"""
|
2025-10-14 16:42:39 +02:00
|
|
|
if with_header:
|
|
|
|
|
headers = self.table_header([_("Variable"), _("Description")])
|
|
|
|
|
else:
|
|
|
|
|
headers = ()
|
2024-11-15 08:13:45 +01:00
|
|
|
msg = (
|
|
|
|
|
tabulate(
|
|
|
|
|
datas,
|
2025-10-14 16:42:39 +02:00
|
|
|
headers=headers,
|
2024-11-20 21:12:56 +01:00
|
|
|
tablefmt=self._table_name,
|
2024-11-15 08:13:45 +01:00
|
|
|
)
|
|
|
|
|
+ "\n\n"
|
|
|
|
|
)
|
|
|
|
|
datas.clear()
|
|
|
|
|
return msg
|
|
|
|
|
|
2025-10-14 12:58:39 +02:00
|
|
|
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
|
|
|
|
|
|
2024-11-15 08:13:45 +01:00
|
|
|
def section(
|
|
|
|
|
self,
|
|
|
|
|
name: str,
|
|
|
|
|
msg: str,
|
2025-10-14 12:58:39 +02:00
|
|
|
submessage: str = "",
|
2024-11-15 08:13:45 +01:00
|
|
|
) -> str:
|
|
|
|
|
"""Return something like Name: msg"""
|
2025-10-14 12:58:39 +02:00
|
|
|
submessage, msg = self.message_to_string(msg, submessage)
|
2024-11-15 08:13:45 +01:00
|
|
|
if isinstance(msg, list):
|
|
|
|
|
if len(msg) == 1:
|
2025-10-16 20:58:39 +02:00
|
|
|
submessage, elt = self.message_to_string(msg[0], submessage)
|
2025-10-18 06:40:23 +02:00
|
|
|
if isinstance(elt, list):
|
|
|
|
|
submessage += self.list(elt)
|
|
|
|
|
else:
|
|
|
|
|
submessage += elt
|
2024-11-15 08:13:45 +01:00
|
|
|
else:
|
2025-10-14 12:58:39 +02:00
|
|
|
lst = []
|
|
|
|
|
for p in msg:
|
|
|
|
|
submessage, elt = self.message_to_string(p, submessage)
|
|
|
|
|
lst.append(elt)
|
|
|
|
|
submessage += self.list(lst)
|
2025-10-16 20:58:39 +02:00
|
|
|
msg = ""
|
2024-11-15 08:13:45 +01:00
|
|
|
if not isinstance(msg, str):
|
2025-10-14 12:58:39 +02:00
|
|
|
submessage += dump(msg)
|
|
|
|
|
else:
|
|
|
|
|
submessage += msg
|
|
|
|
|
return _("{0}: {1}").format(self.bold(name), submessage)
|
|
|
|
|
|
|
|
|
|
|
2025-10-15 09:44:22 +02:00
|
|
|
def calc_path(path, formater=None, identifiers: List[str] = None) -> str:
|
2025-10-14 12:58:39 +02:00
|
|
|
def _path_with_identifier(path, identifier):
|
|
|
|
|
identifier = normalize_family(str(identifier))
|
|
|
|
|
if formater:
|
|
|
|
|
identifier = formater.italic(identifier)
|
2025-10-15 09:44:22 +02:00
|
|
|
return path.replace("{{ identifier }}", identifier, 1)
|
|
|
|
|
|
2025-10-14 12:58:39 +02:00
|
|
|
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_
|