rougail-output-doc/src/rougail/output_doc/utils.py

1449 lines
52 KiB
Python
Raw Normal View History

2024-11-15 08:13:45 +01:00
"""
Silique (https://www.silique.fr)
Copyright (C) 2024-2026
2026-01-14 13:56:51 +01:00
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 tabulate import tabulate
2025-10-14 12:58:39 +02:00
from rougail.tiramisu import normalize_family
from tiramisu import undefined
from tiramisu.error import PropertiesOptionError, display_list
2026-03-29 11:01:15 +02:00
try:
from tiramisu_cmdline_parser.api import gen_argument_name
except:
gen_argument_name = None
2025-10-14 12:58:39 +02:00
2024-11-15 08:13:45 +01:00
from .i18n import _
2025-11-29 22:21:06 +01:00
from .config import Tabulars, ROUGAIL_VARIABLE_TYPE
2024-11-15 08:13:45 +01:00
ENTER = "\n\n"
_yaml = YAML()
_yaml.indent(mapping=2, sequence=4, offset=2)
def dump(informations):
2024-11-15 08:13:45 +01:00
"""Dump variable, means transform bool, ... to yaml string"""
with BytesIO() as ymlfh:
_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"""
msg = str(msg).strip()
2024-11-15 08:13:45 +01:00
if not msg:
# replace None to empty string
return ""
# a phrase must ends with a dot
2025-10-15 09:44:22 +02:00
if type_ == "variable":
2026-03-29 12:42:01 +02:00
msg = add_dot(msg)
2025-10-29 10:46:57 +01:00
elif type_ in ["family", "description"]:
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:]
2026-03-29 12:42:01 +02:00
def add_dot(msg):
msg = str(msg).strip()
last_char = msg[-1]
if last_char not in [".", "?", "!", ",", ":", ";"]:
msg += "."
return msg
2025-11-29 22:21:06 +01:00
class CommonTabular:
"""Class with common function for tabular"""
def __init__(self, formatter: "CommonFormatter") -> None:
self.formatter = formatter
self.clear()
def clear(self):
self.columns = []
def get(self):
columns = list(self.get_columns())
self.clear()
return columns
2026-03-29 11:01:15 +02:00
def add(
self,
informations: dict,
modified_attributes: dict,
force_identifiers: Optional[str],
) -> tuple:
2025-11-29 22:21:06 +01:00
self.informations = informations
self.modified_attributes = modified_attributes
self.force_identifiers = force_identifiers
self.calculated_properties = []
self.set_description()
self.set_type()
self.set_choices()
self.set_properties()
self.set_default()
self.set_examples()
self.set_paths()
self.set_commandline()
self.set_environment()
self.set_validators()
self.set_tags()
self.columns.append(self._add())
def set_description(self):
if "description" in self.informations:
self.description = self.formatter.get_description(
2026-03-29 11:01:15 +02:00
self.informations,
self.modified_attributes,
2026-04-30 06:58:12 +02:00
force_identifiers=self.force_identifiers,
2025-11-29 22:21:06 +01:00
)
else:
self.description = None
2026-03-29 11:01:15 +02:00
self.help_ = self.formatter.convert_list_to_string(
"help", self.informations, self.modified_attributes
)
2025-11-29 22:21:06 +01:00
def set_choices(self):
2026-03-29 11:01:15 +02:00
self.default_is_already_set, self.choices = (
self.formatter.convert_choices_to_string(
self.informations, self.modified_attributes
)
2025-11-29 22:21:06 +01:00
)
def set_type(self):
self.multi = self.informations.get("multiple", True)
self.type = self.informations["variable_type"]
def set_default(self):
if "default" in self.informations:
self.default = self.formatter.convert_section_to_string(
2026-03-29 11:01:15 +02:00
"default", self.informations, self.modified_attributes, multi=self.multi
)
2025-11-29 22:21:06 +01:00
else:
self.default = None
def set_examples(self):
self.examples = self.formatter.convert_section_to_string(
"examples", self.informations, self.modified_attributes, multi=True
)
def set_paths(self):
2026-03-29 11:01:15 +02:00
self.paths = self.formatter.join(
self.formatter.display_paths(
self.informations,
self.modified_attributes,
self.force_identifiers,
is_variable=True,
)
)
2025-11-29 22:21:06 +01:00
def set_commandline(self):
2026-03-29 11:01:15 +02:00
if self.formatter.with_commandline and not self.informations.get(
"not_for_commandline", False
):
commandlines = self.formatter.display_paths(
self.informations,
self.modified_attributes,
self.force_identifiers,
is_variable=True,
is_bold=False,
variable_prefix="--",
with_anchor=False,
)
if self.type == "boolean":
for path in self.formatter.display_paths(
self.informations,
self.modified_attributes,
self.force_identifiers,
is_variable=True,
is_bold=False,
is_upper=False,
with_anchor=False,
variable_prefix="--",
):
commandlines.append(
"--" + gen_argument_name(path[2:], False, True, False)
)
2025-11-29 22:21:06 +01:00
if "alternative_name" in self.informations:
alternative_name = self.informations["alternative_name"]
commandlines[0] = f"-{alternative_name}, {commandlines[0]}"
2026-03-29 11:01:15 +02:00
if self.type == "boolean":
commandlines[1] = (
"-"
+ gen_argument_name(alternative_name, True, True, False)
+ f", {commandlines[1]}"
)
2026-06-21 14:42:34 +02:00
if "full_path" in self.informations:
full_path = self.informations["full_path"]
else:
full_path = self.informations["path"]
2026-03-29 11:01:15 +02:00
self.commandlines = self.formatter.section(
2026-06-21 14:42:34 +02:00
full_path, _("Command line"), commandlines, force_enter=True
2026-03-29 11:01:15 +02:00
)
2025-11-29 22:21:06 +01:00
else:
self.commandlines = None
def set_environment(self):
if self.formatter.with_environment:
2026-03-29 11:01:15 +02:00
environments = self.formatter.display_paths(
self.informations,
self.modified_attributes,
self.force_identifiers,
is_variable=True,
is_bold=False,
is_upper=True,
variable_prefix=self.formatter.prefix,
with_anchor=False,
)
2026-06-21 14:42:34 +02:00
if "full_path" in self.informations:
full_path = self.informations["full_path"]
else:
full_path = self.informations["path"]
2026-03-29 11:01:15 +02:00
self.environments = self.formatter.section(
2026-06-21 14:42:34 +02:00
full_path, _("Environment variable"), environments
2026-03-29 11:01:15 +02:00
)
2025-11-29 22:21:06 +01:00
else:
self.environments = None
def set_properties(self):
self.properties = self.formatter.property_to_string(
2026-03-29 11:01:15 +02:00
self.informations,
self.calculated_properties,
self.modified_attributes,
2025-11-29 22:21:06 +01:00
)
def set_validators(self):
self.validators = self.formatter.convert_section_to_string(
"validators", self.informations, self.modified_attributes, multi=True
)
def set_tags(self):
self.tags = self.formatter.convert_section_to_string(
"tags", self.informations, self.modified_attributes, multi=True
)
def get_column(self, *args):
contents = [arg for arg in args if arg]
self.formatter.columns(contents)
return self.formatter.join(contents)
2025-10-27 21:59:39 +01:00
class CommonFormatter:
"""Class with common function for formatter"""
2024-11-15 08:13:45 +01:00
2025-11-29 22:21:06 +01:00
enter_tabular = "\n"
2026-04-30 06:58:12 +02:00
format_in_title = True
2024-11-15 08:13:45 +01:00
# tabulate module name
name = None
2026-04-30 06:58:12 +02:00
def __init__(self, rougailconfig, support_namespace, document_a_type, **kwargs):
2024-11-15 08:13:45 +01:00
tabulate_module.PRESERVE_WHITESPACE = True
self.header_setted = False
self.rougailconfig = rougailconfig
self.support_namespace = support_namespace
2026-04-30 06:58:12 +02:00
self.document_a_type = document_a_type
2026-03-29 11:01:15 +02:00
def run(self, informations: dict, *, dico_is_already_treated=False) -> str:
"""Transform to string"""
if informations:
level = self.rougailconfig["doc.title_level"]
self.options()
return self._run(informations, level, dico_is_already_treated)
return ""
def options(self):
2025-11-29 22:21:06 +01:00
self.with_commandline = self.rougailconfig["doc.tabulars.with_commandline"]
self.with_environment = self.rougailconfig["doc.tabulars.with_environment"]
if self.with_environment and not gen_argument_name:
2026-03-29 11:01:15 +02:00
raise Exception("please install tiramisu_cmdline_parser")
if not self.rougailconfig["main_namespace"] and self.with_environment:
2026-06-11 08:07:02 +02:00
environment_prefix = self.rougailconfig["doc.tabulars.environment_prefix"]
if environment_prefix:
2026-06-21 17:05:44 +02:00
self.prefix = environment_prefix + "_"
2025-11-29 22:21:06 +01:00
self.with_family = not self.rougailconfig["doc.tabulars.without_family"]
2026-06-21 17:05:44 +02:00
self.other_root_filenames = dict(
self.rougailconfig["doc.other_root_filenames"].items()
)
2025-11-29 22:21:06 +01:00
tabular_template = self.rougailconfig["doc.tabular_template"]
self.tabular_datas = Tabulars().get()[tabular_template](self)
def compute(self, data):
2026-04-30 06:58:12 +02:00
return ENTER.join([d for d in data if d])
2024-11-15 08:13:45 +01:00
2025-10-27 21:59:39 +01:00
# Class you needs implement to your Formatter
2024-11-15 08:13:45 +01:00
def title(
self,
title: str,
level: int,
2026-06-21 17:05:44 +02:00
collapse: bool = True,
2024-11-15 08:13:45 +01:00
) -> str:
"""Display family name as a title"""
raise NotImplementedError()
def join(
self,
lst: List[str],
) -> str:
2025-11-29 22:21:06 +01:00
"""Display line in tabular from a list"""
2024-11-15 08:13:45 +01:00
raise NotImplementedError()
def bold(
self,
msg: str,
) -> str:
"""Set a text to bold"""
raise NotImplementedError()
2025-10-29 10:46:57 +01:00
def underline(
self,
msg: str,
) -> str:
"""Set a text to underline"""
raise NotImplementedError()
2026-03-29 11:01:15 +02:00
def anchor(
self,
path: str,
true_path: str,
) -> str:
2025-10-29 10:46:57 +01:00
"""Set a text to a link anchor"""
return path
2026-03-29 11:01:15 +02:00
def link_variable(
self,
path: str,
true_path: str,
description: str,
filename: Optional[str],
) -> str:
2025-10-29 10:46:57 +01:00
"""Set a text link to variable anchor"""
2026-06-21 14:42:34 +02:00
if not description:
return f'"{path}"'
2026-04-01 17:01:24 +02:00
return f'"{description}" ({path})'
2025-10-29 10:46:57 +01:00
2024-11-15 08:13:45 +01:00
def stripped(
self,
text: str,
) -> str:
"""Return stripped text (as help)"""
raise NotImplementedError()
def list(
self,
choices: list,
*,
2026-03-29 11:01:15 +02:00
inside_tabular: bool = True,
type_: str = "variable",
2025-11-29 22:21:06 +01:00
with_enter: bool = False,
2024-11-15 08:13:45 +01:00
) -> str:
"""Display a liste of element"""
raise NotImplementedError()
def prop(
self,
prop: str,
italic: bool,
delete: bool,
underline: bool,
2024-11-15 08:13:45 +01:00
) -> str:
"""Display property"""
raise NotImplementedError()
def link(
self,
comment: str,
link: str,
underline: bool,
2024-11-15 08:13:45 +01:00
) -> str:
"""Add a link"""
raise NotImplementedError()
2026-06-21 17:05:44 +02:00
def yaml(self, _dump: str, yaml_version: str = "1.1"):
2026-04-30 06:58:12 +02:00
output = f"---\n{_dump}"
if yaml_version == "1.2":
2026-06-21 17:05:44 +02:00
output = f"%YAML 1.2\n{output}\n..."
2026-04-30 06:58:12 +02:00
return self._yaml(output)
2024-11-15 08:13:45 +01:00
##################
2025-10-02 08:19:18 +02:00
def end_family_informations(self) -> str:
2026-04-30 06:58:12 +02:00
return None
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,
force_identifiers: Optional[str],
2025-10-29 10:46:57 +01:00
*,
is_variable=False,
2026-03-29 11:01:15 +02:00
variable_prefix: str = "",
is_bold: bool = True,
is_upper: bool = False,
with_anchor: bool = True,
2025-10-02 08:19:18 +02:00
) -> str:
2025-11-29 22:21:06 +01:00
if with_anchor:
anchor = self.anchor
else:
2026-03-29 11:01:15 +02:00
2025-11-29 22:21:06 +01:00
def anchor(path, true_path):
return path
2026-06-21 17:05:44 +02:00
2025-10-14 12:58:39 +02:00
ret_paths = []
path = informations["path"]
2026-01-14 13:56:51 +01:00
if not path:
return None
if is_bold:
bold = self.bold
else:
2026-03-29 11:01:15 +02:00
def bold(value):
return value
2026-03-29 11:01:15 +02:00
if is_upper:
2026-03-29 11:01:15 +02:00
def upper(value):
return value.upper()
2026-03-29 11:01:15 +02:00
else:
2026-03-29 11:01:15 +02:00
def upper(value):
return value
2026-03-29 11:01:15 +02:00
2025-10-14 12:58:39 +02:00
if "identifiers" in modified_attributes:
name, previous, new = modified_attributes["identifiers"]
2025-10-15 09:44:22 +02:00
ret_paths.extend(
[
2026-03-29 11:01:15 +02:00
bold(
self.delete(
upper(
variable_prefix
+ calc_path(
path, formatter=self, identifiers=identifier
)
)
)
)
2025-10-15 09:44:22 +02:00
for identifier in previous
]
)
2025-10-14 12:58:39 +02:00
else:
new = []
2026-04-30 06:58:12 +02:00
if "full_path" in informations:
full_path = informations["full_path"]
else:
2026-06-21 14:42:34 +02:00
full_path = informations["path"]
2025-10-14 12:58:39 +02:00
if "identifiers" in informations:
2025-10-29 10:46:57 +01:00
for idx, identifier in enumerate(informations["identifiers"]):
if force_identifiers and identifier != force_identifiers:
continue
2026-03-29 11:01:15 +02:00
path_ = calc_path(path, formatter=self, identifiers=identifier)
if variable_prefix:
path_ = variable_prefix + upper(path_)
2025-10-29 10:46:57 +01:00
if not idx:
2026-04-30 06:58:12 +02:00
path_ = anchor(path_, full_path)
2025-10-14 12:58:39 +02:00
if identifier in new:
path_ = self.underline(path_)
ret_paths.append(bold(path_))
2025-10-14 12:58:39 +02:00
else:
2026-04-30 06:58:12 +02:00
ret_paths.append(bold(anchor(variable_prefix + upper(path), full_path)))
2025-10-14 12:58:39 +02:00
return ret_paths
2025-10-02 08:19:18 +02:00
2025-11-29 22:21:06 +01:00
def tabular_header(
2024-11-15 08:13:45 +01:00
self,
lst: list,
) -> tuple:
2025-11-29 22:21:06 +01:00
"""Manage the header of a tabular"""
2024-11-15 08:13:45 +01:00
return lst
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:
2026-04-30 06:58:12 +02:00
return ENTER.join(dico)
return ENTER.join([msg for msg in self.dict_to_dict(dico, level, init=True)])
2025-10-14 12:58:39 +02:00
2025-10-15 09:44:22 +02:00
def dict_to_dict(
self,
dico: dict,
level: int,
*,
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 = []
for value in dico.values():
2025-10-14 12:58:39 +02:00
if value["type"] == "variable":
2025-11-29 22:21:06 +01:00
self.variable_to_string(value)
elif self.with_family:
if init and value["type"] == "namespace":
namespace = True
2024-11-15 08:13:45 +01:00
else:
2025-11-29 22:21:06 +01:00
namespace = False
if self.tabular_datas.columns:
msg.append(self.tabular())
2026-03-29 11:01:15 +02:00
msg.extend(
self.family_to_string(value["informations"], level, namespace)
)
2025-11-29 22:21:06 +01:00
msg.extend(self.dict_to_dict(value["children"], level + 1))
2026-04-30 06:58:12 +02:00
end = self.end_family(level)
if end:
msg.append(end)
2025-11-29 22:21:06 +01:00
else:
self.dict_to_dict(
2026-03-29 11:01:15 +02:00
value["children"],
level + 1,
2025-11-29 22:21:06 +01:00
)
if self.tabular_datas.columns and (init or self.with_family):
msg.append(self.tabular())
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(
2026-04-30 06:58:12 +02:00
self.get_description(informations, {}, title=True),
level,
2024-11-15 08:13:45 +01:00
)
def family_to_string(self, informations: dict, level: int, namespace: bool) -> str:
2024-11-15 08:13:45 +01:00
"""manage other family type"""
if namespace:
ret = [self.namespace_to_title(informations, level)]
else:
2026-03-29 11:01:15 +02:00
ret = [
self.title(
2026-06-21 17:05:44 +02:00
self.get_description(informations, {}, title=True),
level,
2026-03-29 11:01:15 +02:00
)
]
2025-11-29 22:21:06 +01:00
msg = []
helps = informations.get("help")
2024-11-15 08:13:45 +01:00
if helps:
for help_ in helps:
2026-03-29 11:01:15 +02:00
msg.extend([to_phrase(h) for h in help_.strip().split("\n")])
path = self.display_paths(
informations, {}, None, with_anchor=False, is_bold=False
)
2026-01-14 13:56:51 +01:00
if path:
2026-06-21 14:42:34 +02:00
if "full_path" in informations:
full_path = informations["full_path"]
else:
full_path = informations["path"]
msg.append(self.section(full_path, _("Path"), path, type_="family"))
2025-10-02 08:19:18 +02:00
calculated_properties = []
2025-11-29 22:21:06 +01:00
property_str = self.property_to_string(informations, calculated_properties, {})
2025-11-10 14:01:45 +01:00
if property_str:
msg.append(property_str)
2025-10-02 08:19:18 +02:00
if calculated_properties:
2026-03-29 11:01:15 +02:00
msg.append(self.join(calculated_properties))
2025-10-14 12:58:39 +02:00
if "identifier" in informations:
2026-06-21 14:42:34 +02:00
if "full_path" in informations:
full_path = informations["full_path"]
else:
full_path = informations["path"]
2025-05-11 19:13:12 +02:00
msg.append(
2026-03-29 11:01:15 +02:00
self.section(
2026-06-21 17:05:44 +02:00
full_path,
_("Identifiers"),
informations["identifier"],
type_="family",
2026-03-29 11:01:15 +02:00
)
2025-05-11 19:13:12 +02:00
)
2026-01-14 13:56:51 +01:00
if msg:
2026-04-01 17:01:24 +02:00
ret.append(self.display_family_informations(msg))
2026-04-30 06:58:12 +02:00
end = self.end_family_informations()
if end:
ret.append(end)
return ret
2026-04-01 17:01:24 +02:00
def display_family_informations(self, msg) -> str:
fam_info = self.family_informations()
2026-04-30 06:58:12 +02:00
msg = self.family_informations_ends_line().join(msg)
if fam_info:
msg = fam_info + msg
return msg
2026-04-01 17:01:24 +02:00
def family_informations(self) -> str:
2026-04-30 06:58:12 +02:00
return None
2026-04-01 17:01:24 +02:00
def family_informations_starts_line(self) -> str:
2026-04-30 06:58:12 +02:00
return None
def family_informations_ends_line(self) -> str:
2025-10-15 09:44:22 +02:00
return ""
2025-10-02 08:19:18 +02:00
2026-06-21 17:05:44 +02:00
def end_family(self, level: int, collapse: bool = True) -> str:
2026-04-30 06:58:12 +02:00
return None
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(
2026-03-29 11:01:15 +02:00
self,
informations: dict,
modified_attributes: dict,
2026-03-29 12:42:01 +02:00
*,
2026-06-21 17:05:44 +02:00
force_identifiers: Optional[list] = None,
with_to_phrase: bool = True,
title: bool = False,
) -> str:
2026-03-31 12:27:08 +02:00
add_new_description = True
2026-06-21 17:05:44 +02:00
def _get_description(
description,
identifiers,
delete=False,
new=[],
previous_identifiers=[],
new_identifiers=[],
its_a_name=False,
):
if identifiers and "{{ identifier }}" in description:
2026-04-30 06:58:12 +02:00
if its_a_name:
information_type = "name"
2025-10-14 12:58:39 +02:00
else:
2026-04-30 06:58:12 +02:00
information_type = "description"
if isinstance(informations["path"], dict):
info = informations["path"]
else:
info = informations
path = informations["path"]
if isinstance(path, str):
identifier_type = "many"
else:
identifier_type = path["identifier_type"]
if previous_identifiers:
pass
if not title or self.format_in_title:
formatter = self
else:
formatter = None
2026-06-21 17:05:44 +02:00
description = get_path_from_identifiers(
description,
identifiers,
previous_identifiers,
new_identifiers,
identifier_type,
formatter=formatter,
information_type=information_type,
)
2026-03-29 12:42:01 +02:00
elif with_to_phrase:
2025-10-14 12:58:39 +02:00
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
2026-04-30 06:58:12 +02:00
if force_identifiers:
all_identifiers = force_identifiers
else:
all_identifiers = informations.get("identifiers", [])
2025-10-14 12:58:39 +02:00
if "description" in modified_attributes:
name, previous, new = modified_attributes["description"]
if previous:
identifiers = modified_attributes.get("identifiers", [])
2026-03-31 12:27:08 +02:00
# if modified_attributes has no description
if not identifiers:
2026-04-30 06:58:12 +02:00
identifiers = all_identifiers
modified_description = _get_description(
previous[0], identifiers, delete=True
)
else:
modified_description = None
2026-06-21 17:05:44 +02:00
elif (
"identifiers" in modified_attributes
and "{{ identifier }}" in informations["description"]
):
2026-04-30 06:58:12 +02:00
# FIXME aussi au dessus !
2026-03-31 12:27:08 +02:00
name, previous, new = modified_attributes["identifiers"]
previous_identifiers = previous[-1] if previous else []
new_identifiers = new[-1] if new else []
2026-04-30 06:58:12 +02:00
if new_identifiers:
2026-06-21 17:05:44 +02:00
all_identifiers = [
identifier
for identifier in all_identifiers
if identifier != new_identifiers
]
2026-03-31 12:27:08 +02:00
modified_description = _get_description(
2026-06-21 17:05:44 +02:00
informations["description"],
all_identifiers,
previous_identifiers=previous_identifiers,
new_identifiers=new_identifiers,
2026-03-31 12:27:08 +02:00
)
add_new_description = False
2025-10-14 12:58:39 +02:00
else:
modified_description = None
new = []
2026-03-31 12:27:08 +02:00
if add_new_description:
if "description" in informations:
description = _get_description(
2026-04-30 06:58:12 +02:00
informations["description"], all_identifiers, new=new
2026-03-31 12:27:08 +02:00
)
else:
description = _get_description(
2026-06-21 17:05:44 +02:00
informations["name"],
all_identifiers,
new=new,
its_a_name=True,
2026-03-31 12:27:08 +02:00
)
else:
description = None
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(
2026-03-29 11:01:15 +02:00
self,
informations: dict,
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"""
2025-11-29 22:21:06 +01:00
self.tabular_datas.add(informations, modified_attributes, force_identifiers)
2025-10-14 12:58:39 +02:00
2025-10-15 09:44:22 +02:00
def convert_section_to_string(
2026-03-29 11:01:15 +02:00
self,
attribute: str,
informations: dict,
modified_attributes: dict,
multi: bool,
*,
section_name: bool = True,
with_to_phrase=False,
2025-10-15 09:44:22 +02:00
) -> str():
2025-10-14 12:58:39 +02:00
values = []
submessage = ""
2026-06-21 14:42:34 +02:00
if "full_path" in informations:
full_path = informations["full_path"]
else:
full_path = informations["path"]
2025-10-14 12:58:39 +02:00
if modified_attributes and attribute in modified_attributes:
name, previous, new = modified_attributes[attribute]
if isinstance(previous, list):
for p in previous:
2026-06-21 14:42:34 +02:00
submessage, m = self.message_to_string(full_path, p, submessage)
2025-10-14 12:58:39 +02:00
values.append(self.delete(m))
else:
2026-06-21 17:05:44 +02:00
submessage, old_values = self.message_to_string(
full_path, previous, submessage
)
2025-10-14 12:58:39 +02:00
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"]:
2026-06-21 17:05:44 +02:00
if "identifiers" in informations and (
not isinstance(value, dict) or "identifiers" not in value
):
2026-06-21 14:42:34 +02:00
identifiers = informations["identifiers"]
else:
identifiers = []
2026-06-21 17:05:44 +02:00
submessage, old_value = self.message_to_string(
full_path, value, submessage, force_identifiers=identifiers
)
2025-10-14 12:58:39 +02:00
if value in new:
old_value = self.underline(old_value)
values.append(old_value)
if multi:
2025-11-29 22:21:06 +01:00
values = self.list(values, with_enter=section_name)
2025-10-14 12:58:39 +02:00
else:
values = self.join(values)
elif values:
old_values = old["values"]
2026-06-21 17:05:44 +02:00
submessage, old_values = self.message_to_string(
full_path, old_values, submessage
)
2025-10-14 12:58:39 +02:00
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:
2026-06-21 14:42:34 +02:00
old_values = old["values"]
2026-06-21 17:05:44 +02:00
if "identifiers" in informations and (
not isinstance(old_values, dict) or "identifiers" not in old_values
):
2026-06-21 14:42:34 +02:00
identifiers = informations["identifiers"]
else:
identifiers = []
2026-06-21 17:05:44 +02:00
submessage, values = self.message_to_string(
full_path, old_values, submessage, force_identifiers=identifiers
)
2025-10-14 12:58:39 +02:00
if old["values"] in new:
values = self.underline(values)
if values != []:
2026-03-29 11:01:15 +02:00
return self.section(
2026-06-21 14:42:34 +02:00
full_path,
2026-03-29 11:01:15 +02:00
name,
values,
submessage=submessage,
section_name=section_name,
with_to_phrase=with_to_phrase,
)
2025-10-14 12:58:39 +02:00
2025-10-15 09:44:22 +02:00
def convert_choices_to_string(
2026-03-29 11:01:15 +02:00
self,
informations: dict,
modified_attributes: dict,
with_default: bool = True,
2025-10-15 09:44:22 +02:00
) -> str():
2025-10-14 12:58:39 +02:00
default_is_already_set = False
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
2026-06-21 14:42:34 +02:00
if "full_path" in informations:
full_path = informations["full_path"]
else:
full_path = informations["path"]
2025-10-29 10:46:57 +01:00
for idx, choice in enumerate(choices_values.copy()):
if isinstance(choice, dict):
2026-06-21 17:05:44 +02:00
choices_values[idx] = self.message_to_string(
full_path, choice, None
)[1]
2025-10-14 12:58:39 +02:00
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 = [None]
2025-10-14 12:58:39 +02:00
if not isinstance(default, list):
default = [default]
2025-10-29 10:46:57 +01:00
for idx, value in enumerate(default.copy()):
if isinstance(value, dict):
2026-06-21 14:42:34 +02:00
default[idx] = self.message_to_string(full_path, value, None)[1]
2025-10-14 12:58:39 +02:00
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):
2025-10-29 10:46:57 +01:00
if isinstance(choice, dict):
2026-06-21 14:42:34 +02:00
choice = self.message_to_string(full_path, choice, None)[1]
2025-11-29 22:21:06 +01:00
if with_default and 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):
2025-11-29 22:21:06 +01:00
if with_default and val in old_default:
2025-10-15 09:44:22 +02:00
choices_values[idx] = (
dump(val) + " " + self.delete("" + _("(default)"))
)
2025-11-29 22:21:06 +01:00
elif with_default and val in default:
2025-10-14 12:58:39 +02:00
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]
2026-06-21 17:05:44 +02:00
return default_is_already_set, self.section(
full_path, choices["name"], choices_values
)
2025-10-14 12:58:39 +02:00
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
def property_to_string(
2025-10-15 09:44:22 +02:00
self,
informations: dict,
calculated_properties: list,
modified_attributes: dict,
2026-03-29 11:01:15 +02:00
contents: list = ["type", "properties", "mode", "access_control", "validator"],
) -> str:
2024-11-15 08:13:45 +01:00
"""Transform properties to string"""
properties = []
2025-11-29 22:21:06 +01:00
modified_properties = []
if "type" in contents:
if "type" in modified_attributes:
2026-03-29 11:01:15 +02:00
properties.append(
self.prop(
modified_attributes["type"],
italic=False,
delete=True,
underline=False,
)
)
2026-04-30 06:58:12 +02:00
if "multiple" in modified_attributes:
2026-06-21 17:05:44 +02:00
if (
modified_attributes["multiple"][1]
and modified_attributes["multiple"][1][0]
):
2026-04-30 06:58:12 +02:00
properties.append(
2026-06-21 17:05:44 +02:00
self.prop(
"multiple", italic=False, delete=True, underline=False
)
2026-04-30 06:58:12 +02:00
)
else:
properties.append(
2026-06-21 17:05:44 +02:00
self.prop(
"multiple", italic=False, delete=False, underline=True
)
2026-04-30 06:58:12 +02:00
)
2025-11-29 22:21:06 +01:00
if "properties" not in contents and "properties" in modified_attributes:
for prop in modified_attributes["properties"]:
if prop["ori_name"] == "mandatory":
2026-03-29 11:01:15 +02:00
properties.append(
self.prop(
prop["ori_name"],
italic=False,
delete=True,
underline=False,
)
)
2025-11-29 22:21:06 +01:00
break
if "mode" in contents and "mode" in modified_attributes:
2026-03-14 20:51:52 +01:00
name, previous, new = modified_attributes["mode"]
if previous:
2026-03-29 11:01:15 +02:00
properties.append(
self.prop(previous[0], italic=False, delete=True, underline=False)
)
2026-03-14 20:51:52 +01:00
if new:
2026-03-29 11:01:15 +02:00
properties.append(
self.prop(new[0], italic=False, delete=False, underline=True)
)
2025-10-14 12:58:39 +02:00
if "properties" in modified_attributes:
2025-11-29 22:21:06 +01:00
if "properties" in contents:
for props in modified_attributes["properties"]:
if not props:
continue
for prop in props:
if prop.get("access_control"):
if "access_control" in contents:
modified_properties.append(prop)
elif prop["ori_name"] != "unique" or "unique" in contents:
modified_properties.append(prop)
elif "access_control" in contents:
for prop in modified_attributes["properties"]:
if prop.get("access_control"):
modified_properties.append(prop)
elif "validator" in contents:
for prop in modified_attributes["properties"]:
if prop["name"] == "unique":
modified_properties.append(prop)
local_calculated_properties = {}
if "properties" in contents and "properties" in modified_attributes:
2025-10-15 09:44:22 +02:00
previous, new = self.get_modified_properties(
*modified_attributes["properties"][1:]
)
2025-11-29 22:21:06 +01:00
for p, data in previous.items():
if "type" not in contents and data[0] == "mandatory":
continue
if "validator" not in contents and data[0] == "unique":
continue
2025-10-14 12:58:39 +02:00
if p not in new:
2026-03-29 11:01:15 +02:00
properties.append(
self.prop(p, italic=False, delete=True, underline=False)
)
2025-11-29 22:21:06 +01:00
if data[1] is not None:
2026-03-29 11:01:15 +02:00
local_calculated_properties[p] = [
{"annotation": data[1], "delete": True}
]
2025-10-14 12:58:39 +02:00
else:
previous = new = []
2026-04-30 06:58:12 +02:00
if "type" in contents and "variable_type" in modified_attributes:
previous, new = modified_attributes["variable_type"][1:]
properties.append(
self.prop(previous[0], italic=False, delete=True, underline=False)
)
2025-11-29 22:21:06 +01:00
others = []
if "type" in contents and "variable_type" in informations:
others.append({"name": informations["variable_type"], "type": "type"})
2026-04-30 06:58:12 +02:00
if informations.get("multiple") and not "multiple" in modified_attributes:
2025-11-29 22:21:06 +01:00
others.append({"name": "multiple", "type": "multiple"})
if "properties" not in contents and "properties" in informations:
for prop in informations["properties"]:
if prop["ori_name"] == "mandatory":
others.append(prop)
break
if "mode" in contents and "mode" in informations:
others.append({"name": informations["mode"], "type": "mode"})
if "properties" in informations:
if "properties" in contents:
for prop in informations["properties"]:
if prop.get("access_control"):
if "access_control" in contents:
others.append(prop)
elif prop["ori_name"] != "unique" or "validator" in contents:
others.append(prop)
elif "access_control" in contents:
for prop in informations["properties"]:
if prop.get("access_control"):
others.append(prop)
elif "validator" in contents:
for prop in informations["properties"]:
if prop["name"] == "unique":
others.append(prop)
for prop in others:
prop_name = prop["name"]
2026-03-29 11:01:15 +02:00
if (
"type" not in contents
and prop.get("ori_name", prop_name) == "mandatory"
):
2025-11-29 22:21:06 +01:00
continue
if prop_name not in previous and prop_name in new:
underline = True
else:
underline = False
2024-11-20 21:12:56 +01:00
if prop["type"] == "type":
2026-03-29 11:01:15 +02:00
properties.append(
self.link(prop_name, ROUGAIL_VARIABLE_TYPE, underline=underline)
)
2024-11-15 08:13:45 +01:00
else:
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-11-21 08:15:00 +01:00
underline_ = True
else:
underline_ = False
2025-10-15 09:44:22 +02:00
local_calculated_properties.setdefault(prop["name"], []).append(
2026-03-29 11:01:15 +02:00
{"annotation": prop_annotation, "underline": underline_}
2025-10-15 09:44:22 +02:00
)
2024-11-15 08:13:45 +01:00
else:
italic = False
2026-03-29 11:01:15 +02:00
properties.append(
self.prop(
prop_name, italic=italic, delete=False, underline=underline
)
)
2025-10-14 12:58:39 +02:00
if local_calculated_properties:
2026-06-21 14:42:34 +02:00
if "full_path" in informations:
full_path = informations["full_path"]
else:
full_path = informations["path"]
2025-10-15 09:44:22 +02:00
for (
calculated_property_name,
calculated_property,
) in local_calculated_properties.items():
2025-11-21 08:15:00 +01:00
data = []
for calc in calculated_property:
2026-06-21 17:05:44 +02:00
annotation = self.message_to_string(
full_path, calc["annotation"], None
)[1]
2025-11-21 08:15:00 +01:00
if calc.get("underline", False):
annotation = self.underline(annotation)
if calc.get("delete", False):
annotation = self.delete(annotation)
data.append(annotation)
2025-10-14 12:58:39 +02:00
if len(calculated_property) > 1:
2025-11-21 08:15:00 +01:00
calculated_property = self.join(data)
2025-10-14 12:58:39 +02:00
else:
2025-11-21 08:15:00 +01:00
calculated_property = data[0]
2025-10-14 12:58:39 +02:00
calculated_properties.append(
2025-10-15 09:44:22 +02:00
self.section(
2026-06-21 17:05:44 +02:00
full_path,
calculated_property_name.capitalize(),
calculated_property,
2025-10-15 09:44:22 +02:00
)
2025-10-14 12:58:39 +02:00
)
2024-11-15 08:13:45 +01:00
if not properties:
2025-11-29 22:21:06 +01:00
return ""
return " ".join(properties)
2025-10-14 12:58:39 +02:00
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):
2025-11-29 22:21:06 +01:00
return {d["name"]: (d["ori_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-11-29 22:21:06 +01:00
def tabular(self, with_header: bool = True) -> str:
"""Transform list to a tabular in string format"""
if with_header:
2025-11-29 22:21:06 +01:00
headers = self.tabular_header(self.tabular_datas.headers())
else:
headers = ()
2026-06-21 17:05:44 +02:00
msg = tabulate(
self.tabular_datas.get(),
headers=headers,
tablefmt=self._tabular_name,
2024-11-15 08:13:45 +01:00
)
return msg
2026-06-21 14:42:34 +02:00
def message_to_string(self, full_path, msg, ret, *, force_identifiers=[]):
2025-10-14 12:58:39 +02:00
if isinstance(msg, dict):
if "submessage" in msg:
ret += msg["submessage"]
msg = msg["values"]
elif "message" in msg:
2025-10-29 10:46:57 +01:00
filename = None
if self.other_root_filenames:
path = msg["path"]["path"]
for root in self.other_root_filenames:
2026-03-29 11:01:15 +02:00
if path == root or path.startswith(f"{root}."):
2025-10-29 10:46:57 +01:00
filename = self.other_root_filenames[root]
break
2026-03-29 12:42:01 +02:00
else:
if "." in self.other_root_filenames:
filename = self.other_root_filenames["."]
if "identifiers" in msg["path"]:
2026-04-30 06:58:12 +02:00
msg["identifiers"] = msg["path"]["identifiers"]
2026-06-21 17:05:44 +02:00
calculated_paths = calc_path(
msg["path"], formatter=self, identifiers=force_identifiers
)
2026-06-21 14:42:34 +02:00
if self.support_namespace and self.document_a_type:
namespace = full_path.split(".", 1)[0]
else:
namespace = None
2026-04-30 06:58:12 +02:00
if isinstance(calculated_paths, list):
2026-06-21 17:05:44 +02:00
msgs = [
msg["message"].format(
self.link_variable(
doc_path(
calculated_path, self.document_a_type, namespace
),
msg["path"]["path"],
self.get_description(
msg,
{},
force_identifiers=[msg["path"]["identifiers"][idx]],
with_to_phrase=False,
),
filename=filename,
)
)
for idx, calculated_path in enumerate(calculated_paths)
]
2026-04-30 06:58:12 +02:00
msg = self.list(msgs)
else:
path = self.link_variable(
2026-06-21 14:42:34 +02:00
doc_path(calculated_paths, self.document_a_type, namespace),
2026-04-30 06:58:12 +02:00
msg["path"]["path"],
self.get_description(msg, {}, with_to_phrase=False),
filename=filename,
)
msg = msg["message"].format(path)
2025-11-21 08:15:00 +01:00
elif "description" in msg:
if "variables" in msg:
paths = []
for variable in msg["variables"]:
filename = None
if self.other_root_filenames:
2026-03-29 12:42:01 +02:00
path = variable["path"]
2025-11-21 08:15:00 +01:00
for root in self.other_root_filenames:
2026-03-29 11:01:15 +02:00
if path == root or path.startswith(f"{root}."):
2025-11-21 08:15:00 +01:00
filename = self.other_root_filenames[root]
break
2026-03-29 12:42:01 +02:00
else:
if "." in self.other_root_filenames:
filename = self.other_root_filenames["."]
2025-11-21 08:15:00 +01:00
identifiers = variable.get("identifiers")
2026-06-21 14:42:34 +02:00
if identifiers is None and force_identifiers:
identifiers = force_identifiers
2026-04-30 06:58:12 +02:00
if self.format_in_title:
formatter = self
else:
formatter = None
2026-03-29 11:01:15 +02:00
path = calc_path(
variable, formatter=self, identifiers=identifiers
)
paths.append(
self.link_variable(
path,
variable["path"],
self.get_description(
variable,
{},
force_identifiers=identifiers,
2026-03-29 12:42:01 +02:00
with_to_phrase=False,
2026-03-29 11:01:15 +02:00
),
filename=filename,
)
)
2025-11-21 08:15:00 +01:00
msg = msg["description"].format(*paths)
else:
msg = msg["description"]
2025-10-14 12:58:39 +02:00
return ret, msg
2024-11-15 08:13:45 +01:00
def section(
self,
2026-06-21 14:42:34 +02:00
full_path: str,
2024-11-15 08:13:45 +01:00
name: str,
msg: str,
2025-10-14 12:58:39 +02:00
submessage: str = "",
2026-03-29 11:01:15 +02:00
type_="variable",
2025-11-29 22:21:06 +01:00
section_name=True,
2025-12-29 18:47:37 +01:00
with_to_phrase=False,
force_enter=False,
2024-11-15 08:13:45 +01:00
) -> str:
"""Return something like Name: msg"""
2026-06-21 14:42:34 +02:00
submessage, msg = self.message_to_string(full_path, msg, submessage)
2024-11-15 08:13:45 +01:00
if isinstance(msg, list):
if len(msg) == 1:
2026-06-21 14:42:34 +02:00
submessage, elt = self.message_to_string(full_path, msg[0], submessage)
2025-10-18 06:40:23 +02:00
if isinstance(elt, list):
2025-11-29 22:21:06 +01:00
submessage += self.list(elt, type_=type_, with_enter=section_name)
2025-12-29 18:47:37 +01:00
elif force_enter:
submessage += self.enter_tabular + elt
2025-10-18 06:40:23 +02:00
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:
2026-06-21 14:42:34 +02:00
submessage, elt = self.message_to_string(full_path, p, submessage)
2025-10-14 12:58:39 +02:00
lst.append(elt)
2025-11-29 22:21:06 +01:00
submessage += self.list(lst, type_=type_, with_enter=section_name)
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
2025-11-29 22:21:06 +01:00
if section_name:
return _("{0}: {1}").format(self.bold(name), submessage)
if with_to_phrase:
return to_phrase(submessage)
return submessage
2025-10-14 12:58:39 +02:00
2026-03-29 11:01:15 +02:00
def calc_path(path, *, formatter=None, identifiers: List[str] = None) -> str:
2025-10-14 12:58:39 +02:00
def _path_with_identifier(path, identifier):
2026-03-29 11:01:15 +02:00
if identifier is None:
identifier = "{{ __identifier__ }}"
else:
identifier = normalize_family(str(identifier))
2025-10-27 21:59:39 +01:00
if formatter:
identifier = formatter.italic(identifier)
2025-10-15 09:44:22 +02:00
return path.replace("{{ identifier }}", identifier, 1)
2026-06-21 17:05:44 +02:00
2025-10-14 12:58:39 +02:00
if isinstance(path, dict):
path_ = path["path"]
2025-10-29 10:46:57 +01:00
if "identifiers" in path:
2026-06-21 17:05:44 +02:00
path_ = get_path_from_identifiers(
path["path"],
path["identifiers"],
[],
[],
path["identifier_type"],
formatter,
)
2026-06-21 14:42:34 +02:00
elif identifiers:
for identifier in identifiers[0]:
path_ = _path_with_identifier(path_, identifier)
2025-10-14 12:58:39 +02:00
elif identifiers:
path_ = path
for identifier in identifiers:
path_ = _path_with_identifier(path_, identifier)
2026-03-29 11:01:15 +02:00
path_ = path_.replace("{{ __identifier__ }}", "{{ identifier }}")
2025-10-14 12:58:39 +02:00
else:
path_ = path
return path_
2026-04-30 06:58:12 +02:00
2026-06-21 17:05:44 +02:00
def doc_path(path, document_a_type, namespace: Optional[str] = None) -> str:
2026-04-30 06:58:12 +02:00
if document_a_type:
if "." not in path:
return None
2026-06-21 17:05:44 +02:00
if not namespace or path.startswith(namespace + "."):
2026-06-21 14:42:34 +02:00
return path.split(".", 1)[-1]
2026-04-30 06:58:12 +02:00
return path
2026-06-21 17:05:44 +02:00
def get_path_from_identifiers(
text: str,
all_identifiers: list,
previous_identifiers: list,
new_identifiers: list,
identifier_type: str,
formatter: Optional[object] = None,
information_type="path",
) -> str:
2026-04-30 06:58:12 +02:00
if not isinstance(all_identifiers, list):
2026-06-21 17:05:44 +02:00
raise Exception("hu1?")
2026-04-30 06:58:12 +02:00
if all_identifiers:
for i in all_identifiers:
if not isinstance(i, list):
2026-06-21 17:05:44 +02:00
raise Exception("hu2?")
2026-04-30 06:58:12 +02:00
for j in i:
if isinstance(j, list):
2026-06-21 17:05:44 +02:00
raise Exception("hu3?")
2026-04-30 06:58:12 +02:00
if not isinstance(new_identifiers, list):
2026-06-21 17:05:44 +02:00
raise Exception("hu?")
2026-04-30 06:58:12 +02:00
def _text_with_identifier(information, identifier, delete=False, underline=False):
if identifier is None:
identifier = "{{ __identifier__ }}"
elif information_type == "path":
identifier = normalize_family(str(identifier))
else:
identifier = str(identifier)
if formatter:
if delete:
identifier = formatter.delete(identifier)
if underline:
identifier = formatter.underline(identifier)
identifier = formatter.italic(identifier)
return information.replace("{{ identifier }}", identifier, 1)
2026-06-21 17:05:44 +02:00
2026-04-30 06:58:12 +02:00
if identifier_type == "outside":
separator = "and"
else:
separator = "or"
if information_type == "description":
ori_text = "{{ identifier }}"
else:
ori_text = text
paths = []
identifiers_done = []
for identifiers in all_identifiers:
if information_type != "path":
identifier = identifiers[-1]
if identifier in identifiers_done:
continue
text_ = _text_with_identifier(ori_text, identifiers[-1])
identifiers_done.append(identifier)
else:
text_ = ori_text
for identifier in identifiers:
text_ = _text_with_identifier(text_, identifier)
paths.append(text_)
if formatter:
for identifier in previous_identifiers:
paths.append(_text_with_identifier(ori_text, identifier, delete=True))
for identifier in new_identifiers:
paths.append(_text_with_identifier(ori_text, identifier, underline=True))
if information_type == "description":
if identifier_type == "outside":
paths = [text.replace("{{ identifier }}", path) for path in paths]
return display_list(
paths,
separator=separator,
sort=False,
)
identifiers_text = display_list(
paths,
separator=separator,
sort=False,
)
2026-06-21 17:05:44 +02:00
return text.replace("{{ identifier }}", identifiers_text)
if identifier_type == "outside":
2026-04-30 06:58:12 +02:00
return paths
return display_list(paths, separator=separator, sort=False)