Repository: BlueBrain/libsonata (language: python)

Issue:
SimulationConfig doesn't have a way to list the names of the available reports.



Discussion / hints:

Fix patch:
diff --git a/include/bbp/sonata/config.h b/include/bbp/sonata/config.h
index c9e0800a..12db1f5e 100644
--- a/include/bbp/sonata/config.h
+++ b/include/bbp/sonata/config.h
@@ -373,6 +373,11 @@ class SONATA_API SimulationConfig
      */
     const std::string& getJSON() const noexcept;
 
+    /**
+     * Returns circuit config file associated with this simulation config
+     */
+    const std::string& getNetwork() const noexcept;
+
     /**
      * Returns the Run section of the simulation configuration.
      */
@@ -388,6 +393,11 @@ class SONATA_API SimulationConfig
      */
     const Conditions& getConditions() const noexcept;
 
+    /**
+     * Returns the names of the reports
+     */
+    std::set<std::string> listReportNames() const;
+
     /**
      * Returns the given report parameters.
      *
@@ -396,8 +406,16 @@ class SONATA_API SimulationConfig
      */
     const Report& getReport(const std::string& name) const;
 
-    const std::string& getNetwork() const noexcept;
+    /**
+     * Returns the names of the reports
+     */
+    std::set<std::string> listInputNames() const;
 
+    /**
+     * Returns the given input parameters.
+     *
+     * \throws SonataError if the given input name does not exist
+     */
     const Input& getInput(const std::string& name) const;
 
   private:
diff --git a/python/bindings.cpp b/python/bindings.cpp
index 7e7c3896..8a7a1718 100644
--- a/python/bindings.cpp
+++ b/python/bindings.cpp
@@ -726,6 +726,8 @@ PYBIND11_MODULE(_libsonata, m) {
         .def_property_readonly("output", &SimulationConfig::getOutput)
         .def_property_readonly("conditions", &SimulationConfig::getConditions)
         .def_property_readonly("network", &SimulationConfig::getNetwork)
+        .def_property_readonly("list_report_names", &SimulationConfig::listReportNames)
+        .def_property_readonly("list_input_names", &SimulationConfig::listInputNames)
         .def("report", &SimulationConfig::getReport, "name"_a)
         .def("input", &SimulationConfig::getInput, "name"_a);
 
diff --git a/python/generated/docstrings.h b/python/generated/docstrings.h
index 2926871d..8154134a 100644
--- a/python/generated/docstrings.h
+++ b/python/generated/docstrings.h
@@ -779,11 +779,15 @@ static const char *__doc_bbp_sonata_SimulationConfig_getBasePath = R"doc(Returns
 
 static const char *__doc_bbp_sonata_SimulationConfig_getConditions = R"doc(Returns the Conditions section of the simulation configuration.)doc";
 
-static const char *__doc_bbp_sonata_SimulationConfig_getInput = R"doc()doc";
+static const char *__doc_bbp_sonata_SimulationConfig_getInput =
+R"doc(Returns the given input parameters.
+
+Throws:
+    SonataError if the given input name does not exist)doc";
 
 static const char *__doc_bbp_sonata_SimulationConfig_getJSON = R"doc(Returns the JSON content of the simulation config file)doc";
 
-static const char *__doc_bbp_sonata_SimulationConfig_getNetwork = R"doc()doc";
+static const char *__doc_bbp_sonata_SimulationConfig_getNetwork = R"doc(Returns circuit config file associated with this simulation config)doc";
 
 static const char *__doc_bbp_sonata_SimulationConfig_getOutput = R"doc(Returns the Output section of the simulation configuration.)doc";
 
@@ -800,6 +804,10 @@ static const char *__doc_bbp_sonata_SimulationConfig_inputs = R"doc()doc";
 
 static const char *__doc_bbp_sonata_SimulationConfig_jsonContent = R"doc()doc";
 
+static const char *__doc_bbp_sonata_SimulationConfig_listInputNames = R"doc(Returns the names of the reports)doc";
+
+static const char *__doc_bbp_sonata_SimulationConfig_listReportNames = R"doc(Returns the names of the reports)doc";
+
 static const char *__doc_bbp_sonata_SimulationConfig_network = R"doc()doc";
 
 static const char *__doc_bbp_sonata_SimulationConfig_output = R"doc()doc";
diff --git a/src/config.cpp b/src/config.cpp
index 8a42494c..7c2d5bdb 100644
--- a/src/config.cpp
+++ b/src/config.cpp
@@ -12,7 +12,6 @@
 #include <bbp/sonata/config.h>
 #include <bbp/sonata/optional.hpp>
 
-#include <algorithm>  // transform
 #include <bbp/sonata/optional.hpp>
 #include <cassert>
 #include <fstream>
@@ -867,6 +866,10 @@ const std::string& SimulationConfig::getNetwork() const noexcept {
     return _network;
 }
 
+std::set<std::string> SimulationConfig::listReportNames() const {
+    return getMapKeys(_reports);
+}
+
 const SimulationConfig::Report& SimulationConfig::getReport(const std::string& name) const {
     const auto it = _reports.find(name);
     if (it == _reports.end()) {
@@ -877,6 +880,10 @@ const SimulationConfig::Report& SimulationConfig::getReport(const std::string& n
     return it->second;
 }
 
+std::set<std::string> SimulationConfig::listInputNames() const {
+    return getMapKeys(_inputs);
+}
+
 const SimulationConfig::Input& SimulationConfig::getInput(const std::string& name) const {
     const auto it = _inputs.find(name);
     if (it == _inputs.end())
diff --git a/src/node_sets.cpp b/src/node_sets.cpp
index 434e3687..075f9a89 100644
--- a/src/node_sets.cpp
+++ b/src/node_sets.cpp
@@ -80,12 +80,7 @@ class NodeSets
 
 
     std::set<std::string> names() const {
-        std::set<std::string> ret;
-        std::transform(begin(node_sets_),
-                       end(node_sets_),
-                       std::inserter(ret, ret.begin()),
-                       [](decltype(node_sets_)::value_type const& pair) { return pair.first; });
-        return ret;
+        return getMapKeys(node_sets_);
     }
 
     std::string toJSON() const {
diff --git a/src/utils.h b/src/utils.h
index 2a5ab1d6..e453e633 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -9,7 +9,13 @@
 
 #pragma once
 
+#include <algorithm>  // std::transform
+#include <iterator>   // std::inserter
+#include <map>
+#include <set>
 #include <string>
+#include <unordered_map>
+#include <utility>
 #include <vector>
 
 #include <bbp/sonata/population.h>
@@ -29,3 +35,12 @@ bbp::sonata::Selection _getMatchingSelection(const std::vector<T>& values, Unary
     }
     return Selection::fromValues(ids);
 }
+
+template <typename T>
+std::set<std::string> getMapKeys(const T& map) {
+    std::set<std::string> ret;
+    std::transform(map.begin(), map.end(), std::inserter(ret, ret.end()), [](const auto& pair) {
+        return pair.first;
+    });
+    return ret;
+}


Test patch:
diff --git a/python/tests/test.py b/python/tests/test.py
index 3d3076a7..ef43b1f2 100644
--- a/python/tests/test.py
+++ b/python/tests/test.py
@@ -602,6 +602,9 @@ def test_basic(self):
         self.assertEqual(self.config.conditions.minis_single_vesicle, False)
         self.assertEqual(self.config.conditions.randomize_gaba_rise_time, False)
 
+        self.assertEqual(self.config.list_report_names,
+                         { "axonal_comp_centers", "cell_imembrane", "compartment", "soma" })
+
         self.assertEqual(self.config.report('soma').cells, 'Mosaic')
         self.assertEqual(self.config.report('soma').type, Report.Type.compartment)
         self.assertEqual(self.config.report('soma').compartments, Report.Compartments.center)
@@ -622,6 +625,21 @@ def test_basic(self):
         self.assertEqual(self.config.network,
                          os.path.abspath(os.path.join(PATH, 'config/circuit_config.json')))
 
+        self.assertEqual(self.config.list_input_names,
+                         {"ex_extracellular_stimulation",
+                          "ex_hyperpolarizing",
+                          "ex_linear",
+                          "ex_noise_mean",
+                          "ex_noise_meanpercent",
+                          "ex_pulse",
+                          "ex_rel_linear",
+                          "ex_rel_shotnoise",
+                          "ex_replay",
+                          "ex_seclamp",
+                          "ex_shotnoise",
+                          "ex_subthreshold"
+                          })
+
         self.assertEqual(self.config.input('ex_linear').input_type.name, 'current_clamp')
         self.assertEqual(self.config.input('ex_linear').module.name, 'linear')
         self.assertEqual(self.config.input('ex_linear').amp_start, 0.15)
diff --git a/tests/test_config.cpp b/tests/test_config.cpp
index f1b2a652..f98494a0 100644
--- a/tests/test_config.cpp
+++ b/tests/test_config.cpp
@@ -320,6 +320,13 @@ TEST_CASE("SimulationConfig") {
 
         CHECK_THROWS_AS(config.getReport("DoesNotExist"), SonataError);
 
+        CHECK(config.listReportNames() == std::set<std::string>{
+              "axonal_comp_centers",
+              "cell_imembrane",
+              "compartment",
+              "soma"
+              });
+
         CHECK(config.getReport("soma").cells == "Mosaic");
         CHECK(config.getReport("soma").type == SimulationConfig::Report::Type::compartment);
         CHECK(config.getReport("soma").compartments == SimulationConfig::Report::Compartments::center);
@@ -343,6 +350,21 @@ TEST_CASE("SimulationConfig") {
         const auto network = fs::absolute(basePath / fs::path("circuit_config.json"));
         CHECK(config.getNetwork() == network.lexically_normal());
 
+        CHECK(config.listInputNames() == std::set<std::string>{
+              "ex_extracellular_stimulation",
+              "ex_hyperpolarizing",
+              "ex_linear",
+              "ex_noise_mean",
+              "ex_noise_meanpercent",
+              "ex_pulse",
+              "ex_rel_linear",
+              "ex_rel_shotnoise",
+              "ex_replay",
+              "ex_seclamp",
+              "ex_shotnoise",
+              "ex_subthreshold"
+              });
+
         CHECK(config.getInput("ex_linear").inputType ==
               SimulationConfig::Input::InputType::current_clamp);
         CHECK(config.getInput("ex_linear").module == SimulationConfig::Input::Module::linear);
@@ -362,7 +384,9 @@ TEST_CASE("SimulationConfig") {
         CHECK(config.getInput("ex_replay").spikeFile ==
               fs::absolute(basePath / fs::path("replay.dat")).lexically_normal());
         CHECK(config.getInput("ex_replay").source == "ML_afferents");
+
     }
+
     SECTION("manifest_network") {
         auto contents = R"({
           "manifest": {