add risotto_auto_doc script
This commit is contained in:
parent
23bacdb9c6
commit
124c6b56d2
4 changed files with 293 additions and 8 deletions
277
sbin/risotto_auto_doc
Executable file
277
sbin/risotto_auto_doc
Executable file
|
@ -0,0 +1,277 @@
|
|||
#!/usr/bin/env python3
|
||||
from os import listdir
|
||||
from os.path import isdir, join
|
||||
from tabulate import tabulate
|
||||
|
||||
from rougail import RougailConfig
|
||||
from rougail.convert import RougailConvert
|
||||
from rougail.objspace import RootRougailObject
|
||||
from risotto.utils import EXTRA_ANNOTATORS, ROUGAIL_NAMESPACE, ROUGAIL_NAMESPACE_DESCRIPTION
|
||||
from risotto.image import load_application_service
|
||||
|
||||
|
||||
rougailconfig = RougailConfig
|
||||
rougailconfig['variable_namespace'] = ROUGAIL_NAMESPACE
|
||||
rougailconfig['variable_namespace_description'] = ROUGAIL_NAMESPACE_DESCRIPTION
|
||||
|
||||
|
||||
DEFAULT_TYPE = 'string'
|
||||
ROUGAIL_VARIABLE_TYPE = 'https://cloud.silique.fr/gitea/risotto/rougail/src/branch/main/doc/variable/README.md#le-type-de-la-variable'
|
||||
|
||||
|
||||
def add_title_family(elts, dico):
|
||||
for idx, elt in enumerate(elts):
|
||||
description = elt.doc
|
||||
if not idx:
|
||||
description = description.capitalize()
|
||||
space = idx + 3
|
||||
title = '#' * space + f' {description} (*{elt.path}*)'
|
||||
if title not in dico:
|
||||
dico[title] = {'variables': [], 'help': '', 'type': ''}
|
||||
if hasattr(elt, 'information') and hasattr(elt.information, 'help'):
|
||||
dico[title]['help'] = elt.information.help
|
||||
if hasattr(elt, 'suffixes') and elt.suffixes:
|
||||
dico[title]['type'] = 'dynamic'
|
||||
dico[title]['suffixes'] = elt.suffixes.path
|
||||
if hasattr(elt, 'leadership') and elt.leadership:
|
||||
dico[title]['type'] = 'leadership'
|
||||
return title
|
||||
|
||||
|
||||
|
||||
def parse(applicationservice, elts, dico, providers_suppliers, hidden):
|
||||
elt = elts[-1]
|
||||
first_variable = True
|
||||
if not hidden:
|
||||
hidden = hasattr(elt, 'properties') and ('hidden' in elt.properties or 'disabled' in elt.properties)
|
||||
is_leadership = hasattr(elt, 'leadership') and elt.leadership
|
||||
for children in vars(elt).values():
|
||||
if isinstance(children, dict):
|
||||
children = list(children.values())
|
||||
if not isinstance(children, list):
|
||||
continue
|
||||
for idx, child in enumerate(children):
|
||||
if isinstance(child, objectspace.property_) or \
|
||||
not isinstance(child, RootRougailObject):
|
||||
continue
|
||||
if isinstance(child, objectspace.variable):
|
||||
if not hidden and (not hasattr(child, 'properties') or ('hidden' not in child.properties and not 'disabled' in child.properties)):
|
||||
if first_variable:
|
||||
title = add_title_family(elts, dico)
|
||||
first_variable = False
|
||||
var_title = child.doc
|
||||
if hasattr(child, 'properties') and 'mandatory' in child.properties:
|
||||
var_title = '**' + var_title + '**'
|
||||
var_path = child.xmlfiles[-1].split('/', 2)[-1]
|
||||
if child.doc != child.name:
|
||||
var_title += f' (*[{child.name}]({var_path})*)'
|
||||
else:
|
||||
var_title = f'*[{var_title}]({var_path})*'
|
||||
if ((idx == 0 or not is_leadership) and child.multi is True) or (idx != 0 and is_leadership and child.multi == 'submulti'):
|
||||
var_title += ' [+]'
|
||||
values = {'description': var_title,
|
||||
}
|
||||
if hasattr(child, 'information') and hasattr(child.information, 'help'):
|
||||
values['help'] = child.information.help
|
||||
if child.type != DEFAULT_TYPE:
|
||||
values['type'] = child.type
|
||||
if hasattr(child, 'default'):
|
||||
default = child.default
|
||||
if isinstance(default, list):
|
||||
default = '<br />'.join(default)
|
||||
values['values'] = default
|
||||
if hasattr(child, 'choice'):
|
||||
values['choices'] = '<br />'.join([choice.name for choice in child.choice])
|
||||
if hasattr(child, 'provider'):
|
||||
provider = child.provider
|
||||
values['provider'] = provider
|
||||
if ':' not in provider:
|
||||
providers_suppliers['providers'].setdefault(provider, []).append(applicationservice)
|
||||
if hasattr(child, 'supplier'):
|
||||
supplier = child.supplier
|
||||
values['supplier'] = supplier
|
||||
if ':' not in supplier:
|
||||
providers_suppliers['suppliers'].setdefault(supplier, []).append(applicationservice)
|
||||
dico[title]['variables'].append(values)
|
||||
else:
|
||||
if hasattr(child, 'provider'):
|
||||
provider = child.provider
|
||||
if ':' not in provider:
|
||||
providers_suppliers['providers'].setdefault(provider, []).append(applicationservice)
|
||||
if hasattr(child, 'supplier'):
|
||||
supplier = child.supplier
|
||||
if ':' not in supplier:
|
||||
providers_suppliers['suppliers'].setdefault(supplier, []).append(applicationservice)
|
||||
else:
|
||||
parse(applicationservice, elts + [child], dico, providers_suppliers, hidden)
|
||||
|
||||
|
||||
applicationservices = listdir('seed')
|
||||
#applicationservices = ['speedtest-rs']
|
||||
#applicationservices = ['redis']
|
||||
applicationservices_data = {}
|
||||
tmps = {}
|
||||
for applicationservice in applicationservices:
|
||||
as_dir = join('seed', applicationservice)
|
||||
if not isdir(as_dir):
|
||||
continue
|
||||
applicationservice_data = load_application_service(as_dir)
|
||||
applicationservices_data[applicationservice] = {'description': applicationservice_data['description'],
|
||||
'website': applicationservice_data.get('website'),
|
||||
'as_dir': as_dir,
|
||||
'depends': [],
|
||||
'used_by': [],
|
||||
}
|
||||
if applicationservice in tmps:
|
||||
applicationservices_data[applicationservice]['used_by'] = tmps.pop(applicationservice)
|
||||
if 'depends' in applicationservice_data:
|
||||
for depend in applicationservice_data['depends']:
|
||||
applicationservices_data[applicationservice]['depends'].append(depend)
|
||||
if depend in applicationservices_data:
|
||||
applicationservices_data[depend]['used_by'].append(applicationservice)
|
||||
else:
|
||||
tmps.setdefault(depend, []).append(applicationservice)
|
||||
|
||||
dico = {}
|
||||
providers_suppliers = {'providers': {}, 'suppliers': {}}
|
||||
for applicationservice, applicationservice_data in applicationservices_data.items():
|
||||
as_dir = applicationservice_data['as_dir']
|
||||
dirname = join(as_dir, 'dictionaries')
|
||||
if isdir(dirname):
|
||||
rougailconfig['dictionaries_dir'] = [dirname]
|
||||
else:
|
||||
rougailconfig['dictionaries_dir'] = []
|
||||
dirname_extras = join(as_dir, 'extras')
|
||||
extra_dictionaries = {}
|
||||
if isdir(dirname_extras):
|
||||
for extra in listdir(dirname_extras):
|
||||
extra_dir = join(dirname_extras, extra)
|
||||
if isdir(extra_dir):
|
||||
extra_dictionaries.setdefault(extra, []).append(extra_dir)
|
||||
if not isdir(dirname) and not extra_dictionaries:
|
||||
continue
|
||||
rougailconfig['extra_dictionaries'] = extra_dictionaries
|
||||
converted = RougailConvert(rougailconfig)
|
||||
converted.load_dictionaries(just_doc=True)
|
||||
converted.annotate()
|
||||
objectspace = converted.rougailobjspace
|
||||
if hasattr(objectspace.space, 'variables'):
|
||||
dico[applicationservice] = {}
|
||||
for name, elt in objectspace.space.variables.items():
|
||||
parse(applicationservice, [elt], dico[applicationservice], providers_suppliers, False)
|
||||
|
||||
|
||||
def build_dependencies_tree(applicationservice, space):
|
||||
depends = []
|
||||
if applicationservice_data['depends']:
|
||||
for idx, depend in enumerate(applicationservices_data[applicationservice]['depends']):
|
||||
subdepends = build_dependencies_tree(depend, space + 2)
|
||||
if not idx or subdepends:
|
||||
title = '\n'
|
||||
else:
|
||||
title = ''
|
||||
title = ' ' * space + f'- [{depend}](../{depend}/README.md)'
|
||||
depends.append(title)
|
||||
depends.extend(subdepends)
|
||||
return depends
|
||||
|
||||
|
||||
for applicationservice, applicationservice_data in applicationservices_data.items():
|
||||
as_dir = applicationservice_data['as_dir']
|
||||
with open(join(as_dir, 'README.md'), 'w') as as_fh:
|
||||
as_fh.write(f'---\ngitea: none\ninclude_toc: true\n---\n\n')
|
||||
as_fh.write(f'# {applicationservice}\n\n')
|
||||
as_fh.write(f'[All applications services for this dataset.](../README.md)\n\n')
|
||||
as_fh.write(f'## Description\n\n')
|
||||
description = applicationservice_data['description'] + '.\n'
|
||||
if applicationservice_data['website']:
|
||||
description += f'\n[For more informations]({applicationservice_data["website"]})\n'
|
||||
as_fh.write(description)
|
||||
if applicationservice_data['depends']:
|
||||
as_fh.write(f'\n## Dependances\n\n')
|
||||
for depend in build_dependencies_tree(applicationservice, 0):
|
||||
as_fh.write(f'{depend}\n')
|
||||
if applicationservice in dico and dico[applicationservice]:
|
||||
as_fh.write('\n## Variables\n\n')
|
||||
for title, data in dico[applicationservice].items():
|
||||
as_fh.write(f'{title}\n')
|
||||
if data['type'] == 'leadership':
|
||||
as_fh.write('\nThis a family is a leadership.\n')
|
||||
if data['type'] == 'dynamic':
|
||||
as_fh.write(f'\nThis a dynamic family generated from the variable "{data["suffixes"]}".\n')
|
||||
if data['help']:
|
||||
as_fh.write(f'\n{data["help"]}\n')
|
||||
keys = []
|
||||
if data['variables']:
|
||||
variables = data['variables']
|
||||
for variable in variables:
|
||||
for key in variable:
|
||||
if key not in keys:
|
||||
keys.append(key)
|
||||
values = []
|
||||
for variable in variables:
|
||||
value = []
|
||||
for key in keys:
|
||||
if key in variable:
|
||||
val = variable[key]
|
||||
elif key == 'type':
|
||||
val = DEFAULT_TYPE
|
||||
else:
|
||||
val = ''
|
||||
if key == 'type':
|
||||
val = f'[{val}]({ROUGAIL_VARIABLE_TYPE})'
|
||||
value.append(val)
|
||||
values.append(value)
|
||||
as_fh.write('\n')
|
||||
as_fh.write(tabulate(values, headers=[key.capitalize() for key in keys], tablefmt="github"))
|
||||
as_fh.write('\n')
|
||||
as_fh.write('\n')
|
||||
# FIXME if not applicationservice_data['used_by']:
|
||||
# FIXME as_fh.write('\n## Variables with dependencies\n\n')
|
||||
as_fh.write('\n- [+]: variable is multiple\n- **bold**: variable is mandatory\n')
|
||||
if applicationservice_data['used_by']:
|
||||
as_fh.write('\n## Used by\n\n')
|
||||
for link in applicationservice_data['used_by']:
|
||||
as_fh.write(f'- [{link}](../{link}/README.md)\n')
|
||||
linked = []
|
||||
for provider, provider_as in providers_suppliers['providers'].items():
|
||||
if not applicationservice in provider_as:
|
||||
continue
|
||||
for supplier in providers_suppliers['suppliers'][provider]:
|
||||
if not linked:
|
||||
as_fh.write('\n## Linked to\n\n')
|
||||
if supplier in linked:
|
||||
continue
|
||||
as_fh.write(f'- [{supplier}](../{supplier}/README.md)\n')
|
||||
linked.append(supplier)
|
||||
for supplier, supplier_as in providers_suppliers['suppliers'].items():
|
||||
if not applicationservice in supplier_as:
|
||||
continue
|
||||
for provider in providers_suppliers['providers'][supplier]:
|
||||
if not linked:
|
||||
as_fh.write('\n## Linked to\n\n')
|
||||
if provider in linked:
|
||||
continue
|
||||
as_fh.write(f'- [{provider}](../{provider}/README.md)\n')
|
||||
linked.append(provider)
|
||||
|
||||
|
||||
with open('seed/README.md', 'w') as as_fh:
|
||||
as_fh.write('# Application services\n\n')
|
||||
applicationservices = {}
|
||||
for applicationservice in applicationservices_data:
|
||||
applicationservices.setdefault(applicationservice.split('-')[0], []).append(applicationservice)
|
||||
applicationservice_categories = list(applicationservices.keys())
|
||||
applicationservice_categories.sort()
|
||||
for category in applicationservice_categories:
|
||||
applicationservices_ = applicationservices[category]
|
||||
if len(applicationservices_) == 1:
|
||||
applicationservice = applicationservices_[0]
|
||||
applicationservice_data = applicationservices_data[applicationservice]
|
||||
as_fh.write(f'- [{applicationservice}]({applicationservice}/README.md): {applicationservice_data["description"]}\n')
|
||||
else:
|
||||
as_fh.write(f'- {category}:\n')
|
||||
applicationservices_.sort()
|
||||
for applicationservice in applicationservices_:
|
||||
applicationservice_data = applicationservices_data[applicationservice]
|
||||
as_fh.write(f' - [{applicationservice}]({applicationservice}/README.md): {applicationservice_data["description"]}\n')
|
|
@ -24,6 +24,9 @@ class ModuleCfg():
|
|||
def __repr__(self):
|
||||
return str(vars(self))
|
||||
|
||||
def load_application_service(as_dir: str) -> str:
|
||||
with open(join(as_dir, 'applicationservice.yml')) as yaml:
|
||||
return yaml_load(yaml, Loader=SafeLoader)
|
||||
|
||||
class Applications:
|
||||
def __init__(self) -> None:
|
||||
|
@ -119,8 +122,7 @@ class Modules:
|
|||
self._load_applicationservice_directories(as_dir,
|
||||
cfg,
|
||||
)
|
||||
with open(join(as_dir, 'applicationservice.yml')) as yaml:
|
||||
app = yaml_load(yaml, Loader=SafeLoader)
|
||||
app = load_application_service(as_dir)
|
||||
provider = app.get('provider')
|
||||
if provider:
|
||||
cfg.providers.setdefault(provider, [])
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from .utils import MULTI_FUNCTIONS, load_zones, value_pprint, RISOTTO_CONFIG
|
||||
from .utils import MULTI_FUNCTIONS, load_zones, value_pprint, RISOTTO_CONFIG, EXTRA_ANNOTATORS, ROUGAIL_NAMESPACE, ROUGAIL_NAMESPACE_DESCRIPTION
|
||||
from .image import Applications, Modules, valid_mandatories, applicationservice_copy
|
||||
from .rougail.annotator import calc_providers, calc_providers_global, calc_providers_dynamic, calc_providers_dynamic_follower, calc_providers_follower
|
||||
|
||||
|
@ -27,8 +27,6 @@ def tiramisu_display_name(kls,
|
|||
|
||||
|
||||
CONFIG_FILE = 'servers.yml'
|
||||
ROUGAIL_NAMESPACE = 'general'
|
||||
ROUGAIL_NAMESPACE_DESCRIPTION = 'Général'
|
||||
TIRAMISU_CACHE = 'tiramisu_cache.py'
|
||||
VALUES_CACHE = 'values_cache.json'
|
||||
INFORMATIONS_CACHE = 'informations_cache.json'
|
||||
|
@ -148,7 +146,7 @@ class Loader:
|
|||
cfg['variable_namespace'] = ROUGAIL_NAMESPACE
|
||||
cfg['variable_namespace_description'] = ROUGAIL_NAMESPACE_DESCRIPTION
|
||||
cfg['multi_functions'] = MULTI_FUNCTIONS
|
||||
cfg['extra_annotators'] = ['risotto.rougail']
|
||||
cfg['extra_annotators'] = EXTRA_ANNOTATORS
|
||||
cfg['internal_functions'] = list(FUNCTIONS.keys())
|
||||
cfg['force_convert_dyn_option_description'] = True
|
||||
cfg['risotto_globals'] = {}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
from os import environ
|
||||
from os.path import isfile
|
||||
from typing import List
|
||||
from ipaddress import ip_address
|
||||
from toml import load as toml_load
|
||||
|
@ -6,10 +7,17 @@ from pprint import pprint
|
|||
|
||||
|
||||
MULTI_FUNCTIONS = []
|
||||
EXTRA_ANNOTATORS = ['risotto.rougail']
|
||||
ROUGAIL_NAMESPACE = 'general'
|
||||
ROUGAIL_NAMESPACE_DESCRIPTION = 'Général'
|
||||
|
||||
|
||||
with open(environ.get('CONFIG_FILE', 'risotto.conf'), 'r') as fh:
|
||||
RISOTTO_CONFIG = toml_load(fh)
|
||||
config_file = environ.get('CONFIG_FILE', 'risotto.conf')
|
||||
if isfile(config_file):
|
||||
with open(config_file, 'r') as fh:
|
||||
RISOTTO_CONFIG = toml_load(fh)
|
||||
else:
|
||||
RISOTTO_CONFIG = {}
|
||||
|
||||
|
||||
def _(s):
|
||||
|
|
Loading…
Reference in a new issue