diff --git a/.gitignore b/.gitignore index 6478ba29..75768e14 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,7 @@ # These are directories used by IDEs for storing settings .idea/ .vscode/ +.cache # These are common Python virtual enviornment directory names venv/ diff --git a/include/pluginplay/printing/mermaid.hpp b/include/pluginplay/printing/mermaid.hpp new file mode 100644 index 00000000..1e7578ab --- /dev/null +++ b/include/pluginplay/printing/mermaid.hpp @@ -0,0 +1,28 @@ +/* + * Copyright 2023 NWChemEx-Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once +#include +#include + +namespace pluginplay::printing { + +std::stringstream create_mermaid_graph(const pluginplay::ModuleManager& mm); + +void print_submods(const std::string module_key, + const pluginplay::ModuleManager& mm, std::stringstream& ss, + std::string& module_code); +} // namespace pluginplay::printing diff --git a/include/pluginplay/printing/printing.hpp b/include/pluginplay/printing/printing.hpp index b35cb7fc..d571b5ec 100644 --- a/include/pluginplay/printing/printing.hpp +++ b/include/pluginplay/printing/printing.hpp @@ -17,3 +17,4 @@ #pragma once #include #include +#include diff --git a/src/pluginplay/printing/mermaid.cpp b/src/pluginplay/printing/mermaid.cpp new file mode 100644 index 00000000..7a1e734f --- /dev/null +++ b/src/pluginplay/printing/mermaid.cpp @@ -0,0 +1,76 @@ +/* + * Copyright 2024 NWChemEx-Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +namespace pluginplay::printing { + +void add_letter(std::string& code) { + if(code.size() == 0) return; + for(int i = (code.size() - 1); i >= 0; --i) { + if(code[i] == 'Z') { + code[i] = 'A'; + } else { + code[i]++; + return; + } + } +} + +void print_submods(const std::string module_key, + const pluginplay::ModuleManager& mm, std::stringstream& ss, + std::string& module_code) { + const auto& mm_module = mm.at(module_key); // Results in a PluginPlay Module + const auto& submods = mm_module.submods(); // Results in list of Submodules + auto main_mod_code = module_code; + + // Key is the ID/Tag, Value is the reference to the Module + for(const auto& [key, value] : submods) { + ss << "\n\t" << main_mod_code << "-->" + << "|" << key << "| "; + if(value.has_module() == false) { + add_letter(module_code); + ss << module_code << "[No Submodule associated with Key]"; + } else { + add_letter(module_code); + ss << module_code << "[" << value.get_name() << "]"; + print_submods(value.get_name(), mm, ss, module_code); + } + } +} + +std::stringstream create_mermaid_graph(const pluginplay::ModuleManager& mm) { + auto n_modules = mm.size(); + std::stringstream ss; + std::string module_code = "AAA"; + if(n_modules == 0) { + ss << "No modules are loaded, load some modules!"; + } else { + for(decltype(n_modules) i = 0; i < n_modules; i++) { + ss << "\n```mermaid"; + ss << "\nflowchart LR\n"; + auto mod = mm.keys()[i]; + ss << "\t" << module_code << "[" << mod << "]"; + print_submods(mod, mm, ss, module_code); + ss << "\n```"; + add_letter(module_code); + } + } + return ss; +} +} // namespace pluginplay::printing diff --git a/tests/cxx/unit_tests/pluginplay/printing/mermaid.cpp b/tests/cxx/unit_tests/pluginplay/printing/mermaid.cpp new file mode 100644 index 00000000..439a3bd2 --- /dev/null +++ b/tests/cxx/unit_tests/pluginplay/printing/mermaid.cpp @@ -0,0 +1,169 @@ +/* + * Copyright 2024 NWChemEx-Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "../catch.hpp" +#include "../test_common.hpp" +#include "unit_testing_pts.hpp" +#include +#include +#include +#include +#include +#include + +DECLARE_MODULE(OneModule); +inline MODULE_CTOR(OneModule) { satisfies_property_type(); } +inline MODULE_RUN(OneModule) { return results(); } + +DECLARE_MODULE(TwoModule); +inline MODULE_CTOR(TwoModule) { satisfies_property_type(); } +inline MODULE_RUN(TwoModule) { return results(); } + +DECLARE_MODULE(ThreeModule); +inline MODULE_CTOR(ThreeModule) { satisfies_property_type(); } +inline MODULE_RUN(ThreeModule) { return results(); } + +DECLARE_MODULE(OneModuleSubTwo); +inline MODULE_CTOR(OneModuleSubTwo) { + satisfies_property_type(); + add_submodule("Submodule"); +} +inline MODULE_RUN(OneModuleSubTwo) { return results(); } + +DECLARE_MODULE(TwoModuleSubThree); +inline MODULE_CTOR(TwoModuleSubThree) { + satisfies_property_type(); + add_submodule("Submodule"); +} +inline MODULE_RUN(TwoModuleSubThree) { return results(); } + +DECLARE_MODULE(OneModuleSubTwoThree); +inline MODULE_CTOR(OneModuleSubTwoThree) { + satisfies_property_type(); + add_submodule("Submodule 1"); + add_submodule("Submodule 2"); +} +inline MODULE_RUN(OneModuleSubTwoThree) { return results(); } + +TEST_CASE("Mermaid Graph") { + SECTION("No Modules") { + pluginplay::ModuleManager no_mods; + auto hello = pluginplay::printing::create_mermaid_graph(no_mods); + REQUIRE(hello.str() == "No modules are loaded, load some modules!"); + } + + SECTION("One Module") { + pluginplay::ModuleManager one_mod; + one_mod.add_module("One Module"); + auto onemod = pluginplay::printing::create_mermaid_graph(one_mod); + std::string val = "\n```mermaid\nflowchart LR\n\tAAA[One Module]\n```"; + + REQUIRE(val == onemod.str()); + } + + SECTION("2 Modules, Not Related") { + std::stringstream ss; + pluginplay::ModuleManager two_mods_unrelated; + two_mods_unrelated.add_module("One Module"); + two_mods_unrelated.add_module("Two Module"); + auto twomod = + pluginplay::printing::create_mermaid_graph(two_mods_unrelated); + ss << "\n```mermaid\nflowchart LR\n\tAAA[One Module]\n```"; + ss << "\n```mermaid\nflowchart LR\n\tAAB[Two Module]\n```"; + + REQUIRE(ss.str() == twomod.str()); + } + + SECTION("2 Modules, Related") { + std::stringstream ss2; + pluginplay::ModuleManager two_mods_related; + two_mods_related.add_module("One Module Submod Two"); + two_mods_related.add_module("Two Module"); + two_mods_related.change_submod("One Module Submod Two", "Submodule", + "Two Module"); + auto twomodrelated = + pluginplay::printing::create_mermaid_graph(two_mods_related); + ss2 << "\n```mermaid\nflowchart LR\n\tAAA[One Module Submod Two]\n"; + ss2 << "\tAAA-->|Submodule| AAB[Two Module]\n```"; + ss2 << "\n```mermaid\nflowchart LR\n\tAAC[Two Module]\n```"; + REQUIRE(ss2.str() == twomodrelated.str()); + } + + SECTION("3 Modules, Nested") { + std::stringstream ss3; + pluginplay::ModuleManager three_mods_recursive; + three_mods_recursive.add_module( + "One Module Submod Two"); + three_mods_recursive.add_module( + "Two Module Submod Three"); + three_mods_recursive.add_module("Three Module"); + three_mods_recursive.change_submod("One Module Submod Two", "Submodule", + "Two Module Submod Three"); + three_mods_recursive.change_submod("Two Module Submod Three", + "Submodule", "Three Module"); + auto threemodrecursive = + pluginplay::printing::create_mermaid_graph(three_mods_recursive); + ss3 << "\n```mermaid\n"; + ss3 << "flowchart LR\n"; + ss3 << "\tAAA[One Module Submod Two]\n"; + ss3 << "\tAAA-->|Submodule| AAB[Two Module Submod Three]\n"; + ss3 << "\tAAB-->|Submodule| AAC[Three Module]\n"; + ss3 << "```"; + ss3 << "\n```mermaid\n"; + ss3 << "flowchart LR\n"; + ss3 << "\tAAD[Three Module]\n"; + ss3 << "```"; + ss3 << "\n```mermaid\n"; + ss3 << "flowchart LR\n"; + ss3 << "\tAAE[Two Module Submod Three]\n"; + ss3 << "\tAAE-->|Submodule| AAF[Three Module]\n"; + ss3 << "```"; + + REQUIRE(ss3.str() == threemodrecursive.str()); + } + + SECTION("3 Modules, Branched") { + std::stringstream ss4; + pluginplay::ModuleManager three_mods_branched; + three_mods_branched.add_module( + "One Module Submod Two Three"); + three_mods_branched.add_module("One Module"); + three_mods_branched.add_module("Two Module"); + three_mods_branched.change_submod("One Module Submod Two Three", + "Submodule 1", "One Module"); + three_mods_branched.change_submod("One Module Submod Two Three", + "Submodule 2", "Two Module"); + auto threemodsbranched = + pluginplay::printing::create_mermaid_graph(three_mods_branched); + + ss4 << "\n```mermaid\n"; + ss4 << "flowchart LR\n"; + ss4 << "\tAAA[One Module]\n"; + ss4 << "```"; + ss4 << "\n```mermaid\n"; + ss4 << "flowchart LR\n"; + ss4 << "\tAAB[One Module Submod Two Three]\n"; + ss4 << "\tAAB-->|Submodule 1| AAC[One Module]\n"; + ss4 << "\tAAB-->|Submodule 2| AAD[Two Module]\n"; + ss4 << "```"; + ss4 << "\n```mermaid\n"; + ss4 << "flowchart LR\n"; + ss4 << "\tAAE[Two Module]\n"; + ss4 << "```"; + + REQUIRE(ss4.str() == threemodsbranched.str()); + } +}