risotto/sbin/risotto_auto_doc

369 lines
19 KiB
Text
Raw Normal View History

2022-12-25 17:21:03 +01:00
#!/usr/bin/env python3
from os import listdir
from os.path import isdir, join
from tabulate import tabulate
2023-02-27 14:03:56 +01:00
from sys import argv
2022-12-25 17:21:03 +01:00
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'
2023-02-27 14:03:56 +01:00
ROUGAIL_VARIABLE_TYPE = 'https://forge.cloud.silique.fr/risotto/rougail/src/branch/main/doc/variable/README.md#le-type-de-la-variable'
2022-12-25 17:21:03 +01:00
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
2023-02-27 14:03:56 +01:00
def parse(applicationservice, elts, dico, providers_suppliers, hidden, objectspace):
2022-12-25 17:21:03 +01:00
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
2023-01-23 20:23:32 +01:00
if isinstance(default, objectspace.value):
default = '<calculated>'
2022-12-25 17:21:03 +01:00
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:
2023-02-27 14:03:56 +01:00
parse(applicationservice, elts + [child], dico, providers_suppliers, hidden, objectspace)
2022-12-25 17:21:03 +01:00
2023-02-27 14:03:56 +01:00
def build_dependencies_tree(applicationservice, applicationservice_data, applicationservices_data, applicationservices_data_ext, space):
2022-12-25 17:21:03 +01:00
depends = []
if applicationservice_data['depends']:
2023-02-27 14:03:56 +01:00
if applicationservice in applicationservices_data:
app_data = applicationservices_data[applicationservice]
else:
for url, apps_data in applicationservices_data_ext.items():
if applicationservice in apps_data:
app_data = apps_data[applicationservice]
break
else:
raise Exception(f'cannot find applicationservice "{applicationservice}"')
for idx, depend in enumerate(app_data['depends']):
if depend in applicationservices_data:
url = '..'
ext = False
else:
for url, apps_data in applicationservices_data_ext.items():
if depend in apps_data:
break
else:
raise Exception(f'cannot find applicationservice "{applicationservice}"')
ext = True
subdepends = build_dependencies_tree(depend, applicationservice_data, applicationservices_data, applicationservices_data_ext, space + 2)
2022-12-25 17:21:03 +01:00
if not idx or subdepends:
title = '\n'
else:
title = ''
2023-02-27 14:03:56 +01:00
depend_desc = depend
if ext:
depend_desc += ' (in external dataset)'
title = ' ' * space + f'- [{depend_desc}]({url}/{depend}/README.md)'
2022-12-25 17:21:03 +01:00
depends.append(title)
depends.extend(subdepends)
return depends
2023-02-27 14:03:56 +01:00
def load_data(url, directory, applicationservices_data, global_data={}):
root_path = join(directory, 'seed')
applicationservices = listdir(root_path)
tmps = {}
for applicationservice in applicationservices:
as_dir = join(root_path, applicationservice)
if not isdir(as_dir):
continue
applicationservice_data = load_application_service(as_dir)
if not applicationservice_data.get('documentation', True):
continue
applicationservices_data[applicationservice] = {'description': applicationservice_data['description'],
'website': applicationservice_data.get('website'),
'as_dir': as_dir,
'depends': [],
'used_by': [],
}
if applicationservice in tmps:
for app in tmps.pop(applicationservice):
used_by = f'[{app}](../{app}/README.md)'
applicationservices_data[applicationservice]['used_by'].append(used_by)
if 'depends' in applicationservice_data:
for depend in applicationservice_data['depends']:
applicationservices_data[applicationservice]['depends'].append(depend)
if depend in applicationservices_data:
used_by = f'[{applicationservice}](../{applicationservice}/README.md)'
applicationservices_data[depend]['used_by'].append(used_by)
else:
tmps.setdefault(depend, []).append(applicationservice)
if tmps and global_data:
for depend, applications in tmps.items():
for app in applications:
used_by = f'[{app} (in external dataset)]({url}/{app}/README.md)'
global_data[depend]['used_by'].append(used_by)
def write_data(applicationservices_data, applicationservices_data_ext):
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, just_doc=True)
converted.load_dictionaries()
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, objectspace)
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'## 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, applicationservice_data, applicationservices_data, applicationservices_data_ext, 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')
2022-12-25 17:21:03 +01:00
as_fh.write('\n')
2023-02-27 14:03:56 +01:00
# 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')
if len(applicationservice_data['used_by']) == 1:
link = applicationservice_data['used_by'][0]
as_fh.write(f'{link}\n')
else:
for link in applicationservice_data['used_by']:
as_fh.write(f'- {link}\n')
linked = []
for provider, provider_as in providers_suppliers['providers'].items():
if not applicationservice in provider_as:
2022-12-25 17:21:03 +01:00
continue
2023-02-27 14:03:56 +01:00
for supplier in providers_suppliers['suppliers'][provider]:
if supplier in linked:
continue
linked.append(supplier)
linked.sort()
if linked:
if len(linked) == 1:
as_fh.write('\n## Supplier\n\n')
as_fh.write(f'[{linked[0]}](../{linked[0]}/README.md)\n')
else:
as_fh.write('\n## Suppliers\n\n')
for supplier in linked:
as_fh.write(f'- [{supplier}](../{supplier}/README.md)\n')
linked = []
for supplier, supplier_as in providers_suppliers['suppliers'].items():
if not applicationservice in supplier_as:
2022-12-25 17:21:03 +01:00
continue
2023-02-27 14:03:56 +01:00
for provider in providers_suppliers['providers'][supplier]:
if provider in linked:
continue
linked.append(provider)
linked.sort()
if linked:
if len(linked) == 1:
as_fh.write('\n## Provider\n\n')
as_fh.write(f'[{linked[0]}](../{linked[0]}/README.md)\n')
else:
as_fh.write('\n## Providers\n\n')
for provider in linked:
as_fh.write(f'- [{provider}](../{provider}/README.md)\n')
as_fh.write(f'\n[All applications services for this dataset.](../README.md)\n')
2022-12-25 17:21:03 +01:00
2023-02-27 14:03:56 +01:00
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]
2022-12-25 17:21:03 +01:00
applicationservice_data = applicationservices_data[applicationservice]
2023-02-27 14:03:56 +01:00
as_fh.write(f'- [{applicationservice}]({applicationservice}/README.md): {applicationservice_data["description"]}\n')
2023-01-23 20:23:32 +01:00
else:
2023-02-27 14:03:56 +01:00
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')
providers = list(providers_suppliers['providers'].keys())
providers.sort()
if providers:
as_fh.write('\n# Providers and suppliers\n\n')
for provider in providers:
as_fh.write(f'- {provider}:\n')
if providers_suppliers['providers'][provider]:
if len(providers_suppliers['providers'][provider]) == 1:
applicationservice = providers_suppliers['providers'][provider][0]
as_fh.write(f' - Provider: [{applicationservice}]({applicationservice}/README.md)\n')
else:
as_fh.write(f' - Providers:\n')
for applicationservice in providers_suppliers['providers'][provider]:
as_fh.write(f' - [{applicationservice}]({applicationservice}/README.md)\n')
if providers_suppliers['suppliers']:
if len(providers_suppliers['suppliers'][provider]) == 1:
applicationservice = providers_suppliers['suppliers'][provider][0]
as_fh.write(f' - Supplier: [{applicationservice}]({applicationservice}/README.md)\n')
else:
as_fh.write(f' - Suppliers:\n')
for applicationservice in providers_suppliers['suppliers'][provider]:
as_fh.write(f' - [{applicationservice}]({applicationservice}/README.md)\n')
def main():
applicationservices_data = {}
load_data('..', '', applicationservices_data)
applicationservices_data_ext = {}
for arg in argv[1:]:
if '=' not in arg:
raise Exception(f'cannot parse argument "{arg}", should be dataset_path=url')
path, url = arg.split('=', 1)
if url in applicationservices_data_ext:
raise Exception(f'duplicate url "{url}" in arguments')
applicationservices_data_ext[url] = {}
load_data(url, path, applicationservices_data_ext[url], applicationservices_data)
write_data(applicationservices_data, applicationservices_data_ext)
if __name__ == '__main__':
main()