2024-11-28 21:36:45 +01:00
|
|
|
"""
|
2024-07-10 21:27:48 +02:00
|
|
|
Silique (https://www.silique.fr)
|
2025-02-10 09:52:12 +01:00
|
|
|
Copyright (C) 2024-2025
|
2024-07-10 21:27:48 +02:00
|
|
|
|
|
|
|
|
distribued with GPL-2 or later license
|
|
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
|
the Free Software Foundation; either version 2 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 General Public License for more details.
|
|
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
|
"""
|
2024-11-01 11:17:14 +01:00
|
|
|
|
2024-11-15 08:13:45 +01:00
|
|
|
from typing import Union
|
|
|
|
|
|
2025-09-22 09:42:46 +02:00
|
|
|
from rougail.utils import undefined, PROPERTY_ATTRIBUTE
|
2024-07-10 21:27:48 +02:00
|
|
|
from rougail.annotator.variable import Walk
|
|
|
|
|
|
2024-11-07 22:51:28 +01:00
|
|
|
from rougail.output_doc.i18n import _
|
2025-06-18 06:42:33 +02:00
|
|
|
from rougail.convert.object_model import (
|
2024-11-01 11:17:14 +01:00
|
|
|
Calculation,
|
|
|
|
|
JinjaCalculation,
|
|
|
|
|
VariableCalculation,
|
|
|
|
|
VariablePropertyCalculation,
|
|
|
|
|
IdentifierCalculation,
|
|
|
|
|
IdentifierPropertyCalculation,
|
|
|
|
|
InformationCalculation,
|
|
|
|
|
IndexCalculation,
|
2025-03-29 14:37:14 +01:00
|
|
|
NamespaceCalculation,
|
2024-11-01 11:17:14 +01:00
|
|
|
CONVERT_OPTION,
|
|
|
|
|
)
|
2024-07-10 21:27:48 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class Annotator(Walk):
|
|
|
|
|
"""Annotate for documentation"""
|
|
|
|
|
|
|
|
|
|
level = 95
|
|
|
|
|
|
|
|
|
|
def __init__(
|
|
|
|
|
self,
|
|
|
|
|
objectspace,
|
2024-11-15 08:13:45 +01:00
|
|
|
*args, # pylint: disable=unused-argument
|
2024-07-10 21:27:48 +02:00
|
|
|
) -> None:
|
|
|
|
|
if not objectspace.paths:
|
|
|
|
|
return
|
|
|
|
|
self.objectspace = objectspace
|
2025-09-22 09:42:46 +02:00
|
|
|
self.change_default_value = self.objectspace.rougailconfig[
|
|
|
|
|
"doc.change_default_value"
|
|
|
|
|
]
|
2024-07-10 21:27:48 +02:00
|
|
|
self.populate_family()
|
|
|
|
|
self.populate_variable()
|
|
|
|
|
|
|
|
|
|
def populate_family(self) -> None:
|
|
|
|
|
"""Set doc, path, ... to family"""
|
|
|
|
|
for family in self.get_families():
|
2025-10-14 16:09:25 +02:00
|
|
|
self.add_informations_from_properties(family)
|
2025-06-19 06:27:59 +02:00
|
|
|
if family.type == "dynamic":
|
2025-10-14 16:09:25 +02:00
|
|
|
self.force_default_value_in_suffix_variable(family)
|
|
|
|
|
self.calculation_to_information(
|
|
|
|
|
family.path,
|
|
|
|
|
"dynamic",
|
|
|
|
|
family.dynamic,
|
|
|
|
|
family.version,
|
|
|
|
|
)
|
2025-10-14 12:58:39 +02:00
|
|
|
path = family.path
|
|
|
|
|
if path in self.objectspace.forced_descriptions:
|
|
|
|
|
self.objectspace.informations.add(
|
|
|
|
|
path,
|
|
|
|
|
"forced_description",
|
|
|
|
|
True,
|
|
|
|
|
)
|
2025-06-19 06:27:59 +02:00
|
|
|
|
2025-10-14 16:09:25 +02:00
|
|
|
def force_default_value_in_suffix_variable(self, family) -> None:
|
|
|
|
|
if not self.change_default_value:
|
|
|
|
|
return
|
|
|
|
|
if not isinstance(family.dynamic, list):
|
|
|
|
|
self._force_default_value_in_suffix_variable(family, family.dynamic)
|
|
|
|
|
else:
|
|
|
|
|
for value in family.dynamic:
|
|
|
|
|
self._force_default_value_in_suffix_variable(
|
|
|
|
|
family, value, return_a_list=False
|
|
|
|
|
)
|
2024-07-10 21:27:48 +02:00
|
|
|
|
2025-10-14 16:09:25 +02:00
|
|
|
def _force_default_value_in_suffix_variable(
|
2024-11-15 08:13:45 +01:00
|
|
|
self,
|
|
|
|
|
family,
|
|
|
|
|
value,
|
|
|
|
|
*,
|
|
|
|
|
return_a_list=True,
|
|
|
|
|
) -> None:
|
|
|
|
|
"""For dynamic we must have values to document it"""
|
|
|
|
|
if not isinstance(value, Calculation):
|
|
|
|
|
return
|
2025-04-05 11:23:47 +02:00
|
|
|
value_added = False
|
2024-11-15 08:13:45 +01:00
|
|
|
if isinstance(value, (VariableCalculation, VariablePropertyCalculation)):
|
|
|
|
|
variable = self.objectspace.paths.get_with_dynamic(
|
|
|
|
|
value.variable,
|
|
|
|
|
family.path,
|
|
|
|
|
value.version,
|
|
|
|
|
value.namespace,
|
|
|
|
|
value.xmlfiles,
|
|
|
|
|
)[0]
|
2025-04-05 11:23:47 +02:00
|
|
|
if variable and not variable.default:
|
|
|
|
|
values = self.add_examples_values(variable)
|
|
|
|
|
value_added = True
|
|
|
|
|
if not value_added:
|
|
|
|
|
default_values = ["example"]
|
|
|
|
|
if not return_a_list:
|
|
|
|
|
default_values = default_values[0]
|
|
|
|
|
value.default_values = default_values
|
|
|
|
|
|
|
|
|
|
def add_examples_values(self, variable) -> list:
|
|
|
|
|
"""Check examples or test information to define examples values in a variable"""
|
2024-11-15 08:13:45 +01:00
|
|
|
values = self.objectspace.informations.get(variable.path).get("examples", None)
|
|
|
|
|
if not values:
|
|
|
|
|
values = self.objectspace.informations.get(variable.path).get("test", None)
|
|
|
|
|
if isinstance(values, tuple):
|
|
|
|
|
values = list(values)
|
2025-04-05 11:23:47 +02:00
|
|
|
if values:
|
|
|
|
|
variable.default = list(values)
|
|
|
|
|
else:
|
|
|
|
|
variable.default = [CONVERT_OPTION[variable.type]["example"]]
|
|
|
|
|
self.objectspace.informations.add(
|
|
|
|
|
variable.path, "default_value_makes_sense", False
|
|
|
|
|
)
|
2024-11-15 08:13:45 +01:00
|
|
|
return values
|
|
|
|
|
|
2024-07-10 21:27:48 +02:00
|
|
|
def populate_variable(self) -> None:
|
|
|
|
|
"""convert variables"""
|
|
|
|
|
for variable in self.get_variables():
|
|
|
|
|
if variable.type == "symlink":
|
|
|
|
|
continue
|
2025-10-14 12:58:39 +02:00
|
|
|
path = variable.path
|
2024-07-10 21:27:48 +02:00
|
|
|
if variable.type == "choice":
|
2024-11-01 11:17:14 +01:00
|
|
|
self.calculation_to_information(
|
2025-10-14 12:58:39 +02:00
|
|
|
path,
|
2024-11-01 11:17:14 +01:00
|
|
|
"choice",
|
|
|
|
|
variable.choices,
|
|
|
|
|
variable.version,
|
|
|
|
|
)
|
2025-04-25 14:17:07 +02:00
|
|
|
default = variable.default
|
2025-10-14 12:58:39 +02:00
|
|
|
if default is None and path in self.objectspace.default_multi:
|
|
|
|
|
default = self.objectspace.default_multi[path]
|
2024-11-01 11:17:14 +01:00
|
|
|
self.calculation_to_information(
|
2025-10-14 12:58:39 +02:00
|
|
|
path,
|
2024-11-01 11:17:14 +01:00
|
|
|
"default",
|
2025-04-25 14:17:07 +02:00
|
|
|
default,
|
2024-11-01 11:17:14 +01:00
|
|
|
variable.version,
|
|
|
|
|
)
|
|
|
|
|
self.calculation_to_information(
|
2025-10-14 12:58:39 +02:00
|
|
|
path,
|
2024-11-01 11:17:14 +01:00
|
|
|
"validators",
|
|
|
|
|
variable.validators,
|
|
|
|
|
variable.version,
|
|
|
|
|
)
|
2025-09-22 09:42:46 +02:00
|
|
|
if (
|
|
|
|
|
self.change_default_value
|
2025-10-14 12:58:39 +02:00
|
|
|
and path in self.objectspace.leaders
|
2025-09-22 09:42:46 +02:00
|
|
|
and not default
|
|
|
|
|
):
|
2025-04-05 11:23:47 +02:00
|
|
|
self.add_examples_values(variable)
|
2025-10-14 16:09:25 +02:00
|
|
|
self.add_informations_from_properties(variable)
|
2025-10-14 12:58:39 +02:00
|
|
|
if path in self.objectspace.forced_descriptions:
|
|
|
|
|
self.objectspace.informations.add(
|
|
|
|
|
path,
|
|
|
|
|
"forced_description",
|
|
|
|
|
True,
|
|
|
|
|
)
|
2024-07-10 21:27:48 +02:00
|
|
|
|
2025-10-14 16:09:25 +02:00
|
|
|
def add_informations_from_properties(
|
2024-07-10 21:27:48 +02:00
|
|
|
self,
|
|
|
|
|
variable: dict,
|
|
|
|
|
) -> None:
|
|
|
|
|
"""convert properties"""
|
2024-11-15 08:13:45 +01:00
|
|
|
for prop in PROPERTY_ATTRIBUTE:
|
2024-07-10 21:27:48 +02:00
|
|
|
prop_value = getattr(variable, prop, None)
|
|
|
|
|
if not prop_value:
|
|
|
|
|
continue
|
2024-11-01 11:17:14 +01:00
|
|
|
self.calculation_to_information(
|
|
|
|
|
variable.path,
|
|
|
|
|
prop,
|
|
|
|
|
prop_value,
|
|
|
|
|
variable.version,
|
|
|
|
|
)
|
2025-09-29 21:18:55 +02:00
|
|
|
if isinstance(prop_value, Calculation):
|
|
|
|
|
prop_value.default_values = False
|
2024-11-01 11:17:14 +01:00
|
|
|
|
|
|
|
|
def calculation_to_information(
|
|
|
|
|
self,
|
|
|
|
|
path: str,
|
|
|
|
|
prop: str,
|
|
|
|
|
values,
|
|
|
|
|
version: str,
|
|
|
|
|
):
|
2024-11-15 08:13:45 +01:00
|
|
|
"""tranform calculation to an information"""
|
2025-06-19 06:27:59 +02:00
|
|
|
one_is_calculation = False
|
2024-11-15 08:13:45 +01:00
|
|
|
if not isinstance(values, list):
|
|
|
|
|
if not isinstance(values, Calculation):
|
|
|
|
|
return
|
2025-06-19 06:27:59 +02:00
|
|
|
one_is_calculation = True
|
|
|
|
|
datas = self.calculation_to_string(path, prop, values, version)
|
2024-11-15 08:13:45 +01:00
|
|
|
else:
|
|
|
|
|
datas = []
|
2024-07-10 21:27:48 +02:00
|
|
|
for idx, val in enumerate(values):
|
2025-06-19 06:27:59 +02:00
|
|
|
data = self.calculation_to_string(path, prop, val, version)
|
2024-11-15 08:13:45 +01:00
|
|
|
if data is None:
|
|
|
|
|
continue
|
2025-05-04 14:29:51 +02:00
|
|
|
if "type" in data or "description" in data:
|
2024-11-15 08:13:45 +01:00
|
|
|
one_is_calculation = True
|
|
|
|
|
datas.append(data)
|
2025-06-19 06:27:59 +02:00
|
|
|
if one_is_calculation:
|
|
|
|
|
self.objectspace.informations.add(
|
|
|
|
|
path,
|
|
|
|
|
f"{prop}_calculation",
|
|
|
|
|
datas,
|
|
|
|
|
)
|
2024-11-15 08:13:45 +01:00
|
|
|
|
2025-06-19 06:27:59 +02:00
|
|
|
def calculation_to_string(
|
2024-11-01 11:17:14 +01:00
|
|
|
self,
|
|
|
|
|
path: str,
|
|
|
|
|
prop: str,
|
|
|
|
|
values,
|
|
|
|
|
version: str,
|
|
|
|
|
):
|
|
|
|
|
if not isinstance(values, Calculation):
|
2024-11-20 21:12:56 +01:00
|
|
|
return {"value": values}
|
2025-05-04 14:29:51 +02:00
|
|
|
if values.description:
|
|
|
|
|
return {"description": values.description}
|
2024-07-10 21:27:48 +02:00
|
|
|
if isinstance(values, JinjaCalculation):
|
2025-06-19 06:27:59 +02:00
|
|
|
if values.description:
|
|
|
|
|
value = values.description
|
|
|
|
|
else:
|
|
|
|
|
value = True
|
2024-11-20 21:12:56 +01:00
|
|
|
return {
|
|
|
|
|
"type": "jinja",
|
2025-06-19 06:27:59 +02:00
|
|
|
"value": value,
|
2024-11-20 21:12:56 +01:00
|
|
|
}
|
2024-11-15 08:13:45 +01:00
|
|
|
if isinstance(values, (VariableCalculation, VariablePropertyCalculation)):
|
2025-06-19 06:27:59 +02:00
|
|
|
variable_path = self.get_path_from_variable(values, version, path)
|
|
|
|
|
value = self.calculation_to_information_variable(
|
2025-04-25 14:17:07 +02:00
|
|
|
variable_path, values, prop, version, path
|
2024-11-15 08:13:45 +01:00
|
|
|
)
|
|
|
|
|
if value is None:
|
|
|
|
|
return
|
2024-11-20 21:12:56 +01:00
|
|
|
return {
|
|
|
|
|
"type": "variable",
|
|
|
|
|
"value": value,
|
2025-04-25 14:17:07 +02:00
|
|
|
"ori_path": variable_path,
|
2025-09-29 21:18:55 +02:00
|
|
|
"optional": values.optional,
|
2024-11-20 21:12:56 +01:00
|
|
|
}
|
2024-11-15 08:13:45 +01:00
|
|
|
if isinstance(values, InformationCalculation):
|
2024-11-20 21:12:56 +01:00
|
|
|
return {
|
|
|
|
|
"type": "information",
|
2025-06-19 06:27:59 +02:00
|
|
|
"value": self.calculation_to_information_information(
|
2024-11-20 21:12:56 +01:00
|
|
|
values, version, path
|
|
|
|
|
),
|
|
|
|
|
}
|
2024-11-15 08:13:45 +01:00
|
|
|
if isinstance(values, (IdentifierCalculation, IdentifierPropertyCalculation)):
|
2024-11-20 21:12:56 +01:00
|
|
|
return {
|
|
|
|
|
"type": "identifier",
|
2025-06-19 06:27:59 +02:00
|
|
|
"value": self.calculation_to_information_identifier(
|
2024-11-20 21:12:56 +01:00
|
|
|
values, prop, version
|
|
|
|
|
),
|
|
|
|
|
}
|
2024-11-15 08:13:45 +01:00
|
|
|
if isinstance(values, IndexCalculation):
|
2024-11-20 21:12:56 +01:00
|
|
|
return {
|
|
|
|
|
"type": "index",
|
|
|
|
|
"value": True,
|
|
|
|
|
}
|
2025-03-29 14:37:14 +01:00
|
|
|
if isinstance(values, NamespaceCalculation):
|
|
|
|
|
return {
|
|
|
|
|
"type": "namespace",
|
|
|
|
|
"value": True,
|
|
|
|
|
}
|
|
|
|
|
raise Exception(f'unknown calculation {type(values)} "{values}"')
|
2024-11-15 08:13:45 +01:00
|
|
|
|
2025-06-19 06:27:59 +02:00
|
|
|
def calculation_to_information_variable(
|
2025-05-11 19:13:12 +02:00
|
|
|
self, variable_path: str, values, prop: str, version: str, path: str
|
2024-11-15 08:13:45 +01:00
|
|
|
) -> str:
|
|
|
|
|
# is optional
|
2025-04-25 14:17:07 +02:00
|
|
|
variable = self.objectspace.paths.get_with_dynamic(
|
|
|
|
|
variable_path,
|
|
|
|
|
path,
|
|
|
|
|
values.version,
|
|
|
|
|
values.namespace,
|
|
|
|
|
values.xmlfiles,
|
|
|
|
|
)[0]
|
|
|
|
|
if isinstance(values, VariableCalculation) and values.optional and not variable:
|
|
|
|
|
return None
|
|
|
|
|
if variable:
|
2024-11-15 08:13:45 +01:00
|
|
|
variable_path = variable.path
|
|
|
|
|
if prop in PROPERTY_ATTRIBUTE:
|
|
|
|
|
# get comparative value
|
|
|
|
|
if values.when_not is not undefined:
|
|
|
|
|
value = values.when_not
|
2025-04-28 19:34:24 +02:00
|
|
|
condition = "when_not"
|
|
|
|
|
elif values.when is not undefined:
|
|
|
|
|
value = values.when
|
|
|
|
|
condition = "when"
|
2024-11-07 22:51:28 +01:00
|
|
|
else:
|
2025-04-28 19:34:24 +02:00
|
|
|
value = True
|
|
|
|
|
condition = "when"
|
2024-11-15 08:13:45 +01:00
|
|
|
|
|
|
|
|
# set message
|
2025-04-28 19:34:24 +02:00
|
|
|
values_calculation = (variable_path, value, condition)
|
2024-11-15 08:13:45 +01:00
|
|
|
else:
|
|
|
|
|
values_calculation = variable_path
|
|
|
|
|
|
|
|
|
|
return values_calculation
|
|
|
|
|
|
2025-06-19 06:27:59 +02:00
|
|
|
def calculation_to_information_information(
|
2024-11-15 08:13:45 +01:00
|
|
|
self, values, version: str, path: str
|
|
|
|
|
) -> Union[str, bool]:
|
|
|
|
|
if values.variable:
|
2025-06-19 06:27:59 +02:00
|
|
|
variable_path = self.get_path_from_variable(values, version, path)
|
2024-11-15 08:13:45 +01:00
|
|
|
return _('the value of the information "{0}" of the variable "{1}"').format(
|
|
|
|
|
values.information, variable_path
|
|
|
|
|
)
|
2024-11-20 21:12:56 +01:00
|
|
|
return _('the value of the global information "{0}"').format(values.information)
|
2024-11-15 08:13:45 +01:00
|
|
|
|
2025-06-19 06:27:59 +02:00
|
|
|
def calculation_to_information_identifier(
|
2024-11-15 08:13:45 +01:00
|
|
|
self, values, prop: str, version: str
|
|
|
|
|
) -> Union[str, bool]:
|
|
|
|
|
if version != "1.0" and prop in PROPERTY_ATTRIBUTE:
|
|
|
|
|
if values.when is not undefined:
|
|
|
|
|
return _('when the identifier is "{0}"').format(values.when)
|
|
|
|
|
if values.when_not is not undefined:
|
|
|
|
|
return _('when the identifier is not "{0}"').format(values.when_not)
|
|
|
|
|
return True
|
|
|
|
|
|
2025-06-19 06:27:59 +02:00
|
|
|
def get_path_from_variable(self, values, version: str, path: str) -> str:
|
2024-11-15 08:13:45 +01:00
|
|
|
variable_path = values.variable
|
|
|
|
|
paths = self.objectspace.paths
|
|
|
|
|
if version != "1.0" and paths.regexp_relative.search(variable_path):
|
|
|
|
|
variable_path = paths.get_full_path(
|
|
|
|
|
variable_path,
|
2024-11-07 22:51:28 +01:00
|
|
|
path,
|
|
|
|
|
)
|
2025-03-02 13:38:14 +01:00
|
|
|
elif version == "1.0" and "{{ suffix }}" in variable_path:
|
2025-02-19 16:43:32 +01:00
|
|
|
variable_path = variable_path.replace("{{ suffix }}", "{{ identifier }}")
|
2024-11-15 08:13:45 +01:00
|
|
|
return variable_path
|