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

339 lines
11 KiB
Python
Raw Normal View History

"""
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-05-09 07:45:10 +02:00
from rougail.utils import undefined
2024-07-10 21:27:48 +02:00
from rougail.annotator.variable import Walk
from rougail.output_doc.i18n import _
2024-11-01 11:17:14 +01:00
from rougail.object_model import (
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,
PROPERTY_ATTRIBUTE,
)
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
self.populate_family()
self.populate_variable()
def populate_family(self) -> None:
"""Set doc, path, ... to family"""
for family in self.get_families():
self.convert_variable_property(family)
if family.type != "dynamic":
continue
if not isinstance(family.dynamic, list):
2024-11-15 08:13:45 +01:00
self.dynamic_values(family, family.dynamic)
2024-07-10 21:27:48 +02:00
else:
for value in family.dynamic:
2024-11-15 08:13:45 +01:00
self.dynamic_values(family, value, return_a_list=False)
2024-11-01 11:17:14 +01:00
self.calculation_to_information(
family.path,
"dynamic",
family.dynamic,
family.version,
)
2024-07-10 21:27:48 +02:00
2024-11-15 08:13:45 +01:00
def dynamic_values(
self,
family,
value,
*,
return_a_list=True,
) -> None:
"""For dynamic we must have values to document it"""
if not isinstance(value, Calculation):
return
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]
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)
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
if variable.type == "choice":
2024-11-01 11:17:14 +01:00
self.calculation_to_information(
variable.path,
"choice",
variable.choices,
variable.version,
)
2025-04-25 14:17:07 +02:00
default = variable.default
if default is None and variable.path in self.objectspace.default_multi:
default = self.objectspace.default_multi[variable.path]
2024-11-01 11:17:14 +01:00
self.calculation_to_information(
variable.path,
"default",
2025-04-25 14:17:07 +02:00
default,
2024-11-01 11:17:14 +01:00
variable.version,
)
self.calculation_to_information(
variable.path,
"validators",
variable.validators,
variable.version,
)
2025-04-25 14:17:07 +02:00
if variable.path in self.objectspace.leaders and not default:
self.add_examples_values(variable)
2024-07-10 21:27:48 +02:00
self.convert_variable_property(variable)
def convert_variable_property(
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,
)
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"""
if not isinstance(values, list):
if not isinstance(values, Calculation):
return
self._calculation_to_information(
path,
prop,
self._calculation_to_string(path, prop, values, version),
)
else:
datas = []
one_is_calculation = False
2024-07-10 21:27:48 +02:00
for idx, val in enumerate(values):
2024-11-15 08:13:45 +01:00
data = self._calculation_to_string(path, prop, val, version)
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)
if one_is_calculation:
2024-11-01 11:17:14 +01:00
self._calculation_to_information(
path,
prop,
2024-11-15 08:13:45 +01:00
datas,
2024-11-01 11:17:14 +01:00
)
def _calculation_to_information(
2024-11-15 08:13:45 +01:00
self,
path: str,
prop: str,
datas: Union[dict, list[dict]],
) -> None:
if datas is None:
return
self.objectspace.informations.add(
path,
f"{prop}_calculation",
datas,
)
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):
2024-11-20 21:12:56 +01:00
return {
"type": "jinja",
"value": self._calculation_to_information_jinja(values),
}
2024-11-15 08:13:45 +01:00
if isinstance(values, (VariableCalculation, VariablePropertyCalculation)):
2025-04-25 14:17:07 +02:00
variable_path = self._get_path_from_variable(values, version, path)
2024-11-15 08:13:45 +01:00
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,
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",
"value": self._calculation_to_information_information(
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",
"value": self._calculation_to_information_identifier(
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
def _calculation_to_information_jinja(self, values):
if values.description:
return values.description
return True
def _calculation_to_information_variable(
2025-04-25 14:17:07 +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
condition = "when_not"
elif values.when is not undefined:
value = values.when
condition = "when"
else:
value = True
condition = "when"
2024-11-15 08:13:45 +01:00
# set message
values_calculation = (variable_path, value, condition)
2024-11-15 08:13:45 +01:00
else:
values_calculation = variable_path
return values_calculation
def _calculation_to_information_information(
self, values, version: str, path: str
) -> Union[str, bool]:
if values.variable:
variable_path = self._get_path_from_variable(values, version, path)
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
def _calculation_to_information_identifier(
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
def _get_path_from_variable(self, values, version: str, path: str) -> str:
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,
path,
)
elif version == "1.0" and "{{ suffix }}" in variable_path:
variable_path = variable_path.replace("{{ suffix }}", "{{ identifier }}")
2024-11-15 08:13:45 +01:00
return variable_path