334 lines
11 KiB
Python
334 lines
11 KiB
Python
"""Annotate for documentation
|
|
|
|
Silique (https://www.silique.fr)
|
|
Copyright (C) 2024
|
|
|
|
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,
|
|
CONVERT_OPTION,
|
|
PROPERTY_ATTRIBUTE,
|
|
)
|
|
from rougail.output_doc.utils import dump
|
|
|
|
|
|
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.objectspace.informations.add(
|
|
family.path, "dictionaries", family.xmlfiles
|
|
)
|
|
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
|
|
default_values = ["example"]
|
|
if isinstance(value, (VariableCalculation, VariablePropertyCalculation)):
|
|
variable = self.objectspace.paths.get_with_dynamic(
|
|
value.variable,
|
|
value.path_prefix,
|
|
family.path,
|
|
value.version,
|
|
value.namespace,
|
|
value.xmlfiles,
|
|
)[0]
|
|
if variable:
|
|
values = self.get_examples_values(variable)
|
|
if values:
|
|
default_values = values
|
|
if not return_a_list:
|
|
default_values = default_values[0]
|
|
value.default_values = default_values
|
|
|
|
def get_examples_values(self, variable) -> list:
|
|
"""Check examples or test information to define examples values"""
|
|
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)
|
|
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,
|
|
)
|
|
self.calculation_to_information(
|
|
variable.path,
|
|
"default",
|
|
variable.default,
|
|
variable.version,
|
|
)
|
|
self.calculation_to_information(
|
|
variable.path,
|
|
"validators",
|
|
variable.validators,
|
|
variable.version,
|
|
)
|
|
if variable.path in self.objectspace.leaders and not variable.default:
|
|
values = self.get_examples_values(variable)
|
|
if values:
|
|
variable.default = list(values)
|
|
else:
|
|
variable.default = [CONVERT_OPTION[variable.type]["example"]]
|
|
self.objectspace.informations.add(variable.path, "fake_default", True)
|
|
self.objectspace.informations.add(
|
|
variable.path, "dictionaries", variable.xmlfiles
|
|
)
|
|
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)):
|
|
value = self._calculation_to_information_variable(
|
|
values, prop, version, path
|
|
)
|
|
if value is None:
|
|
return
|
|
return {
|
|
"type": "variable",
|
|
"value": value,
|
|
}
|
|
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,
|
|
}
|
|
raise Exception('unknown calculation "{values}"')
|
|
|
|
def _calculation_to_information_jinja(self, values):
|
|
if values.description:
|
|
return values.description
|
|
return True
|
|
|
|
def _calculation_to_information_variable(
|
|
self, values, prop: str, version: str, path: str
|
|
) -> str:
|
|
# is optional
|
|
if isinstance(values, VariableCalculation) and values.optional:
|
|
variable = self.objectspace.paths.get_with_dynamic(
|
|
values.variable,
|
|
values.path_prefix,
|
|
path,
|
|
values.version,
|
|
values.namespace,
|
|
values.xmlfiles,
|
|
)[0]
|
|
if not variable:
|
|
return None
|
|
variable_path = variable.path
|
|
else:
|
|
variable_path = self._get_path_from_variable(values, version, path)
|
|
if prop in PROPERTY_ATTRIBUTE:
|
|
# get comparative value
|
|
if values.when_not is not undefined:
|
|
value = values.when_not
|
|
msg = _('when the variable "{0}" hasn\'t the value "{1}"')
|
|
else:
|
|
if values.when is not undefined:
|
|
value = values.when
|
|
else:
|
|
value = True
|
|
msg = _('when the variable "{0}" has the value "{1}"')
|
|
if not isinstance(value, str):
|
|
value = dump(value)
|
|
|
|
# set message
|
|
values_calculation = msg.format(variable_path, value)
|
|
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,
|
|
)
|
|
return variable_path
|