""" Silique (https://www.silique.fr) Copyright (C) 2024-2025 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 """ from typing import Union from tiramisu import undefined from rougail.annotator.variable import Walk from rougail.output_doc.i18n import _ from rougail.object_model import ( Calculation, JinjaCalculation, VariableCalculation, VariablePropertyCalculation, IdentifierCalculation, IdentifierPropertyCalculation, InformationCalculation, IndexCalculation, NamespaceCalculation, CONVERT_OPTION, PROPERTY_ATTRIBUTE, ) class Annotator(Walk): """Annotate for documentation""" level = 95 def __init__( self, objectspace, *args, # pylint: disable=unused-argument ) -> 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): self.dynamic_values(family, family.dynamic) else: for value in family.dynamic: self.dynamic_values(family, value, return_a_list=False) self.calculation_to_information( family.path, "dynamic", family.dynamic, family.version, ) 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 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""" 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 ) return values def populate_variable(self) -> None: """convert variables""" for variable in self.get_variables(): if variable.type == "symlink": continue if variable.type == "choice": self.calculation_to_information( variable.path, "choice", variable.choices, variable.version, ) default = variable.default if default is None and variable.path in self.objectspace.default_multi: default = self.objectspace.default_multi[variable.path] self.calculation_to_information( variable.path, "default", default, variable.version, ) self.calculation_to_information( variable.path, "validators", variable.validators, variable.version, ) if variable.path in self.objectspace.leaders and not default: self.add_examples_values(variable) self.convert_variable_property(variable) def convert_variable_property( self, variable: dict, ) -> None: """convert properties""" for prop in PROPERTY_ATTRIBUTE: prop_value = getattr(variable, prop, None) if not prop_value: continue self.calculation_to_information( variable.path, prop, prop_value, variable.version, ) def calculation_to_information( self, path: str, prop: str, values, version: str, ): """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 for idx, val in enumerate(values): data = self._calculation_to_string(path, prop, val, version) if data is None: continue if "type" in data: one_is_calculation = True datas.append(data) if one_is_calculation: self._calculation_to_information( path, prop, datas, ) def _calculation_to_information( 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( self, path: str, prop: str, values, version: str, ): if not isinstance(values, Calculation): return {"value": values} if isinstance(values, JinjaCalculation): return { "type": "jinja", "value": self._calculation_to_information_jinja(values), } if isinstance(values, (VariableCalculation, VariablePropertyCalculation)): variable_path = self._get_path_from_variable(values, version, path) value = self._calculation_to_information_variable( variable_path, values, prop, version, path ) if value is None: return return { "type": "variable", "value": value, "ori_path": variable_path, } if isinstance(values, InformationCalculation): return { "type": "information", "value": self._calculation_to_information_information( values, version, path ), } if isinstance(values, (IdentifierCalculation, IdentifierPropertyCalculation)): return { "type": "identifier", "value": self._calculation_to_information_identifier( values, prop, version ), } if isinstance(values, IndexCalculation): return { "type": "index", "value": True, } if isinstance(values, NamespaceCalculation): return { "type": "namespace", "value": True, } raise Exception(f'unknown calculation {type(values)} "{values}"') def _calculation_to_information_jinja(self, values): if values.description: return values.description return True def _calculation_to_information_variable( self, variable_path: str, values, prop: str, version: str, path: str ) -> str: # is optional 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: 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" # set message values_calculation = (variable_path, value, condition) 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 ) return _('the value of the global information "{0}"').format(values.information) 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 }}") return variable_path