From 65dc84b240c5e854c3c5123607d6f44bfbf60972 Mon Sep 17 00:00:00 2001 From: Marshall Hanmer Date: Tue, 14 Jan 2025 14:04:13 -0500 Subject: [PATCH 01/17] Add generate_operators_doc.py & jinja2 template --- .ci/generate_operators_doc.py | 115 ++++++++++++++++++ .gitignore | 4 + .../operators_doc/operator_doc_template.md | 31 +++++ 3 files changed, 150 insertions(+) create mode 100644 .ci/generate_operators_doc.py create mode 100644 doc/source/operators_doc/operator_doc_template.md diff --git a/.ci/generate_operators_doc.py b/.ci/generate_operators_doc.py new file mode 100644 index 0000000000..233bfeafcc --- /dev/null +++ b/.ci/generate_operators_doc.py @@ -0,0 +1,115 @@ +from ansys.dpf import core as dpf +from ansys.dpf.core.dpf_operator import available_operator_names +from ansys.dpf.core.core import load_library +import argparse +import json +import os +from jinja2 import Template + +def initialize_server(ansys_path=None): + server = dpf.start_local_server(ansys_path=ansys_path) + print(f"Ansys Path: {server.ansys_path}") + print(f"Server Info: {server.info}") + print(f"Server Context: {server.context}") + print(f"Server Config: {server.config}") + print(f"Server version: {dpf.global_server().version}") + print("Loading Composites Plugin") + load_library(os.path.join(server.ansys_path, "dpf", "plugins", "dpf_composites", "composite_operators.dll")) + print("Loading Acoustics Plugin") + load_library(os.path.join(server.ansys_path, "Acoustics", "SAS", "ads", "dpf_sound.dll")) + return server + +def fetch_doc_info(server, operator_name): + spec = dpf.Operator.operator_specification(op_name=operator_name, server=server) + input_info = [] + output_info = [] + configurations_info = [] + for input_pin in spec.inputs: + input = spec.inputs[input_pin] + input_info.append({ + "pin_number": input_pin, + "name": input.name, + "types": [str(t) for t in input._type_names], + "document": input.document, + "optional": input.optional, + }) + for output_pin in spec.outputs: + output = spec.outputs[output_pin] + output_info.append({ + "pin_number": output_pin, + "name": output.name, + "types": [str(t) for t in output._type_names], + "document": output.document, + "optional": output.optional, + }) + for configuration_key in spec.config_specification: + configuration = spec.config_specification[configuration_key] + configurations_info.append({ + "name": configuration.name, + "types": [str(t) for t in configuration.type_names], + "document": configuration.document, + "default_value": configuration.default_value_str, + }) + properties = spec.properties + if "plugin" in properties: + plugin = properties["plugin"] + else: + plugin = "N/A" + scripting_info = { + "category": properties['category'], + "plugin": plugin, + "scripting_name": properties['scripting_name'], + "full_name": properties['category'] + "." + properties['scripting_name'], + "internal_name": properties['scripting_name'], + } + return { + "operator_name": scripting_info['category'] + ": " + properties['user_name'], + "operator_description": spec.description, + "inputs": input_info, + "outputs": output_info, + "configurations": configurations_info, + "scripting_info": scripting_info, + } + +def get_plugin_operators(server, plugin_name): + operators = available_operator_names(server) + plugin_operators = [] + for operator_name in operators: + spec = dpf.Operator.operator_specification(op_name=operator_name, server=server) + if "plugin" in spec.properties and spec.properties["plugin"] == plugin_name: + plugin_operators.append(operator_name) + return plugin_operators + +def generate_operator_doc(server, operator_name): + operator_info = fetch_doc_info(server, operator_name) + script_dir = os.path.dirname(__file__) + root_dir = os.path.dirname(script_dir) + template_dir = os.path.join(root_dir, 'doc', 'source', 'operators_doc') + with open(os.path.join(template_dir, 'operator_doc_template.md'), 'r') as file: + template = Template(file.read()) + + output = template.render(operator_info) + if "::" in operator_name: + operator_name = operator_name.replace("::", "_") + with open(os.path.join(template_dir, f"{operator_name}.md"), 'w') as file: + file.write(output) + +def main(): + parser = argparse.ArgumentParser(description="Fetch available operators") + parser.add_argument("--plugin", help="Filter operators by plugin") + parser.add_argument("--ansys_path", default=None, help="Path to Ansys DPF Server installation directory") + args = parser.parse_args() + desired_plugin = args.plugin + + server = initialize_server(args.ansys_path) + if desired_plugin is None: + operators = available_operator_names(server) + for operator_name in operators: + generate_operator_doc(server, operator_name) + else: + plugin_operators = get_plugin_operators(server, desired_plugin) + for operator_name in plugin_operators: + generate_operator_doc(server, operator_name) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/.gitignore b/.gitignore index cab916ab12..0cbfae7dad 100644 --- a/.gitignore +++ b/.gitignore @@ -69,6 +69,10 @@ instance/ # Sphinx documentation doc/_build/ +# Operators documentation +doc/source/operators_doc/*.md +!doc/source/operators_doc/operator_doc_template.md + # PyBuilder .pybuilder/ target/ diff --git a/doc/source/operators_doc/operator_doc_template.md b/doc/source/operators_doc/operator_doc_template.md new file mode 100644 index 0000000000..3a8a4d8ccf --- /dev/null +++ b/doc/source/operators_doc/operator_doc_template.md @@ -0,0 +1,31 @@ +# {{ operator_name }} + +## Description + +{{ operator_description }} + +## Inputs + +{% for input in inputs %} +- **Pin {{ input.pin_number }}** {{ input.name }} (type: {{ input.types }}) (optional: {{ input.optional }}): {{ input.document }} +{% endfor %} + +## Outputs + +{% for output in outputs %} +- **Pin {{ output.pin_number }}** {{ output.name }} (type: {{ output.types }}): {{ output.document }} +{% endfor %} + +## Configurations + +{% for configuration in configurations %} +- **{{ configuration.name }}** (type: {{ configuration.types }}) (default: {{ configuration.default_value }}): {{ configuration.document }} +{% endfor %} + +## Scripting + +- **category**: {{ scripting_info.category }} +- **plugin**: {{ scripting_info.plugin }} +- **scripting name**: {{ scripting_info.scripting_name }} +- **full name**: {{ scripting_info.full_name }} +- **internal name**: {{ scripting_info.internal_name }} \ No newline at end of file From c70a436f79d444a077adb5b3284ec709ed5226ab Mon Sep 17 00:00:00 2001 From: Marshall Hanmer Date: Wed, 15 Jan 2025 09:18:51 -0500 Subject: [PATCH 02/17] Handle edge cases with missing properties data --- .ci/generate_operators_doc.py | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/.ci/generate_operators_doc.py b/.ci/generate_operators_doc.py index 233bfeafcc..f584c42082 100644 --- a/.ci/generate_operators_doc.py +++ b/.ci/generate_operators_doc.py @@ -2,7 +2,6 @@ from ansys.dpf.core.dpf_operator import available_operator_names from ansys.dpf.core.core import load_library import argparse -import json import os from jinja2 import Template @@ -55,15 +54,31 @@ def fetch_doc_info(server, operator_name): plugin = properties["plugin"] else: plugin = "N/A" + try: + scripting_name = properties['scripting_name'] + full_name = properties['category'] + "." + properties['scripting_name'] + except KeyError: + scripting_name = None + full_name = None + try: + user_name = properties['user_name'] + except KeyError: + user_name = operator_name + try: + category = properties['category'] + op_friendly_name = category + ": " + user_name + except KeyError: + category = "" + op_friendly_name = user_name scripting_info = { - "category": properties['category'], + "category": category, "plugin": plugin, - "scripting_name": properties['scripting_name'], - "full_name": properties['category'] + "." + properties['scripting_name'], - "internal_name": properties['scripting_name'], + "scripting_name": scripting_name, + "full_name": full_name, + "internal_name": scripting_name, } return { - "operator_name": scripting_info['category'] + ": " + properties['user_name'], + "operator_name": op_friendly_name, "operator_description": spec.description, "inputs": input_info, "outputs": output_info, From 22ac4f1dadab78e2f68315f089e5adbc534032d4 Mon Sep 17 00:00:00 2001 From: Marshall Hanmer Date: Wed, 15 Jan 2025 15:10:30 -0500 Subject: [PATCH 03/17] Add support for ignoring private operators, add license if available --- .ci/generate_operators_doc.py | 17 +++++++++++++---- .../operators_doc/operator_doc_template.md | 3 ++- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/.ci/generate_operators_doc.py b/.ci/generate_operators_doc.py index f584c42082..9ddff3bc6b 100644 --- a/.ci/generate_operators_doc.py +++ b/.ci/generate_operators_doc.py @@ -70,12 +70,17 @@ def fetch_doc_info(server, operator_name): except KeyError: category = "" op_friendly_name = user_name + try: + license = properties['license'] + except KeyError: + license = "None" scripting_info = { "category": category, "plugin": plugin, "scripting_name": scripting_name, "full_name": full_name, - "internal_name": scripting_name, + "internal_name": operator_name, + "license": license, } return { "operator_name": op_friendly_name, @@ -84,6 +89,7 @@ def fetch_doc_info(server, operator_name): "outputs": output_info, "configurations": configurations_info, "scripting_info": scripting_info, + "exposure": properties['exposure'], } def get_plugin_operators(server, plugin_name): @@ -95,8 +101,10 @@ def get_plugin_operators(server, plugin_name): plugin_operators.append(operator_name) return plugin_operators -def generate_operator_doc(server, operator_name): +def generate_operator_doc(server, operator_name, include_private): operator_info = fetch_doc_info(server, operator_name) + if not include_private and operator_info["exposure"] == "private": + return script_dir = os.path.dirname(__file__) root_dir = os.path.dirname(script_dir) template_dir = os.path.join(root_dir, 'doc', 'source', 'operators_doc') @@ -113,6 +121,7 @@ def main(): parser = argparse.ArgumentParser(description="Fetch available operators") parser.add_argument("--plugin", help="Filter operators by plugin") parser.add_argument("--ansys_path", default=None, help="Path to Ansys DPF Server installation directory") + parser.add_argument("--include_private", action="store_true", help="Include private operators") args = parser.parse_args() desired_plugin = args.plugin @@ -120,11 +129,11 @@ def main(): if desired_plugin is None: operators = available_operator_names(server) for operator_name in operators: - generate_operator_doc(server, operator_name) + generate_operator_doc(server, operator_name, args.include_private) else: plugin_operators = get_plugin_operators(server, desired_plugin) for operator_name in plugin_operators: - generate_operator_doc(server, operator_name) + generate_operator_doc(server, operator_name, args.include_private) if __name__ == "__main__": main() \ No newline at end of file diff --git a/doc/source/operators_doc/operator_doc_template.md b/doc/source/operators_doc/operator_doc_template.md index 3a8a4d8ccf..e62361675f 100644 --- a/doc/source/operators_doc/operator_doc_template.md +++ b/doc/source/operators_doc/operator_doc_template.md @@ -28,4 +28,5 @@ - **plugin**: {{ scripting_info.plugin }} - **scripting name**: {{ scripting_info.scripting_name }} - **full name**: {{ scripting_info.full_name }} -- **internal name**: {{ scripting_info.internal_name }} \ No newline at end of file +- **internal name**: {{ scripting_info.internal_name }} +- **license**: {{ scripting_info.license }} \ No newline at end of file From c7b661b86b7beb3f1c157c1c3633a5f316b87bb3 Mon Sep 17 00:00:00 2001 From: Marshall Hanmer Date: Fri, 17 Jan 2025 14:35:16 -0500 Subject: [PATCH 04/17] cleanup --- .ci/generate_operators_doc.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.ci/generate_operators_doc.py b/.ci/generate_operators_doc.py index 9ddff3bc6b..3bf0eea9a8 100644 --- a/.ci/generate_operators_doc.py +++ b/.ci/generate_operators_doc.py @@ -128,12 +128,10 @@ def main(): server = initialize_server(args.ansys_path) if desired_plugin is None: operators = available_operator_names(server) - for operator_name in operators: - generate_operator_doc(server, operator_name, args.include_private) else: - plugin_operators = get_plugin_operators(server, desired_plugin) - for operator_name in plugin_operators: - generate_operator_doc(server, operator_name, args.include_private) + operators = get_plugin_operators(server, desired_plugin) + for operator_name in operators: + generate_operator_doc(server, operator_name, args.include_private) if __name__ == "__main__": main() \ No newline at end of file From 568a46b6a43a14cf01836a2b5594b83f8cb2ca61 Mon Sep 17 00:00:00 2001 From: Marshall Hanmer Date: Fri, 17 Jan 2025 16:36:19 -0500 Subject: [PATCH 05/17] Fix items flagged by ruff hook --- .ci/generate_operators_doc.py | 89 ++++++++++++++++++++--------------- 1 file changed, 52 insertions(+), 37 deletions(-) diff --git a/.ci/generate_operators_doc.py b/.ci/generate_operators_doc.py index 3bf0eea9a8..c696521cb0 100644 --- a/.ci/generate_operators_doc.py +++ b/.ci/generate_operators_doc.py @@ -2,8 +2,8 @@ from ansys.dpf.core.dpf_operator import available_operator_names from ansys.dpf.core.core import load_library import argparse -import os from jinja2 import Template +from pathlib import Path def initialize_server(ansys_path=None): server = dpf.start_local_server(ansys_path=ansys_path) @@ -13,11 +13,14 @@ def initialize_server(ansys_path=None): print(f"Server Config: {server.config}") print(f"Server version: {dpf.global_server().version}") print("Loading Composites Plugin") - load_library(os.path.join(server.ansys_path, "dpf", "plugins", "dpf_composites", "composite_operators.dll")) + load_library( + Path(server.ansys_path) / "dpf" / "plugins" / "dpf_composites" / "composite_operators.dll" + ) print("Loading Acoustics Plugin") - load_library(os.path.join(server.ansys_path, "Acoustics", "SAS", "ads", "dpf_sound.dll")) + load_library(Path(server.ansys_path) / "Acoustics" / "SAS" / "ads" / "dpf_sound.dll") return server + def fetch_doc_info(server, operator_name): spec = dpf.Operator.operator_specification(op_name=operator_name, server=server) input_info = [] @@ -25,53 +28,59 @@ def fetch_doc_info(server, operator_name): configurations_info = [] for input_pin in spec.inputs: input = spec.inputs[input_pin] - input_info.append({ - "pin_number": input_pin, - "name": input.name, - "types": [str(t) for t in input._type_names], - "document": input.document, - "optional": input.optional, - }) + input_info.append( + { + "pin_number": input_pin, + "name": input.name, + "types": [str(t) for t in input._type_names], + "document": input.document, + "optional": input.optional, + } + ) for output_pin in spec.outputs: output = spec.outputs[output_pin] - output_info.append({ - "pin_number": output_pin, - "name": output.name, - "types": [str(t) for t in output._type_names], - "document": output.document, - "optional": output.optional, - }) + output_info.append( + { + "pin_number": output_pin, + "name": output.name, + "types": [str(t) for t in output._type_names], + "document": output.document, + "optional": output.optional, + } + ) for configuration_key in spec.config_specification: configuration = spec.config_specification[configuration_key] - configurations_info.append({ - "name": configuration.name, - "types": [str(t) for t in configuration.type_names], - "document": configuration.document, - "default_value": configuration.default_value_str, - }) + configurations_info.append( + { + "name": configuration.name, + "types": [str(t) for t in configuration.type_names], + "document": configuration.document, + "default_value": configuration.default_value_str, + } + ) properties = spec.properties if "plugin" in properties: plugin = properties["plugin"] else: plugin = "N/A" try: - scripting_name = properties['scripting_name'] - full_name = properties['category'] + "." + properties['scripting_name'] + scripting_name = properties["scripting_name"] + full_name = properties["category"] + "." + properties["scripting_name"] except KeyError: scripting_name = None full_name = None try: - user_name = properties['user_name'] + user_name = properties["user_name"] except KeyError: user_name = operator_name try: - category = properties['category'] + category = properties["category"] op_friendly_name = category + ": " + user_name except KeyError: category = "" op_friendly_name = user_name try: - license = properties['license'] + license = properties["license"] except KeyError: license = "None" scripting_info = { @@ -89,9 +98,10 @@ def fetch_doc_info(server, operator_name): "outputs": output_info, "configurations": configurations_info, "scripting_info": scripting_info, - "exposure": properties['exposure'], + "exposure": properties["exposure"], } + def get_plugin_operators(server, plugin_name): operators = available_operator_names(server) plugin_operators = [] @@ -101,26 +111,30 @@ def get_plugin_operators(server, plugin_name): plugin_operators.append(operator_name) return plugin_operators + def generate_operator_doc(server, operator_name, include_private): operator_info = fetch_doc_info(server, operator_name) if not include_private and operator_info["exposure"] == "private": return - script_dir = os.path.dirname(__file__) - root_dir = os.path.dirname(script_dir) - template_dir = os.path.join(root_dir, 'doc', 'source', 'operators_doc') - with open(os.path.join(template_dir, 'operator_doc_template.md'), 'r') as file: + script_path = Path(__file__) + root_dir = script_path.parent.parent + template_dir = Path(root_dir) / "doc" / "source" / "operators_doc" + with Path.open(Path(template_dir) / "operator_doc_template.md", "r") as file: template = Template(file.read()) - + output = template.render(operator_info) if "::" in operator_name: operator_name = operator_name.replace("::", "_") - with open(os.path.join(template_dir, f"{operator_name}.md"), 'w') as file: + with Path.open(Path(template_dir) / f"{operator_name}.md", "w") as file: file.write(output) + def main(): parser = argparse.ArgumentParser(description="Fetch available operators") parser.add_argument("--plugin", help="Filter operators by plugin") - parser.add_argument("--ansys_path", default=None, help="Path to Ansys DPF Server installation directory") + parser.add_argument( + "--ansys_path", default=None, help="Path to Ansys DPF Server installation directory" + ) parser.add_argument("--include_private", action="store_true", help="Include private operators") args = parser.parse_args() desired_plugin = args.plugin @@ -133,5 +147,6 @@ def main(): for operator_name in operators: generate_operator_doc(server, operator_name, args.include_private) + if __name__ == "__main__": - main() \ No newline at end of file + main() From 78147eb8ad56491af8c902d4af9ab396f73be680 Mon Sep 17 00:00:00 2001 From: Marshall Hanmer Date: Tue, 11 Feb 2025 15:33:36 -0500 Subject: [PATCH 06/17] Suggestions --- .ci/generate_operators_doc.py | 55 +++++++++++++++++------------------ 1 file changed, 26 insertions(+), 29 deletions(-) diff --git a/.ci/generate_operators_doc.py b/.ci/generate_operators_doc.py index c696521cb0..dd4009a735 100644 --- a/.ci/generate_operators_doc.py +++ b/.ci/generate_operators_doc.py @@ -5,19 +5,21 @@ from jinja2 import Template from pathlib import Path -def initialize_server(ansys_path=None): +def initialize_server(ansys_path=None, include_composites=False, include_sound=False): server = dpf.start_local_server(ansys_path=ansys_path) print(f"Ansys Path: {server.ansys_path}") print(f"Server Info: {server.info}") print(f"Server Context: {server.context}") print(f"Server Config: {server.config}") print(f"Server version: {dpf.global_server().version}") - print("Loading Composites Plugin") - load_library( - Path(server.ansys_path) / "dpf" / "plugins" / "dpf_composites" / "composite_operators.dll" - ) - print("Loading Acoustics Plugin") - load_library(Path(server.ansys_path) / "Acoustics" / "SAS" / "ads" / "dpf_sound.dll") + if include_composites: + print("Loading Composites Plugin") + load_library( + Path(server.ansys_path) / "dpf" / "plugins" / "dpf_composites" / "composite_operators.dll" + ) + if include_sound: + print("Loading Acoustics Plugin") + load_library(Path(server.ansys_path) / "Acoustics" / "SAS" / "ads" / "dpf_sound.dll") return server @@ -59,30 +61,23 @@ def fetch_doc_info(server, operator_name): } ) properties = spec.properties - if "plugin" in properties: - plugin = properties["plugin"] + plugin = properties.pop("plugin", "N/A") + + category = properties.pop("category", None) + + scripting_name = properties.pop("scripting_name", None) + if category and scripting_name: + full_name = category + "." + scripting_name else: - plugin = "N/A" - try: - scripting_name = properties["scripting_name"] - full_name = properties["category"] + "." + properties["scripting_name"] - except KeyError: - scripting_name = None full_name = None - try: - user_name = properties["user_name"] - except KeyError: - user_name = operator_name - try: - category = properties["category"] - op_friendly_name = category + ": " + user_name - except KeyError: - category = "" - op_friendly_name = user_name - try: - license = properties["license"] - except KeyError: - license = "None" + + user_name = properties.pop("user_name", operator_name) + + op_friendly_name = user_name + if category: + op_friendly_name = category + ":" + op_friendly_name + + license = properties.pop("license", "None") scripting_info = { "category": category, "plugin": plugin, @@ -136,6 +131,8 @@ def main(): "--ansys_path", default=None, help="Path to Ansys DPF Server installation directory" ) parser.add_argument("--include_private", action="store_true", help="Include private operators") + parser.add_argument("--include_composites", action="store_true", help="Include composites operators") + parser.add_argument("--include_sound", action="store_true", help="Include sound operators") args = parser.parse_args() desired_plugin = args.plugin From 5ec8abe4d3a9482d77c747ada7017a454775f5ea Mon Sep 17 00:00:00 2001 From: Marshall Hanmer Date: Tue, 11 Feb 2025 15:54:45 -0500 Subject: [PATCH 07/17] Process new inputs properly --- .ci/generate_operators_doc.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.ci/generate_operators_doc.py b/.ci/generate_operators_doc.py index dd4009a735..9f746f8558 100644 --- a/.ci/generate_operators_doc.py +++ b/.ci/generate_operators_doc.py @@ -19,7 +19,9 @@ def initialize_server(ansys_path=None, include_composites=False, include_sound=F ) if include_sound: print("Loading Acoustics Plugin") - load_library(Path(server.ansys_path) / "Acoustics" / "SAS" / "ads" / "dpf_sound.dll") + load_library( + Path(server.ansys_path) / "Acoustics" / "SAS" / "ads" / "dpf_sound.dll" + ) return server @@ -136,7 +138,7 @@ def main(): args = parser.parse_args() desired_plugin = args.plugin - server = initialize_server(args.ansys_path) + server = initialize_server(args.ansys_path, args.include_composites, args.include_sound) if desired_plugin is None: operators = available_operator_names(server) else: From cd12ecc3a6639d81169768ead17a059cd1bed88a Mon Sep 17 00:00:00 2001 From: Marshall Hanmer Date: Wed, 12 Feb 2025 10:14:04 -0500 Subject: [PATCH 08/17] Style --- .ci/generate_operators_doc.py | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/.ci/generate_operators_doc.py b/.ci/generate_operators_doc.py index 9f746f8558..2869533f97 100644 --- a/.ci/generate_operators_doc.py +++ b/.ci/generate_operators_doc.py @@ -1,10 +1,13 @@ -from ansys.dpf import core as dpf -from ansys.dpf.core.dpf_operator import available_operator_names -from ansys.dpf.core.core import load_library import argparse -from jinja2 import Template from pathlib import Path +from jinja2 import Template + +from ansys.dpf import core as dpf +from ansys.dpf.core.core import load_library +from ansys.dpf.core.dpf_operator import available_operator_names + + def initialize_server(ansys_path=None, include_composites=False, include_sound=False): server = dpf.start_local_server(ansys_path=ansys_path) print(f"Ansys Path: {server.ansys_path}") @@ -15,13 +18,15 @@ def initialize_server(ansys_path=None, include_composites=False, include_sound=F if include_composites: print("Loading Composites Plugin") load_library( - Path(server.ansys_path) / "dpf" / "plugins" / "dpf_composites" / "composite_operators.dll" + Path(server.ansys_path) + / "dpf" + / "plugins" + / "dpf_composites" + / "composite_operators.dll" ) if include_sound: print("Loading Acoustics Plugin") - load_library( - Path(server.ansys_path) / "Acoustics" / "SAS" / "ads" / "dpf_sound.dll" - ) + load_library(Path(server.ansys_path) / "Acoustics" / "SAS" / "ads" / "dpf_sound.dll") return server @@ -74,7 +79,7 @@ def fetch_doc_info(server, operator_name): full_name = None user_name = properties.pop("user_name", operator_name) - + op_friendly_name = user_name if category: op_friendly_name = category + ":" + op_friendly_name @@ -133,7 +138,9 @@ def main(): "--ansys_path", default=None, help="Path to Ansys DPF Server installation directory" ) parser.add_argument("--include_private", action="store_true", help="Include private operators") - parser.add_argument("--include_composites", action="store_true", help="Include composites operators") + parser.add_argument( + "--include_composites", action="store_true", help="Include composites operators" + ) parser.add_argument("--include_sound", action="store_true", help="Include sound operators") args = parser.parse_args() desired_plugin = args.plugin From 73b6e772e2d206048af1afbc74451140b3fb15c2 Mon Sep 17 00:00:00 2001 From: Marshall Hanmer Date: Wed, 19 Feb 2025 12:16:06 -0500 Subject: [PATCH 09/17] Add YAML frontmatter metadata to operators doc template --- doc/source/operators_doc/operator_doc_template.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/doc/source/operators_doc/operator_doc_template.md b/doc/source/operators_doc/operator_doc_template.md index e62361675f..ffc938bf93 100644 --- a/doc/source/operators_doc/operator_doc_template.md +++ b/doc/source/operators_doc/operator_doc_template.md @@ -1,3 +1,9 @@ +--- +category: {{ scripting_info.category }} +plugin: {{ scripting_info.plugin }} +license: {{ scripting_info.license }} +--- + # {{ operator_name }} ## Description From ae5c6a290d91691636e2f241119424f6ec9f0b4d Mon Sep 17 00:00:00 2001 From: Marshall Hanmer Date: Wed, 19 Feb 2025 15:00:35 -0500 Subject: [PATCH 10/17] Handle errors in fetching config_specification --- .ci/generate_operators_doc.py | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/.ci/generate_operators_doc.py b/.ci/generate_operators_doc.py index 2869533f97..0b8ed9ea90 100644 --- a/.ci/generate_operators_doc.py +++ b/.ci/generate_operators_doc.py @@ -57,16 +57,19 @@ def fetch_doc_info(server, operator_name): "optional": output.optional, } ) - for configuration_key in spec.config_specification: - configuration = spec.config_specification[configuration_key] - configurations_info.append( - { - "name": configuration.name, - "types": [str(t) for t in configuration.type_names], - "document": configuration.document, - "default_value": configuration.default_value_str, - } - ) + try: + for configuration_key in spec.config_specification: + configuration = spec.config_specification[configuration_key] + configurations_info.append( + { + "name": configuration.name, + "types": [str(t) for t in configuration.type_names], + "document": configuration.document, + "default_value": configuration.default_value_str, + } + ) + except: + print(f"Operator {operator_name} - error in fetching configuration info!") properties = spec.properties plugin = properties.pop("plugin", "N/A") @@ -85,6 +88,8 @@ def fetch_doc_info(server, operator_name): op_friendly_name = category + ":" + op_friendly_name license = properties.pop("license", "None") + + exposure = properties.pop("exposure", "N/A") scripting_info = { "category": category, "plugin": plugin, @@ -100,7 +105,7 @@ def fetch_doc_info(server, operator_name): "outputs": output_info, "configurations": configurations_info, "scripting_info": scripting_info, - "exposure": properties["exposure"], + "exposure": exposure, } From e84339c8cad3a8d4d7b5870f2622b85223a09605 Mon Sep 17 00:00:00 2001 From: Marshall Hanmer Date: Fri, 21 Feb 2025 09:55:03 -0500 Subject: [PATCH 11/17] PR suggestion --- .ci/generate_operators_doc.py | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/.ci/generate_operators_doc.py b/.ci/generate_operators_doc.py index 0b8ed9ea90..afba7e9959 100644 --- a/.ci/generate_operators_doc.py +++ b/.ci/generate_operators_doc.py @@ -57,19 +57,16 @@ def fetch_doc_info(server, operator_name): "optional": output.optional, } ) - try: - for configuration_key in spec.config_specification: - configuration = spec.config_specification[configuration_key] - configurations_info.append( - { - "name": configuration.name, - "types": [str(t) for t in configuration.type_names], - "document": configuration.document, - "default_value": configuration.default_value_str, - } - ) - except: - print(f"Operator {operator_name} - error in fetching configuration info!") + for configuration_key in spec.config_specification: + configuration = spec.config_specification[configuration_key] + configurations_info.append( + { + "name": configuration.name, + "types": [str(t) for t in configuration.type_names], + "document": configuration.document, + "default_value": configuration.default_value_str, + } + ) properties = spec.properties plugin = properties.pop("plugin", "N/A") @@ -89,7 +86,7 @@ def fetch_doc_info(server, operator_name): license = properties.pop("license", "None") - exposure = properties.pop("exposure", "N/A") + exposure = properties.pop("exposure", "private") scripting_info = { "category": category, "plugin": plugin, From c6ac5f06f7ce70b12125d716b18b2c136ec6e8d7 Mon Sep 17 00:00:00 2001 From: Marshall Hanmer Date: Wed, 19 Mar 2025 11:17:57 -0400 Subject: [PATCH 12/17] Restructure output --- .ci/generate_operators_doc.py | 20 +++++++++++++++++--- .gitignore | 2 +- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/.ci/generate_operators_doc.py b/.ci/generate_operators_doc.py index afba7e9959..ff8a97d6de 100644 --- a/.ci/generate_operators_doc.py +++ b/.ci/generate_operators_doc.py @@ -1,5 +1,6 @@ import argparse from pathlib import Path +import shutil from jinja2 import Template @@ -118,18 +119,31 @@ def get_plugin_operators(server, plugin_name): def generate_operator_doc(server, operator_name, include_private): operator_info = fetch_doc_info(server, operator_name) + scripting_name = operator_info["scripting_info"]["scripting_name"] + category = operator_info["scripting_info"]["category"] + if scripting_name: + file_name = scripting_name + else: + file_name = operator_name + if "::" in file_name: + file_name = file_name.replace("::", "_") if not include_private and operator_info["exposure"] == "private": return script_path = Path(__file__) root_dir = script_path.parent.parent template_dir = Path(root_dir) / "doc" / "source" / "operators_doc" + category_dir = Path(template_dir) / category + if not category_dir.exists() and category is not None: + category_dir.mkdir() + if category is not None: + file_dir = category_dir + else: + file_dir = template_dir with Path.open(Path(template_dir) / "operator_doc_template.md", "r") as file: template = Template(file.read()) output = template.render(operator_info) - if "::" in operator_name: - operator_name = operator_name.replace("::", "_") - with Path.open(Path(template_dir) / f"{operator_name}.md", "w") as file: + with Path.open(Path(file_dir) / f"{file_name}.md", "w") as file: file.write(output) diff --git a/.gitignore b/.gitignore index 28b4943d5c..1867277ea5 100644 --- a/.gitignore +++ b/.gitignore @@ -70,7 +70,7 @@ instance/ doc/_build/ # Operators documentation -doc/source/operators_doc/*.md +doc/source/operators_doc/**/* !doc/source/operators_doc/operator_doc_template.md # PyBuilder From 5aaab20aa71853a1fb08c80fed6f02435b469975 Mon Sep 17 00:00:00 2001 From: Marshall Hanmer Date: Fri, 21 Mar 2025 10:18:05 -0400 Subject: [PATCH 13/17] Add toc tree generation --- .ci/generate_operators_doc.py | 35 +++++++++++++++++++++++- .gitignore | 1 + doc/source/operators_doc/toc_template.j2 | 8 ++++++ 3 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 doc/source/operators_doc/toc_template.j2 diff --git a/.ci/generate_operators_doc.py b/.ci/generate_operators_doc.py index ff8a97d6de..b5cf5a3d0c 100644 --- a/.ci/generate_operators_doc.py +++ b/.ci/generate_operators_doc.py @@ -1,6 +1,7 @@ import argparse from pathlib import Path -import shutil +import json +import os from jinja2 import Template @@ -146,6 +147,34 @@ def generate_operator_doc(server, operator_name, include_private): with Path.open(Path(file_dir) / f"{file_name}.md", "w") as file: file.write(output) +def generate_toc_tree(docs_path): + data = [] + for folder in docs_path.iterdir(): + if folder.is_dir(): # Ensure 'folder' is a directory + category = folder.name + operators = [] # Reset operators for each category + for file in folder.iterdir(): + if file.is_file() and file.suffix == ".md": # Ensure 'file' is a file with .md extension + file_name = file.name + operator_name = file_name.replace("_", " ").replace(".md", "") + operators.append({"operator_name": operator_name, "file_name": file_name}) + data.append({"category": category, "operators": operators}) + + # Write the JSON file for debugging purposes + with open(docs_path / "toc_tree.json", "w") as file: + json.dump(data, file, indent=4) + + # Render the Jinja2 template + template_path = docs_path / "toc_template.j2" + with open(template_path, "r") as template_file: + template = Template(template_file.read()) + output = template.render(data=data) # Pass 'data' as a named argument + + # Write the rendered output to toc.md + with open(docs_path / "toc.md", "w") as file: + file.write(output) + + def main(): parser = argparse.ArgumentParser(description="Fetch available operators") @@ -169,6 +198,10 @@ def main(): for operator_name in operators: generate_operator_doc(server, operator_name, args.include_private) + docs_path = Path(__file__).parent.parent / "doc" / "source" / "operators_doc" + print(docs_path) + generate_toc_tree(docs_path) + if __name__ == "__main__": main() diff --git a/.gitignore b/.gitignore index 1867277ea5..2a10ebaba0 100644 --- a/.gitignore +++ b/.gitignore @@ -72,6 +72,7 @@ doc/_build/ # Operators documentation doc/source/operators_doc/**/* !doc/source/operators_doc/operator_doc_template.md +!doc/source/operators_doc/toc_template.j2 # PyBuilder .pybuilder/ diff --git a/doc/source/operators_doc/toc_template.j2 b/doc/source/operators_doc/toc_template.j2 new file mode 100644 index 0000000000..90c67c5e32 --- /dev/null +++ b/doc/source/operators_doc/toc_template.j2 @@ -0,0 +1,8 @@ +- name: Introduction + href: index.md +{% for category in data %} +- name: {{ category.category }} + items:{% for operator in category.operators %} + - name: {{ operator.operator_name }} + href: {{ operator.file_name }}{% endfor %} +{% endfor %} \ No newline at end of file From 0d2ab7de93f48858d8cab8face4bfda9f0cbfe5e Mon Sep 17 00:00:00 2001 From: Marshall Hanmer Date: Fri, 21 Mar 2025 10:20:23 -0400 Subject: [PATCH 14/17] Remove unnecessary json file write --- .ci/generate_operators_doc.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.ci/generate_operators_doc.py b/.ci/generate_operators_doc.py index b5cf5a3d0c..0b765141c4 100644 --- a/.ci/generate_operators_doc.py +++ b/.ci/generate_operators_doc.py @@ -1,7 +1,5 @@ import argparse from pathlib import Path -import json -import os from jinja2 import Template @@ -160,10 +158,6 @@ def generate_toc_tree(docs_path): operators.append({"operator_name": operator_name, "file_name": file_name}) data.append({"category": category, "operators": operators}) - # Write the JSON file for debugging purposes - with open(docs_path / "toc_tree.json", "w") as file: - json.dump(data, file, indent=4) - # Render the Jinja2 template template_path = docs_path / "toc_template.j2" with open(template_path, "r") as template_file: From 7260da448b0615c511fe88f7ac3985246251ad50 Mon Sep 17 00:00:00 2001 From: Marshall Hanmer Date: Fri, 21 Mar 2025 10:44:37 -0400 Subject: [PATCH 15/17] Ruff --- .ci/generate_operators_doc.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.ci/generate_operators_doc.py b/.ci/generate_operators_doc.py index 0b765141c4..6af212c98e 100644 --- a/.ci/generate_operators_doc.py +++ b/.ci/generate_operators_doc.py @@ -145,6 +145,7 @@ def generate_operator_doc(server, operator_name, include_private): with Path.open(Path(file_dir) / f"{file_name}.md", "w") as file: file.write(output) + def generate_toc_tree(docs_path): data = [] for folder in docs_path.iterdir(): @@ -152,7 +153,9 @@ def generate_toc_tree(docs_path): category = folder.name operators = [] # Reset operators for each category for file in folder.iterdir(): - if file.is_file() and file.suffix == ".md": # Ensure 'file' is a file with .md extension + if ( + file.is_file() and file.suffix == ".md" + ): # Ensure 'file' is a file with .md extension file_name = file.name operator_name = file_name.replace("_", " ").replace(".md", "") operators.append({"operator_name": operator_name, "file_name": file_name}) @@ -160,14 +163,13 @@ def generate_toc_tree(docs_path): # Render the Jinja2 template template_path = docs_path / "toc_template.j2" - with open(template_path, "r") as template_file: + with Path.open(template_path, "r") as template_file: template = Template(template_file.read()) output = template.render(data=data) # Pass 'data' as a named argument # Write the rendered output to toc.md - with open(docs_path / "toc.md", "w") as file: + with Path.open(docs_path / "toc.md", "w") as file: file.write(output) - def main(): From 122e29968e37e9466133d167136cd9b019b13d95 Mon Sep 17 00:00:00 2001 From: Marshall Hanmer Date: Fri, 21 Mar 2025 11:39:07 -0400 Subject: [PATCH 16/17] Feedback --- .ci/generate_operators_doc.py | 5 +++-- doc/source/operators_doc/toc_template.j2 | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.ci/generate_operators_doc.py b/.ci/generate_operators_doc.py index 6af212c98e..3d3ed15902 100644 --- a/.ci/generate_operators_doc.py +++ b/.ci/generate_operators_doc.py @@ -157,8 +157,9 @@ def generate_toc_tree(docs_path): file.is_file() and file.suffix == ".md" ): # Ensure 'file' is a file with .md extension file_name = file.name + file_path = f"{category}/{file_name}" operator_name = file_name.replace("_", " ").replace(".md", "") - operators.append({"operator_name": operator_name, "file_name": file_name}) + operators.append({"operator_name": operator_name, "file_path": file_path}) data.append({"category": category, "operators": operators}) # Render the Jinja2 template @@ -168,7 +169,7 @@ def generate_toc_tree(docs_path): output = template.render(data=data) # Pass 'data' as a named argument # Write the rendered output to toc.md - with Path.open(docs_path / "toc.md", "w") as file: + with Path.open(docs_path / "toc.yml", "w") as file: file.write(output) diff --git a/doc/source/operators_doc/toc_template.j2 b/doc/source/operators_doc/toc_template.j2 index 90c67c5e32..1bc730baa2 100644 --- a/doc/source/operators_doc/toc_template.j2 +++ b/doc/source/operators_doc/toc_template.j2 @@ -4,5 +4,5 @@ - name: {{ category.category }} items:{% for operator in category.operators %} - name: {{ operator.operator_name }} - href: {{ operator.file_name }}{% endfor %} + href: {{ operator.file_path }}{% endfor %} {% endfor %} \ No newline at end of file From 2c03c97fb25af9246991d6c86c40e4b2f99383ff Mon Sep 17 00:00:00 2001 From: Marshall Hanmer Date: Fri, 21 Mar 2025 11:50:19 -0400 Subject: [PATCH 17/17] Remove space in toc.yml generation --- doc/source/operators_doc/toc_template.j2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/operators_doc/toc_template.j2 b/doc/source/operators_doc/toc_template.j2 index 1bc730baa2..a7728cef6a 100644 --- a/doc/source/operators_doc/toc_template.j2 +++ b/doc/source/operators_doc/toc_template.j2 @@ -1,6 +1,6 @@ - name: Introduction href: index.md -{% for category in data %} +{% for category in data -%} - name: {{ category.category }} items:{% for operator in category.operators %} - name: {{ operator.operator_name }}