diff --git a/open_api_framework/management/commands/generate_envvar_docs.py b/open_api_framework/management/commands/generate_envvar_docs.py index fafaa1a..eb9ebef 100644 --- a/open_api_framework/management/commands/generate_envvar_docs.py +++ b/open_api_framework/management/commands/generate_envvar_docs.py @@ -6,6 +6,7 @@ from django.template import loader from django.utils.module_loading import import_string +from django_setup_configuration.config_settings import ConfigSettings from django_setup_configuration.management.commands.generate_config_docs import ( ConfigDocBase, ) @@ -27,7 +28,140 @@ def convert_variables_to_rst(variables: list[EnvironmentVariable]) -> str: return template.render({"vars": vars}) -class Command(ConfigDocBase, BaseCommand): +class SetupConfigDocs(ConfigDocBase): + + def generate_config_file(self) -> str: + + full_rendered_content = "" + + if not hasattr(settings, "SETUP_CONFIGURATION_STEPS"): + return full_rendered_content + + for config_string in settings.SETUP_CONFIGURATION_STEPS: + config_step = import_string(config_string) + + config_settings = getattr(config_step, "config_settings", None) + if not config_settings or not config_settings.independent: + continue + + rendered_content = self.render_doc(config_settings, config_step) + full_rendered_content += rendered_content + + template = loader.get_template("open_api_framework/setup_config.rst") + rendered = template.render( + {"rendered_configuration_steps": full_rendered_content} + ) + + return rendered + + def render_doc(self, config_settings, config_step) -> str: + """ + Render a `ConfigSettings` documentation template with the following variables: + 1. enable_setting + 2. required_settings + 3. optional_settings + 4. detailed_info + 5. title + """ + # 1. + enable_setting = getattr(config_settings, "enable_setting", None) + + # 2. + required_settings = [ + name for name in getattr(config_settings, "required_settings", []) + ] + + # additional settings from related configuration steps to embed + # the documentation of several steps into one + related_config_settings = [ + config for config in getattr(config_settings, "related_config_settings", []) + ] + required_settings_related = self.extract_unique_settings( + [config.required_settings for config in related_config_settings] + ) + # optional_settings_related = self.extract_unique_settings( + # [config.optional_settings for config in related_config_settings] + # ) + + required_settings.extend(required_settings_related) + required_settings.sort() + + optional_settings = config_settings.optional_settings + optional_settings.sort() + + # 4. + detailed_info = self.get_detailed_info( + config_settings, + related_config_settings, + ) + + # 5. + title = self.format_display_name(config_step.verbose_name) + + template_variables = { + "enable_setting": enable_setting, + "required_settings": required_settings, + "optional_settings": optional_settings, + "detailed_info": detailed_info, + "title": title, + } + + template = loader.get_template( + "open_api_framework/components/setup_config_step.rst" + ) + rendered = template.render(template_variables) + + return rendered + + def format_display_name(self, display_name: str) -> str: + """Underlines title with '=' to display as heading in rst file""" + + heading_bar = "-" * len(display_name) + display_name_formatted = f"{display_name}\n{heading_bar}" + return display_name_formatted + + def get_detailed_info( + self, + config_settings: ConfigSettings, + related_config_settings: list[ConfigSettings], + ) -> dict[dict[str]]: + """ + Get information about the configuration settings: + 1. from model fields associated with the `ConfigSettings` + 2. from information provided manually in the `ConfigSettings` + 3. from information provided manually in the `ConfigSettings` of related + configuration steps + """ + result = dict() + for field in config_settings.config_fields: + part = dict() + variable = config_settings.get_config_variable(field.name) + part["setting"] = field.verbose_name + part["description"] = field.description or 'No description' + part["possible_values"] = field.field_description + part["default_value"] = field.default_value + + result[variable] = part + + self.add_additional_info(config_settings, result) + for config_settings in related_config_settings: + self.add_additional_info(config_settings, result) + + return result + + @staticmethod + def add_additional_info( + config_settings: ConfigSettings, result: dict[dict[str]] + ) -> None: + """Convenience/helper function to retrieve additional documentation info""" + + additional_info = config_settings.additional_info + + for key, value in additional_info.items(): + result[key] = value + + +class Command(BaseCommand): help = "Generate documentation for all used envvars" def add_arguments(self, parser): @@ -55,7 +189,8 @@ def handle(self, *args, **options): self.generate_regular_config_docs(*args, **options) self.generate_setup_config_docs(*args, **options) - def generate_regular_config_docs(self, *args, **options): + @staticmethod + def generate_regular_config_docs(*args, **options): from open_api_framework.conf.utils import ENVVAR_REGISTRY file_path = options["envvar_file"] @@ -77,22 +212,13 @@ def _sort(envvar): with open(file_path, "w") as f: f.write(convert_variables_to_rst(sorted_registry)) - def generate_setup_config_docs(self, *args, **options) -> None: - full_rendered_content = "" + @staticmethod + def generate_setup_config_docs(*args, **options) -> None: file_path = options["config_file"] - if not hasattr(settings, "SETUP_CONFIGURATION_STEPS"): - return + doc_generator = SetupConfigDocs() - for config_string in settings.SETUP_CONFIGURATION_STEPS: - config_step = import_string(config_string) - - config_settings = getattr(config_step, "config_settings", None) - if not config_settings or not config_settings.independent: - continue - - rendered_content = self.render_doc(config_settings, config_step) - full_rendered_content += rendered_content + full_rendered_content = doc_generator.generate_config_file() if len(full_rendered_content) > 0: with open(file_path, "w") as f: diff --git a/open_api_framework/templates/open_api_framework/components/setup_config_step.rst b/open_api_framework/templates/open_api_framework/components/setup_config_step.rst new file mode 100644 index 0000000..847e439 --- /dev/null +++ b/open_api_framework/templates/open_api_framework/components/setup_config_step.rst @@ -0,0 +1,26 @@ +{% load doc_tags %} +{% spaceless %} +{{ title }} + +Enable/Disable configuration: +""""""""""""""""""""""""""""" + +:: + + {{ enable_setting }} + +{% if required_settings %} +Required settings +""""""""""""""""" +{% for setting in required_settings %} +* ``{{ setting }}``: {% config_var_description setting detailed_info %} +{% endfor %}{% endif %} + +{% if optional_settings %} +Optional Settings +""""""""""""""""" +{% for setting in optional_settings %} +* ``{{ setting }}``: {% config_var_description setting detailed_info %} +{% endfor %}{% endif %} + +{% endspaceless %} diff --git a/open_api_framework/templates/open_api_framework/env_config.rst b/open_api_framework/templates/open_api_framework/env_config.rst index 6847434..5ad8c76 100644 --- a/open_api_framework/templates/open_api_framework/env_config.rst +++ b/open_api_framework/templates/open_api_framework/env_config.rst @@ -11,13 +11,16 @@ Available environment variables {% for group_name, group_vars in vars %} {{group_name}} -{{group_name|repeat_char:"-"}}{% for subgroup_name, subgroup_vars in group_vars %} -{% if subgroup_name is not null %} +{{group_name|repeat_char:"-"}} + +{% spaceless %}{% for subgroup_name, subgroup_vars in group_vars %}{% if subgroup_name is not null %} {{subgroup_name}} -{{subgroup_name|repeat_char:"^"}} -{% endif %} -{% for var in subgroup_vars %}* ``{{var.name}}``: {% if var.help_text %}{{var.help_text|safe|ensure_endswith:"."}}{% endif %}{% if var.auto_display_default and not var.default|is_undefined %} Defaults to: ``{{var.default|to_str|safe}}``.{% endif %} -{% endfor %}{% endfor %} +{{subgroup_name|repeat_char:"^"}}{% endif %} + +{% spaceless %} +{% for var in subgroup_vars %} +* ``{{var.name}}``: {% if var.help_text %}{{var.help_text|safe|ensure_endswith:"."}}{% endif %}{% if var.auto_display_default and not var.default|is_undefined %} Defaults to: ``{{var.default|to_str|safe}}``.{% endif %}{% endfor %}{% endspaceless %}{% endfor %}{% endspaceless %} + {% endfor %} {% block extra %}{% endblock %} diff --git a/open_api_framework/templates/open_api_framework/setup_config.rst b/open_api_framework/templates/open_api_framework/setup_config.rst new file mode 100644 index 0000000..8c9c1cb --- /dev/null +++ b/open_api_framework/templates/open_api_framework/setup_config.rst @@ -0,0 +1,16 @@ +{% load doc_tags %}.. _installation_setup_config: + +============================= +Setup configuration reference +============================= + +{% block intro %} +Setup configuration description +{% endblock %} + +Configuration Environment Variables +=================================== + +All configuration steps currently available: + +{{ rendered_configuration_steps | safe }} diff --git a/open_api_framework/templatetags/doc_tags.py b/open_api_framework/templatetags/doc_tags.py index e1791f5..d737f8b 100644 --- a/open_api_framework/templatetags/doc_tags.py +++ b/open_api_framework/templatetags/doc_tags.py @@ -33,3 +33,8 @@ def ensure_endswith(value, char): if not value.endswith(char): value += char return value + +@register.filter() +def config_var_description(variable, details): + + return details[variable].get("description") diff --git a/tox.ini b/tox.ini index b1ea562..51b85bd 100644 --- a/tox.ini +++ b/tox.ini @@ -27,7 +27,7 @@ extras = deps = django42: Django~=4.2.0 commands = - py.test tests \ + py.test tests -vv \ --cov --cov-report xml:reports/coverage-{envname}.xml \ {posargs}