From 33e4b637319d5984ee7bbd1bba48912fac884302 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Wed, 23 Aug 2023 22:42:53 +0200 Subject: [PATCH] update doc tools --- sbin/risotto_auto_doc | 772 +++++++++++++++++++++++++++++++----------- src/risotto/utils.py | 2 +- 2 files changed, 584 insertions(+), 190 deletions(-) diff --git a/sbin/risotto_auto_doc b/sbin/risotto_auto_doc index ff7a8d9..ca45548 100755 --- a/sbin/risotto_auto_doc +++ b/sbin/risotto_auto_doc @@ -1,14 +1,21 @@ #!/usr/bin/env python3 -from os import listdir -from os.path import isdir, join + + +from os import listdir, unlink +from os.path import isdir, isfile, join, abspath from tabulate import tabulate from sys import argv +from yaml import dump +from ruamel.yaml import YAML +from glob import glob from rougail import RougailConfig from rougail.convert import RougailConvert from rougail.objspace import RootRougailObject +from rougail.annotator.family import Mode from risotto.utils import EXTRA_ANNOTATORS, ROUGAIL_NAMESPACE, ROUGAIL_NAMESPACE_DESCRIPTION from risotto.image import load_application_service +from risotto.rougail import func rougailconfig = RougailConfig @@ -16,36 +23,146 @@ rougailconfig['variable_namespace'] = ROUGAIL_NAMESPACE rougailconfig['variable_namespace_description'] = ROUGAIL_NAMESPACE_DESCRIPTION -DEFAULT_TYPE = 'string' ROUGAIL_VARIABLE_TYPE = 'https://forge.cloud.silique.fr/risotto/rougail/src/branch/main/doc/variable/README.md#le-type-de-la-variable' -def add_title_family(elts, dico): +def add_title_family(elts, dico, modes): + is_dynamic = False for idx, elt in enumerate(elts): description = elt.doc if not idx: description = description.capitalize() space = idx + 3 - title = '#' * space + f' {description} (*{elt.path}*)' + title = '#' * space + f' {description}' if title not in dico: - dico[title] = {'variables': [], 'help': '', 'type': ''} + if not is_dynamic and hasattr(elt, 'suffixes') and elt.suffixes: + is_dynamic = True + if is_dynamic: + title += '*suffix value*' + dico[title] = {'help': '', 'type': 'optiondescription', 'level': idx + 1} 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 + help_ = elt.information.help + if not help_.endswith('.'): + help_ += '.' + dico[title]['help'] += help_ + if hasattr(elt, 'suffixes') and elt.suffixes: + dico[title]['suffixes'] = elt.suffixes.path + if dico[title]['help']: + dico[title]['help'] += ' ' + dico[title]['help'] += f'This a dynamic family generated from the variable "{elt.suffixes.path}".' + if hasattr(elt, 'leadership') and elt.leadership: + if dico[title]['help']: + dico[title]['help'] += ' ' + dico[title]['help'] += f'This family is a leadership.' + add_mode(elt, dico[title], modes) +def add_parameter_variable(idx, child, is_leadership, type_, name): + if type_ == 'variable': + var_path = child.xmlfiles[-1].split('/', 2)[-1] + parameter = f'**[{name}]({var_path})**' + elif type_ == 'provider': + parameter = f'**{name}**' + subparameter = '' + if hasattr(child, 'properties') and 'mandatory' in child.properties: + subparameter = 'mandatory' + if ((idx == 0 or not is_leadership) and child.multi is True) or (idx != 0 and is_leadership and child.multi == 'submulti'): + if subparameter: + subparameter += ', ' + subparameter += 'multiple' + if subparameter: + parameter += f'
{subparameter}' + var_type = f'**Type:** [`{child.type}`]({ROUGAIL_VARIABLE_TYPE})' + return f'{parameter}
{var_type}' -def parse(applicationservice, elts, dico, providers_suppliers, hidden, objectspace): + +def add_title_variable(child, is_leadership, is_dynamic, objectspace): + description = f'{child.doc}' + if is_dynamic: + description += '*suffix value*' + if not description.endswith('.'): + description += '.' + if hasattr(child, 'information') and hasattr(child.information, 'help'): + if description: + description += '
' + help_ = child.information.help + if not help_.endswith('.'): + help_ += '.' + description += help_ + if hasattr(child, 'default'): + default = child.default + if isinstance(default, objectspace.value): + default = '*calculated*' + if isinstance(default, list): + default = '' + elif is_leadership and hasattr(child, 'default_multi'): + default = child.default_multi + else: + default = None + if hasattr(child, 'choice'): + if description: + description += '
' + description += '**Choices:**' + for choice in child.choice: + description += f'
- `{choice.name}`' + if choice.name == default: + description += ' ← default' + elif default is not None: + if description: + description += '
' + description += f'**Default:** {default}' + if hasattr(child, 'test'): + if description: + description += '
' + description += f'**Example:** {child.test}' + return description + + +def add_mode(child, values, modes, parent=None, is_leadership=False): + for mode in modes: + if mode in child.properties: + break + else: + raise Exception(f'cannot find mode for {child.path}') + if is_leadership: + for leadership_mode in modes: + if leadership_mode in parent.properties: + break + else: + raise Exception(f'cannot find mode for {parent.path}') + if modes[mode] < modes[leadership_mode]: + mode = leadership_mode + values['mode'] = mode + + +def parse(applicationservice, + elts, + dico, + providers_suppliers, + hidden, + dynamic, + objectspace, + mandatory_values_without_values, + modes, + is_dynamic, + dynamic_path, + is_service, + suppliers_variables, + ): 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 + if not dynamic: + dynamic = hasattr(elt, 'suffixes') + is_leadership = hasattr(elt, 'leadership') and elt.leadership is True + if not is_dynamic: + is_dynamic = hasattr(elt, 'suffixes') and elt.suffixes is not None + dynamic_path += elt.name + if is_dynamic: + dynamic_path += "*suffix value*" + dynamic_path += '.' + leader_is_mandatory = False + first_variable = True for children in vars(elt).values(): if isinstance(children, dict): children = list(children.values()) @@ -56,60 +173,109 @@ def parse(applicationservice, elts, dico, providers_suppliers, hidden, objectspa not isinstance(child, RootRougailObject): continue if isinstance(child, objectspace.variable): + if hasattr(child, 'provider') and child.provider and not child.provider.startswith('global:') and not child.provider.startswith('Host:'): + provider_prefix = child.provider + if ':' in provider_prefix: + provider_prefix = provider_prefix.split(':', 1)[0] + if provider_prefix != 'Host': + if child.doc == child.name: + description = None + else: + parameter = add_parameter_variable(idx, child, is_leadership, 'provider', child.provider) + description = add_title_variable(child, is_leadership, is_dynamic, objectspace) + suppliers_variables.setdefault(provider_prefix, {})[child.provider] = {'parameter': parameter, 'description': description} + if provider_prefix == child.provider: + doc = child.doc + if not doc.endswith('.'): + doc += '.' + suppliers_variables[provider_prefix]['doc'] = doc if not hidden and (not hasattr(child, 'properties') or ('hidden' not in child.properties and not 'disabled' in child.properties)): + if is_service: + if hasattr(child, 'provider'): + provider = child.provider + if ':' not in provider: + providers_suppliers['providers'].setdefault(provider, []).append(applicationservice) + if hasattr(child, 'supplier') and child.supplier: + supplier = child.supplier + if ':' not in supplier: + providers_suppliers['suppliers'].setdefault(supplier, []).append(applicationservice) + if 'mandatory' in child.properties: + providers_suppliers['mandatory_suppliers'].setdefault(supplier, []).append(applicationservice) + if hasattr(child, 'supplier') and child.supplier and ':' not in child.supplier: + raise Exception(f'Main supplier variable "{child.path}" in {child.xmlfiles} must be hidden') + if hasattr(child, 'provider') and child.provider and not child.provider.startswith('Host:'): + raise Exception(f'Provider variable "{child.path}" in {child.xmlfiles} must be hidden') if first_variable: - title = add_title_family(elts, dico) + add_title_family(elts, dico, modes) 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, objectspace.value): - default = '' - if isinstance(default, list): - default = '
'.join(default) - values['values'] = default - if hasattr(child, 'choice'): - values['choices'] = '
'.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: + sub_dynamic_path = dynamic_path + child.name + if is_dynamic: + sub_dynamic_path += "*suffix value*" + if not idx and is_leadership and hasattr(child, 'properties') and 'mandatory' in child.properties: + leader_is_mandatory = True + if hasattr(child, 'properties') and 'mandatory' in child.properties and \ + mandatory_values_without_values is not None and \ + not hidden and \ + not dynamic and \ + not hasattr(child, 'default') and \ + not hasattr(child, 'default_multi') and \ + (not is_leadership or leader_is_mandatory): + if not hasattr(child, 'test'): + raise Exception(f'{child.path} need test tag to document') + value = child.test + if ((not is_leadership or not idx) and child.multi is True) or (is_leadership and idx and child.multi == 'submulti'): + value = [value] + if is_leadership and idx: + value = {'0': value} + mandatory_values_without_values[child.path] = value + parameter = add_parameter_variable(idx, child, is_leadership, 'variable', sub_dynamic_path) + # + if child.name == child.doc: + raise Exception(f'missing doc for variable {child.path} in {child.xmlfiles}') + description = add_title_variable(child, is_leadership, is_dynamic, objectspace) + values = {'parameter': parameter, + 'description': description, + 'type': 'option'} + if hasattr(child, 'supplier') and child.supplier: + values['supplier'] = child.supplier + add_mode(child, values, modes, elt, is_leadership) + dico[child.path] = values + elif is_service: if hasattr(child, 'provider'): provider = child.provider if ':' not in provider: providers_suppliers['providers'].setdefault(provider, []).append(applicationservice) - if hasattr(child, 'supplier'): + if hasattr(child, 'supplier') and child.supplier: supplier = child.supplier if ':' not in supplier: providers_suppliers['suppliers'].setdefault(supplier, []).append(applicationservice) + if 'mandatory' in child.properties: + providers_suppliers['mandatory_suppliers'].setdefault(supplier, []).append(applicationservice) else: - parse(applicationservice, elts + [child], dico, providers_suppliers, hidden, objectspace) + parse(applicationservice, + elts + [child], + dico, + providers_suppliers, + hidden, + dynamic, + objectspace, + mandatory_values_without_values, + modes, + is_dynamic, + dynamic_path, + is_service, + suppliers_variables, + ) + return first_variable -def build_dependencies_tree(applicationservice, applicationservice_data, applicationservices_data, applicationservices_data_ext, space): +def build_dependencies_tree(applicationservice, + applicationservice_data, + applicationservices_data, + applicationservices_data_ext, + space, + only_name: bool=False, + ): depends = [] if applicationservice_data['depends']: if applicationservice in applicationservices_data: @@ -132,17 +298,33 @@ def build_dependencies_tree(applicationservice, applicationservice_data, applica else: raise Exception(f'cannot find applicationservice "{applicationservice}"') ext = True - subdepends = build_dependencies_tree(depend, applicationservice_data, applicationservices_data, applicationservices_data_ext, space + 2) - if not idx or subdepends: - title = '\n' + subdepends = build_dependencies_tree(depend, + applicationservice_data, + applicationservices_data, + applicationservices_data_ext, + space + 2, + only_name, + ) + if only_name: + if depend not in depends: + depends.append(depend) + for dep in subdepends: + if dep not in depends: + depends.append(dep) else: - title = '' - depend_desc = depend - if ext: - depend_desc += ' (in external dataset)' - title = ' ' * space + f'- [{depend_desc}]({url}/{depend}/README.md)' - depends.append(title) - depends.extend(subdepends) + if not idx or subdepends: + title = '\n' + else: + title = '' + depend_desc = depend + if ext: + depend_desc += ' (in external dataset)' + doc = applicationservices_data[depend]["description"] + if not doc.endswith('.'): + doc += '.' + title = ' ' * space + f'- [{depend_desc}]({url}/{depend}/README.md): {doc}' + depends.append(title) + depends.extend(subdepends) return depends @@ -159,9 +341,11 @@ def load_data(url, directory, applicationservices_data, global_data={}): continue applicationservices_data[applicationservice] = {'description': applicationservice_data['description'], 'website': applicationservice_data.get('website'), + 'help': applicationservice_data.get('help'), 'as_dir': as_dir, 'depends': [], 'used_by': [], + 'service': applicationservice_data.get('service', False), } if applicationservice in tmps: for app in tmps.pop(applicationservice): @@ -182,86 +366,255 @@ def load_data(url, directory, applicationservices_data, global_data={}): 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'] +def display_example(as_fh, + applicationservice, + provider_zone, + zones_name, + values, + ): + as_fh.write('\n## Example\n') + as_fh.write('\nZone names are provided as examples. Think about adapting with the value of provider_zone in configuration file.\n') + as_fh.write('\n```\n') + example = {applicationservice: {'applicationservice': applicationservice}} + if provider_zone: + example[applicationservice]['provider_zone'] = provider_zone + if zones_name: + example[applicationservice]['zones_name'] = zones_name + if values: + example[applicationservice]['values'] = values +# as_fh.write(dump(example, sort_keys=False)) + yaml = YAML() + yaml.indent(sequence=4, offset=2) + yaml.dump(example, as_fh) + as_fh.write('```\n') + + +def load_dict(applicationservices, + applicationservices_data, + suppliers_variables, + dico, + providers_suppliers, + mandatory_values_without_values, + just_doc=True, + ): + rougailconfig['dictionaries_dir'] = [] + extra_dictionaries = {} + functions_file = [func.__file__] + for applicationservice in applicationservices: + as_dir = applicationservices_data[applicationservice]['as_dir'] dirname = join(as_dir, 'dictionaries') if isdir(dirname): - rougailconfig['dictionaries_dir'] = [dirname] - else: - rougailconfig['dictionaries_dir'] = [] + rougailconfig['dictionaries_dir'].append(dirname) 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) + dirname_func = join(as_dir, 'funcs') + if isdir(dirname_func): + for f in listdir(dirname_func): + if f.startswith('__'): + continue + functions_file.append(abspath(join(dirname_func, f))) + rougailconfig['extra_dictionaries'] = extra_dictionaries + rougailconfig['functions_file'] = functions_file + rougailconfig['extra_annotators'] = EXTRA_ANNOTATORS + converted = RougailConvert(rougailconfig, just_doc=just_doc) + converted.load_dictionaries() + converted.annotate() + objectspace = converted.rougailobjspace + modes = {name: Mode(idx) for idx, name in enumerate(objectspace.rougailconfig['modes_level'])} + if hasattr(objectspace.space, 'variables'): + dico[applicationservice] = {} + if mandatory_values_without_values is not None: + mandatory_values_without_values[applicationservice] = {} + mandatory_values = mandatory_values_without_values[applicationservice] + else: + mandatory_values = None + for name, elt in objectspace.space.variables.items(): + parse(applicationservice, + [elt], + dico[applicationservice], + providers_suppliers, + False, + False, + objectspace, + mandatory_values, + modes, + False, + '', + applicationservices_data[applicationservice]['service'], + suppliers_variables, + ) + + +def write_data(applicationservices_data, applicationservices_data_ext): + dico = {} + providers_suppliers = {'providers': {}, 'suppliers': {}, 'mandatory_suppliers': {}, 'depends': {}} + mandatory_values_without_values = {} + suppliers_variables = {} + for applicationservice, applicationservice_data in applicationservices_data.items(): + if applicationservice_data['service']: + depends = build_dependencies_tree(applicationservice, + applicationservice_data, + applicationservices_data, + applicationservices_data_ext, + 0, + only_name=True, + ) + depends.append(applicationservice) + providers_suppliers['depends'][applicationservice] = set(depends) + load_dict(['provider-systemd-machined'] + depends, + applicationservices_data, + suppliers_variables, + dico, + providers_suppliers, + mandatory_values_without_values, + just_doc=False, + ) + else: + load_dict([applicationservice], + applicationservices_data, + suppliers_variables, + dico, + providers_suppliers, + None, + ) + for provider in suppliers_variables: + for supplier, tdico in suppliers_variables[provider].items(): + if supplier == 'doc': + continue + if not tdico['description']: + raise Exception(f'variable with provider "{supplier}" must have a description') + # + for filename in glob('seed/README.*.md'): + unlink(filename) + for provider in suppliers_variables: + with open(join(f'seed/README.{provider}.md'), 'w') as as_fh: + as_fh.write(f'---\ngitea: none\ninclude_toc: true\n---\n\n') + as_fh.write(f'\n[Return to the list of application services.](README.md)\n') + as_fh.write(f'# {provider}\n\n') + as_fh.write(f'## Synopsis\n\n') + as_fh.write(suppliers_variables[provider][provider]['description']) + as_fh.write(f'\n\n## Variables\n\n') + providers = [] + for provider_name, tdico in suppliers_variables[provider].items(): + if ':' in provider_name: + providers.append(list(tdico.values())) + if providers: + as_fh.write(tabulate(providers, headers=['Parameter', 'Comment'], tablefmt="github")) + as_fh.write(f'\n') + applicationservices = providers_suppliers['providers'].get(provider, []) + if applicationservices: + if len(applicationservices) == 1: + as_fh.write(f'\n## Provider\n\n') + for applicationservice in applicationservices: + doc = applicationservices_data[applicationservice]["description"] + if not doc.endswith('.'): + doc += '.' + as_fh.write(f'[{applicationservice}]({applicationservice}/README.md): {doc}\n') + else: + as_fh.write(f'\n## Providers\n\n') + for applicationservice in applicationservices: + doc = applicationservices_data[applicationservice]["description"] + if not doc.endswith('.'): + doc += '.' + as_fh.write(f'- [{applicationservice}]({applicationservice}/README.md): {doc}\n') + applicationservices = providers_suppliers['suppliers'].get(provider, []) + if applicationservices: + if len(applicationservices) == 1: + as_fh.write(f'\n## Supplier\n\n') + for applicationservice in applicationservices: + doc = applicationservices_data[applicationservice]["description"] + if not doc.endswith('.'): + doc += '.' + as_fh.write(f'[{applicationservice}]({applicationservice}/README.md): {doc}\n') + else: + as_fh.write(f'\n## Suppliers\n\n') + for applicationservice in applicationservices: + doc = applicationservices_data[applicationservice]["description"] + if not doc.endswith('.'): + doc += '.' + as_fh.write(f'- [{applicationservice}]({applicationservice}/README.md): {doc}\n') + # for applicationservice, applicationservice_data in applicationservices_data.items(): as_dir = applicationservice_data['as_dir'] + local_dico = dico 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'\n[Return to the list of application services.](../README.md)\n') as_fh.write(f'# {applicationservice}\n\n') - as_fh.write(f'## Description\n\n') - description = applicationservice_data['description'] + '.\n' + as_fh.write(f'## Synopsis\n\n') + description = applicationservice_data['description'] + '.' if applicationservice_data['website']: - description += f'\n[For more informations]({applicationservice_data["website"]})\n' + description = f"\n[{description}]({applicationservice_data['website']})" + description += '\n' + if applicationservice_data['help']: + help_ = applicationservice_data['help'].strip() + if not help_.endswith('.'): + help_ += 'p.' + description += f'\n{help_}\n' as_fh.write(description) + # variables + if applicationservice in local_dico and local_dico[applicationservice]: + display_variables(as_dir, as_fh, applicationservice, local_dico) + if applicationservice_data['service']: + mandatory_zones_name = {} + zones_name = {} + provider_name = None + if applicationservice_data['depends']: + depends_set = providers_suppliers['depends'][applicationservice] + # providers + for provider, provider_as in providers_suppliers['providers'].items(): + if not set(provider_as) & depends_set: + continue + provider_name = provider.lower() + break + # + if providers_suppliers['suppliers'] and list(providers_suppliers['suppliers']) != ['Host']: + as_fh.write('\n## Requirements services\n') + provider_hosts = [] + for provider, provider_data in providers_suppliers['suppliers'].items(): + if provider == 'Host': + continue + if applicationservice not in provider_data: + continue + suppliers = providers_suppliers['providers'].get(provider, []) + if len(suppliers) == 1: + if suppliers[0] in provider_hosts: + continue + provider_hosts.append(suppliers[0]) + filename = f'README.{provider}.md' + if isfile(join('seed', filename)): + msg = f'[{provider}](../{filename})' + else: + msg = provider + if provider in providers_suppliers['mandatory_suppliers']: + mandatory_zones_name[provider] = msg + else: + zones_name[provider] = msg + if mandatory_zones_name: + as_fh.write('\n### Mandatories\n\n') + for provider, msg in mandatory_zones_name.items(): + as_fh.write(f'- {msg}: {suppliers_variables[provider]["doc"]}\n') + if zones_name: + as_fh.write('\n### Optionals\n\n') + for provider, msg in zones_name.items(): + as_fh.write(f'- {msg}: {suppliers_variables[provider]["doc"]}\n') + # + mandatory_zones_name = [provider.lower() for provider in mandatory_zones_name] + mandatory_zones_name.sort() + display_example(as_fh, + applicationservice, + provider_name, + mandatory_zones_name, + mandatory_values_without_values[applicationservice], + ) if applicationservice_data['depends']: - as_fh.write(f'\n## Dependances\n\n') + as_fh.write('\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') - 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') if len(applicationservice_data['used_by']) == 1: @@ -274,79 +627,120 @@ def write_data(applicationservices_data, applicationservices_data_ext): for provider, provider_as in providers_suppliers['providers'].items(): if not applicationservice in provider_as: continue - for supplier in providers_suppliers['suppliers'][provider]: - if supplier in linked: - continue - linked.append(supplier) + if provider in providers_suppliers['suppliers']: + 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') + as_fh.write('\n## Useful for service\n\n') + description = applicationservices_data[linked[0]]["description"] + if not description.endswith('.'): + description += '.' + as_fh.write(f'[{linked[0]}](../{linked[0]}/README.md): {description}\n') else: - as_fh.write('\n## Suppliers\n\n') + as_fh.write('\n## Useful for services\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: - continue - 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') + description = applicationservices_data[supplier]["description"] + if not description.endswith('.'): + description += '.' + as_fh.write(f'- [{supplier}](../{supplier}/README.md): {description}\n') 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') + for service in [True, False]: + if service: + as_fh.write('# Application services\n\n') else: - as_fh.write(f'- {category}:\n') - applicationservices_.sort() - for applicationservice in applicationservices_: + as_fh.write('# Application dependencies\n\n') + applicationservices = {} + for applicationservice, applicationservice_data in applicationservices_data.items(): + if applicationservice_data['service'] is not service: + continue + 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') + 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') providers = list(providers_suppliers['providers'].keys()) providers.sort() if providers: - as_fh.write('\n# Providers and suppliers\n\n') + as_fh.write('\n# Providers\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') + if provider in suppliers_variables[provider]: + as_fh.write(f'- [{provider}](README.{provider}.md): {suppliers_variables[provider]["doc"]}\n') + + +def display_variables(as_dir, as_fh, applicationservice, local_dico): + for main_title, modes in [('Basic variables', ['basic']), + ('Variables', ['basic', 'normal']), + ('Variables for expert', ['basic', 'normal', 'expert']), + ]: + main_title_added = False + titles = [] + variables = [] + parent_path = None + for title, data in list(local_dico[applicationservice].items()): + keys = [] + if data['type'] == 'optiondescription': + #if data['mode'] not in modes: + # continue + if variables: + as_fh.write(tabulate(variables, headers=['Parameter', 'Comment'], tablefmt="github")) + as_fh.write('\n') + as_fh.write('\n') + variables = [] + parent_path = None + title_level = data['level'] + len_titles = len(titles) + if len_titles < title_level: + titles.append((None, None)) + titles[title_level - 1] = (title, data) + if len_titles > title_level: + for idx in range(title_level, len_titles): + titles.pop(title_level) + else: + if data['mode'] not in modes: + continue + if not main_title_added: + as_fh.write(f'\n## {main_title}\n\n') + main_title_added = True + for idx, td in enumerate(titles): + t, d = td + if not t: + continue + as_fh.write(f'{t}\n\n') + if d['help']: + help = d['help'] + if not help.endswith('.'): + help += '.' + as_fh.write(f'{help}\n\n') + titles[idx] = (None, None) + if data['mode'] != modes[-1]: + continue + new_parent_path = title.rsplit('.', 1)[0] + if variables and parent_path != new_parent_path: + as_fh.write(tabulate(variables, headers=['Parameter', 'Comments'], tablefmt="github")) + as_fh.write('\n') + as_fh.write('\n') + variables = [] + parent_path = new_parent_path + variables.append([data['parameter'], data['description']]) + del local_dico[applicationservice][title] + if variables: + as_fh.write(tabulate(variables, headers=['Parameter', 'Comments'], tablefmt="github")) + as_fh.write('\n') def main(): diff --git a/src/risotto/utils.py b/src/risotto/utils.py index 1372e8a..c72bbc2 100644 --- a/src/risotto/utils.py +++ b/src/risotto/utils.py @@ -11,7 +11,7 @@ from pprint import pprint MULTI_FUNCTIONS = [] EXTRA_ANNOTATORS = ['risotto.rougail'] ROUGAIL_NAMESPACE = 'general' -ROUGAIL_NAMESPACE_DESCRIPTION = 'Général' +ROUGAIL_NAMESPACE_DESCRIPTION = 'General' HERE = environ['PWD'] IP_DIR = join(HERE, 'ip')