diff --git a/examples/dynamic_algorithm_demo.py b/examples/dynamic_algorithm_demo.py new file mode 100644 index 0000000000..9152d11e73 --- /dev/null +++ b/examples/dynamic_algorithm_demo.py @@ -0,0 +1,45 @@ +import desbordante +import pandas + +TABLE = 'examples/datasets/pfd.csv' + +algo = desbordante.dynamic.algorithms.Default(table=(TABLE, ',', True)) + +def print_result(algo): + result = algo.get_result() + res = [(int((i.split(',')[0].split(' ')[1])), i) for i in result] + res.sort() + for item in res: + print(' ', item[1]) + ins_, del_ = algo.get_result_diff() + if len(ins_): + print(' Added into result:') + for item in ins_: + print(' ', item) + if len(del_): + print(' Removed from result:') + for item in del_: + print(' ', item) + +print('Init result:') +print_result(algo) + +df1 = pandas.DataFrame({'X': [100, 101], 'Y': [100, 101]}) +algo.process(insert=df1) +print('Insert test result:') +print_result(algo) + +algo.process(delete=[3, 4, 12]) +print('Delete test result:') +print_result(algo) + +df2 = pandas.DataFrame({'X': [4], 'Y': [6]}) +algo.process(update_old=[6], update_new=df2) +print('Update test result:') +print_result(algo) + +df3 = pandas.DataFrame({'X': [100, 100, 102], 'Y': [200, 200, 202]}) +df4 = pandas.DataFrame({'X': [102, 105], 'Y': [202, 205]}) +algo.process(insert=df3, update_new=df4, delete=[1, 2, 13], update_old=[5, 7]) +print('Mixed test result:') +print_result(algo) diff --git a/src/core/algorithms/algorithm.cpp b/src/core/algorithms/algorithm.cpp index 326aa3d38f..6a17c45631 100644 --- a/src/core/algorithms/algorithm.cpp +++ b/src/core/algorithms/algorithm.cpp @@ -3,6 +3,7 @@ #include #include "config/exceptions.h" +#include "algorithm.h" namespace algos { @@ -146,4 +147,7 @@ std::string_view Algorithm::GetDescription(std::string_view option_name) const { } } +bool Algorithm::IsOptionSet(std::string_view option_name) const { + return possible_options_.at(option_name)->IsSet(); +} } // namespace algos diff --git a/src/core/algorithms/algorithm.h b/src/core/algorithms/algorithm.h index 29c9ef9a8c..03a2fe9348 100644 --- a/src/core/algorithms/algorithm.h +++ b/src/core/algorithms/algorithm.h @@ -108,6 +108,7 @@ class Algorithm { [[nodiscard]] std::unordered_set GetPossibleOptions() const; [[nodiscard]] std::string_view GetDescription(std::string_view option_name) const; + [[nodiscard]] bool IsOptionSet(std::string_view option_name) const; std::unordered_map GetOptValues() const { std::unordered_map opt_values; diff --git a/src/core/algorithms/dynamic/demo/demo_algo.cpp b/src/core/algorithms/dynamic/demo/demo_algo.cpp new file mode 100644 index 0000000000..60afcacf5a --- /dev/null +++ b/src/core/algorithms/dynamic/demo/demo_algo.cpp @@ -0,0 +1,47 @@ +#include "demo_algo.h" + +#include "config/ioption.h" +#include "config/names.h" + +using config::InputTable; +using config::names::kTable; + +algos::DynamicAlgorithmDemo::DynamicAlgorithmDemo(std::vector phase_names) + : DynamicAlgorithm(phase_names) {} + +unsigned long long algos::DynamicAlgorithmDemo::ProcessBatch() { + auto start_time = std::chrono::system_clock::now(); + added_to_result_.Clear(); + erased_from_result_.Clear(); + for (size_t row_id : delete_statements_) { + erased_from_result_.Add(result_collection_.Erase({row_id})); + } + for (TableRow row : insert_statements_.AsUnorderedMultiset()) { + table_rows_ids_.emplace(row.getId()); + result_collection_.Add(row); + added_to_result_.Add(row); + } + sleep(1); + auto elapsed_milliseconds = std::chrono::duration_cast( + std::chrono::system_clock::now() - start_time); + return elapsed_milliseconds.count(); +} + +void algos::DynamicAlgorithmDemo::LoadDataInternal() { + DynamicAlgorithm::LoadDataInternal(); + while (input_table_->HasNextRow()) { + TableRow row{input_table_->GetNextRow()}; + table_rows_ids_.emplace(row.getId()); + result_collection_.Add(row); + } + sleep(1); +} + +std::vector algos::DynamicAlgorithmDemo::GetResult() { + return result_collection_.AsStringVector(); +} + +std::pair, std::vector> +algos::DynamicAlgorithmDemo::GetResultDiff() { + return std::make_pair(added_to_result_.AsStringVector(), erased_from_result_.AsStringVector()); +} diff --git a/src/core/algorithms/dynamic/demo/demo_algo.h b/src/core/algorithms/dynamic/demo/demo_algo.h new file mode 100644 index 0000000000..d78497a019 --- /dev/null +++ b/src/core/algorithms/dynamic/demo/demo_algo.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include + +#include "algorithms/dynamic/dynamic_algorithm.h" +#include "config/tabular_data/input_table_type.h" +#include "model/table/table_row.h" +#include "util/dynamic_collection.h" + +namespace algos { + +class DynamicAlgorithmDemo : public DynamicAlgorithm { +private: + // algorithm result for the last version of data. + RowsContainer result_collection_; + + // result diff after last processBatch() call + RowsContainer added_to_result_; + RowsContainer erased_from_result_; + + unsigned long long ProcessBatch() final; + void LoadDataInternal() final; + +public: + DynamicAlgorithmDemo(std::vector phase_names = {}); + + std::vector GetResult(); + std::pair, std::vector> GetResultDiff(); +}; + +} // namespace algos \ No newline at end of file diff --git a/src/core/algorithms/dynamic/dynamic_algorithm.cpp b/src/core/algorithms/dynamic/dynamic_algorithm.cpp new file mode 100644 index 0000000000..77b90c0e57 --- /dev/null +++ b/src/core/algorithms/dynamic/dynamic_algorithm.cpp @@ -0,0 +1,118 @@ +#include "dynamic_algorithm.h" + +#include + +#include "config/names_and_descriptions.h" +#include "config/option_using.h" +#include "config/tabular_data/crud_operations/operations.h" +#include "config/tabular_data/input_table/option.h" +#include "config/tabular_data/input_table_type.h" + + +void algos::DynamicAlgorithm::RegisterOptions() { + RegisterOption(config::kTableOpt(&input_table_)); + RegisterOption(config::kInsertStatementsOpt(&insert_batch_)); + RegisterOption(config::kDeleteStatementsOpt(&delete_batch_)); + RegisterOption(config::kUpdateOldStatementsOpt(&update_old_batch_)); + RegisterOption(config::kUpdateNewStatementsOpt(&update_new_batch_)); +} + +algos::DynamicAlgorithm::DynamicAlgorithm(std::vector phase_names) + : Algorithm(phase_names) { + RegisterOptions(); + MakeOptionsAvailable({config::names::kTable}); +} + +void algos::DynamicAlgorithm::LoadDataInternal() { + if (input_table_->GetNumberOfColumns() == 0) { + throw std::runtime_error("Unable to work on an empty dataset."); + } + is_initialized_ = true; +} + +void algos::DynamicAlgorithm::ResetState() { + insert_statements_.Clear(); + delete_statements_.clear(); +} + +void algos::DynamicAlgorithm::MakeExecuteOptsAvailable() { + if (is_initialized_) { + MakeOptionsAvailable(kCrudOptions); + } +} + +void algos::DynamicAlgorithm::AddSpecificNeededOptions( + std::unordered_set& previous_options) const { + for (const std::string_view& option_name : kCrudOptions) { + previous_options.erase(option_name); + } +} + +void algos::DynamicAlgorithm::ValidateInsertStatements(InputTable& data) { + if (!data) { + return; + } + if (data->GetNumberOfColumns() != input_table_->GetNumberOfColumns()) { + throw config::ConfigurationError( + "Invalid data received: the number of columns in the \ + modification statements is different from the table."); + } + while (data->HasNextRow()) { + TableRow row{data->GetNextRow()}; + if (row.getData().size() != input_table_->GetNumberOfColumns()) { + LOG(WARNING) << "Unexpected number of columns for a row, skipping (expected " + << input_table_->GetNumberOfColumns() << ", got " << row.getData().size() + << ")"; + continue; + } + insert_statements_.Add(row); + table_rows_ids_.insert(row.getId()); + } +} + +void algos::DynamicAlgorithm::ValidateDeleteStatements(std::vector& data) { + for (size_t row_id : data) { + if (!table_rows_ids_.count(row_id)) { + throw config::ConfigurationError("Invalid data received: the row with " + + std::to_string(row_id) + + " does not exist in the table."); + } else { + table_rows_ids_.erase(row_id); + delete_statements_.emplace_back(row_id); + } + } + data.clear(); +} + +void algos::DynamicAlgorithm::ConfigureBatch() { + // configure update statements + ValidateDeleteStatements(update_old_batch_); + ValidateInsertStatements(update_new_batch_); + if (insert_statements_.Size() != delete_statements_.size()) { + throw config::ConfigurationError( + "Invalid data received: number of rows to update: " + + std::to_string(insert_statements_.Size()) + + ", number of rows to update with: " + std::to_string(delete_statements_.size())); + } + // configure insert statements + ValidateInsertStatements(insert_batch_); + // configure delete statements + ValidateDeleteStatements(delete_batch_); +} + +bool algos::DynamicAlgorithm::HasBatch() { + bool result = false; + for (const std::string_view& option_name : kCrudOptions) { + result |= IsOptionSet(option_name); + } + return result; +} + +unsigned long long algos::DynamicAlgorithm::ExecuteInternal() { + if (HasBatch()) { + ConfigureBatch(); + unsigned long long time_ms = ProcessBatch(); + return time_ms; + } + return 0; +} diff --git a/src/core/algorithms/dynamic/dynamic_algorithm.h b/src/core/algorithms/dynamic/dynamic_algorithm.h new file mode 100644 index 0000000000..58a57a0039 --- /dev/null +++ b/src/core/algorithms/dynamic/dynamic_algorithm.h @@ -0,0 +1,59 @@ +#pragma once + +#include +#include +#include + +#include "algorithms/algorithm.h" +#include "config/tabular_data/input_table_type.h" +#include "model/table/table_row.h" +#include "util/dynamic_collection.h" + +using RowsContainer = util::DynamicCollection; + +namespace algos { +using config::InputTable; + +class DynamicAlgorithm : public Algorithm { +public: + explicit DynamicAlgorithm(std::vector phase_names); + +protected: + // algo doing its stuff + virtual unsigned long long ProcessBatch() = 0; + void LoadDataInternal() override; + unsigned long long ExecuteInternal() final; + void MakeExecuteOptsAvailable() override; + + void ResetState() override; + + // modify statements in last batch of changes + RowsContainer insert_statements_; + std::vector delete_statements_{}; + + // init state of table + InputTable input_table_; + // insert operations stream + InputTable insert_batch_; + // delete operations stream + std::vector delete_batch_{}; + // update operations stream (old values) + std::vector update_old_batch_{}; + // update operations stream (new values) + InputTable update_new_batch_; + // stores current table rows ids (after last Execute() method call) + std::unordered_set table_rows_ids_{}; + +private: + void AddSpecificNeededOptions( + std::unordered_set& previous_options) const override; + void ValidateInsertStatements(InputTable& data); + void ValidateDeleteStatements(std::vector& data); + void ConfigureBatch(); + void RegisterOptions(); + bool HasBatch(); + + bool is_initialized_ = false; +}; + +} // namespace algos \ No newline at end of file diff --git a/src/core/config/descriptions.h b/src/core/config/descriptions.h index 4e8b85c3ea..bd2098fb98 100644 --- a/src/core/config/descriptions.h +++ b/src/core/config/descriptions.h @@ -102,6 +102,12 @@ constexpr auto kDIgnoreConstantCols = constexpr auto kDGraphData = "Path to dot-file with graph"; constexpr auto kDGfdData = "Path to file with GFD"; constexpr auto kDMemLimitMB = "memory limit im MBs"; +constexpr auto kDInsertStatements = "Rows to be inserted into the table using the insert operation"; +constexpr auto kDDeleteStatements = "Rows to be deleted from the table using the delete operation"; +constexpr auto kDUpdateOldStatements = + "Rows that need to be replaced in the table using the update operation"; +constexpr auto kDUpdateNewStatements = + "Rows that need to be used to replace old data in the table using the update operation"; constexpr auto kDDifferenceTable = "CSV table containing difference limits for each column"; constexpr auto kDNumRows = "Use only first N rows of the table"; constexpr auto kDNUmColumns = "Use only first N columns of the table"; diff --git a/src/core/config/names.h b/src/core/config/names.h index 74f532cc95..0eab3569bd 100644 --- a/src/core/config/names.h +++ b/src/core/config/names.h @@ -56,6 +56,10 @@ constexpr auto kIgnoreConstantCols = "ignore_constant_cols"; constexpr auto kGraphData = "graph"; constexpr auto kGfdData = "gfd"; constexpr auto kMemLimitMB = "mem_limit"; +constexpr auto kInsertStatements = "insert"; +constexpr auto kDeleteStatements = "delete"; +constexpr auto kUpdateOldStatements = "update_old"; +constexpr auto kUpdateNewStatements = "update_new"; constexpr auto kDifferenceTable = "difference_table"; constexpr auto kNumRows = "num_rows"; constexpr auto kNumColumns = "num_columns"; diff --git a/src/core/config/tabular_data/crud_operations/delete/option.cpp b/src/core/config/tabular_data/crud_operations/delete/option.cpp new file mode 100644 index 0000000000..23243961af --- /dev/null +++ b/src/core/config/tabular_data/crud_operations/delete/option.cpp @@ -0,0 +1,9 @@ +#include "config/tabular_data/crud_operations/delete/option.h" + +#include "config/names_and_descriptions.h" + +namespace config { +using names::kDeleteStatements, descriptions::kDDeleteStatements; +extern CommonOption> const kDeleteStatementsOpt = { + kDeleteStatements, kDDeleteStatements, std::nullopt, nullptr, nullptr}; +} // namespace config diff --git a/src/core/config/tabular_data/crud_operations/delete/option.h b/src/core/config/tabular_data/crud_operations/delete/option.h new file mode 100644 index 0000000000..6c6a61b0be --- /dev/null +++ b/src/core/config/tabular_data/crud_operations/delete/option.h @@ -0,0 +1,8 @@ +#pragma once + +#include "config/common_option.h" +#include "config/tabular_data/input_table_type.h" + +namespace config { +extern CommonOption> const kDeleteStatementsOpt; +} // namespace config diff --git a/src/core/config/tabular_data/crud_operations/insert/option.cpp b/src/core/config/tabular_data/crud_operations/insert/option.cpp new file mode 100644 index 0000000000..0b6114e154 --- /dev/null +++ b/src/core/config/tabular_data/crud_operations/insert/option.cpp @@ -0,0 +1,9 @@ +#include "config/tabular_data/crud_operations/insert/option.h" + +#include "config/names_and_descriptions.h" + +namespace config { +using names::kInsertStatements, descriptions::kDInsertStatements; +extern CommonOption const kInsertStatementsOpt = {kInsertStatements, kDInsertStatements, + std::nullopt, nullptr, nullptr}; +} // namespace config diff --git a/src/core/config/tabular_data/crud_operations/insert/option.h b/src/core/config/tabular_data/crud_operations/insert/option.h new file mode 100644 index 0000000000..688ecd40de --- /dev/null +++ b/src/core/config/tabular_data/crud_operations/insert/option.h @@ -0,0 +1,8 @@ +#pragma once + +#include "config/common_option.h" +#include "config/tabular_data/input_table_type.h" + +namespace config { +extern CommonOption const kInsertStatementsOpt; +} // namespace config diff --git a/src/core/config/tabular_data/crud_operations/operations.h b/src/core/config/tabular_data/crud_operations/operations.h new file mode 100644 index 0000000000..64430e4db9 --- /dev/null +++ b/src/core/config/tabular_data/crud_operations/operations.h @@ -0,0 +1,12 @@ +#pragma once + +#include "config/names.h" +#include "delete/option.h" +#include "insert/option.h" +#include "update_new/option.h" +#include "update_old/option.h" + +const std::vector kCrudOptions{ + config::names::kInsertStatements, config::names::kDeleteStatements, + config::names::kUpdateOldStatements, config::names::kUpdateNewStatements}; + diff --git a/src/core/config/tabular_data/crud_operations/update_new/option.cpp b/src/core/config/tabular_data/crud_operations/update_new/option.cpp new file mode 100644 index 0000000000..c7f4c3d9a0 --- /dev/null +++ b/src/core/config/tabular_data/crud_operations/update_new/option.cpp @@ -0,0 +1,9 @@ +#include "config/tabular_data/crud_operations/update_new/option.h" + +#include "config/names_and_descriptions.h" + +namespace config { +using names::kUpdateNewStatements, descriptions::kDUpdateNewStatements; +extern CommonOption const kUpdateNewStatementsOpt = { + kUpdateNewStatements, kDUpdateNewStatements, std::nullopt, nullptr, nullptr}; +} // namespace config diff --git a/src/core/config/tabular_data/crud_operations/update_new/option.h b/src/core/config/tabular_data/crud_operations/update_new/option.h new file mode 100644 index 0000000000..c9c701b799 --- /dev/null +++ b/src/core/config/tabular_data/crud_operations/update_new/option.h @@ -0,0 +1,8 @@ +#pragma once + +#include "config/common_option.h" +#include "config/tabular_data/input_table_type.h" + +namespace config { +extern CommonOption const kUpdateNewStatementsOpt; +} // namespace config diff --git a/src/core/config/tabular_data/crud_operations/update_old/option.cpp b/src/core/config/tabular_data/crud_operations/update_old/option.cpp new file mode 100644 index 0000000000..9445abfaf7 --- /dev/null +++ b/src/core/config/tabular_data/crud_operations/update_old/option.cpp @@ -0,0 +1,9 @@ +#include "config/tabular_data/crud_operations/update_old/option.h" + +#include "config/names_and_descriptions.h" + +namespace config { +using names::kUpdateOldStatements, descriptions::kDUpdateOldStatements; +extern CommonOption> const kUpdateOldStatementsOpt = { + kUpdateOldStatements, kDUpdateOldStatements, std::nullopt, nullptr, nullptr}; +} // namespace config diff --git a/src/core/config/tabular_data/crud_operations/update_old/option.h b/src/core/config/tabular_data/crud_operations/update_old/option.h new file mode 100644 index 0000000000..f27bed3dbe --- /dev/null +++ b/src/core/config/tabular_data/crud_operations/update_old/option.h @@ -0,0 +1,8 @@ +#pragma once + +#include "config/common_option.h" +#include "config/tabular_data/input_table_type.h" + +namespace config { +extern CommonOption> const kUpdateOldStatementsOpt; +} // namespace config diff --git a/src/core/model/table/table_row.h b/src/core/model/table/table_row.h new file mode 100644 index 0000000000..d4adaa1f52 --- /dev/null +++ b/src/core/model/table/table_row.h @@ -0,0 +1,77 @@ +#pragma once + +#include +#include +#include + +#include + +#include "column_index.h" +#include "relational_schema.h" + +class TableRow { +public: + using IndexType = model::ColumnIndex; + +private: + std::vector data_; + size_t id_; + + static int CreateId() { + static int id = 1; + return id++; + } + +public: + TableRow(std::vector&& data) : data_(std::move(data)), id_(CreateId()) {} + TableRow(size_t id) : data_({}), id_(id) {} + TableRow() : data_({}), id_(0){}; + + TableRow(const TableRow& other) : data_(other.data_), id_(other.id_) {} + + TableRow(TableRow&& other) : data_(std::move(other.data_)), id_(other.id_) {} + + TableRow& operator=(const TableRow& other) { + if (this != &other) { + data_ = other.data_; + id_ = other.id_; + } + return *this; + } + + TableRow& operator=(TableRow&& other) { + if (this != &other) { + data_ = std::move(other.data_); + id_ = other.id_; + } + return *this; + } + + const std::vector& getData() const { + return data_; + } + + std::string toString() const { + return "(id: " + std::to_string(id_) + ", data: " + boost::algorithm::join(data_, ",") + + ")"; + } + + explicit operator std::string() const { + return toString(); + } + + size_t getId() const { + return id_; + } + + bool operator==(const TableRow& rhs) const { + return id_ == rhs.getId(); + } +}; + +template <> +struct std::hash { + std::size_t operator()(const TableRow& s) const noexcept { + return s.getId(); + } +}; diff --git a/src/core/util/dynamic_collection.h b/src/core/util/dynamic_collection.h new file mode 100644 index 0000000000..0375a4f0e3 --- /dev/null +++ b/src/core/util/dynamic_collection.h @@ -0,0 +1,73 @@ +#pragma once + +#include +#include +#include + +namespace util { + +// Represents version Collection with thread-safe deletions for dynamic algorithms +template +class DynamicCollection { +private: + std::unordered_multiset collection_{}; + std::mutex mutable mutex_; + +public: + void Add(T&& primitive) { + std::scoped_lock lock(mutex_); + collection_.insert(std::move(primitive)); + } + + template + void Add(Args&&... args) { + std::scoped_lock lock(mutex_); + collection_.emplace(std::forward(args)...); + } + + void Clear() noexcept { + std::scoped_lock lock(mutex_); + collection_.clear(); + } + + bool Contains(const T& primitive) noexcept { + std::scoped_lock lock(mutex_); + auto it = collection_.find(primitive); + return it != collection_.end(); + } + + T Erase(const T& primitive) { + std::scoped_lock lock(mutex_); + auto it = collection_.find(primitive); + T res; + if (it != collection_.end()) { + res = *it; + collection_.erase(it); + return res; + } + return res; + } + + size_t Size() const noexcept { + std::scoped_lock lock(mutex_); + return collection_.size(); + } + + std::vector AsStringVector() const noexcept { + std::vector result_{}; + for (const T& item : collection_) { + result_.emplace_back(std::string(item)); + } + return result_; + } + + const std::unordered_multiset& AsUnorderedMultiset() const noexcept { + return collection_; + } + + std::unordered_multiset& AsUnorderedMultiset() noexcept { + return collection_; + } +}; + +} // namespace util diff --git a/src/python_bindings/bindings.cpp b/src/python_bindings/bindings.cpp index c3c0ac798e..41423643ca 100644 --- a/src/python_bindings/bindings.cpp +++ b/src/python_bindings/bindings.cpp @@ -9,6 +9,7 @@ #include "cfd/bind_cfd.h" #include "data/bind_data_types.h" #include "dd/bind_split.h" +#include "dynamic/bind_dynamic_algorithms.h" #include "fd/bind_fd.h" #include "fd/bind_fd_verification.h" #include "gfd/bind_gfd_verification.h" @@ -36,7 +37,8 @@ PYBIND11_MODULE(desbordante, module) { for (auto bind_func : {BindMainClasses, BindDataTypes, BindFd, BindCfd, BindAr, BindUcc, BindAc, BindOd, BindFdVerification, BindMfdVerification, BindUccVerification, - BindStatistics, BindInd, BindGfdVerification, BindSplit}) { + BindStatistics, BindInd, BindGfdVerification, BindDynamicAlgorithms, + BindSplit}) { bind_func(module); } } diff --git a/src/python_bindings/dynamic/bind_dynamic_algorithms.cpp b/src/python_bindings/dynamic/bind_dynamic_algorithms.cpp new file mode 100644 index 0000000000..6e58deda53 --- /dev/null +++ b/src/python_bindings/dynamic/bind_dynamic_algorithms.cpp @@ -0,0 +1,71 @@ +#include "bind_dynamic_algorithms.h" + +#include +#include + +#include "algorithms/algo_factory.h" +#include "algorithms/dynamic/demo/demo_algo.h" +#include "algorithms/dynamic/dynamic_algorithm.h" +#include "config/names.h" +#include "config/tabular_data/crud_operations/operations.h" +#include "py_util/bind_primitive.h" +#include "py_util/py_to_any.h" + +namespace { +namespace py = pybind11; +using algos::DynamicAlgorithm; +using algos::DynamicAlgorithmDemo; +using python_bindings::PyToAny; +auto const kVoidIndex = std::type_index{typeid(void)}; + +void ConfigureAlgo(DynamicAlgorithm& algorithm, py::kwargs const& kwargs) { + algos::ConfigureFromFunction( + algorithm, [&kwargs, &algorithm](std::string_view option_name) -> boost::any { + std::type_index type_index = algorithm.GetTypeIndex(option_name); + assert(type_index != kVoidIndex); + return kwargs.contains(option_name) + ? PyToAny(option_name, type_index, kwargs[option_name.data()]) + : boost::any{}; + }); +} + +void SetOptionByName(DynamicAlgorithm& algorithm, + std::string_view option_name, + py::kwargs const& kwargs) { + if (kwargs.contains(option_name)) { + std::type_index type_index = algorithm.GetTypeIndex(option_name); + assert(type_index != kVoidIndex); + boost::any option_value = PyToAny(option_name, type_index, kwargs[option_name.data()]); + algorithm.SetOption(option_name, option_value); + } +} +} // namespace + +namespace python_bindings { +void BindDynamicAlgorithms(py::module_& main_module) { + using namespace algos; + + auto dyn_module = main_module.def_submodule("dynamic"); + BindPrimitiveNoBase(dyn_module, "Demo") + .def(py::init([](py::kwargs const& kwargs) { + auto algo = std::make_unique(); + ConfigureAlgo(*algo, kwargs); + algo->LoadData(); + return algo; + })) + .def("process", + [](DynamicAlgorithmDemo& algo, py::kwargs const& kwargs) { + for (const std::string_view& option_name : kCrudOptions) { + SetOptionByName(algo, option_name, kwargs); + } + algo.Execute(); + }, + "Process algorithm with given batch of changes") + .def("get_result", &DynamicAlgorithmDemo::GetResult, + pybind11::return_value_policy::reference_internal) + .def("get_result_diff", &DynamicAlgorithmDemo::GetResultDiff, + pybind11::return_value_policy::reference_internal) + ; + +} +} // namespace python_bindings diff --git a/src/python_bindings/dynamic/bind_dynamic_algorithms.h b/src/python_bindings/dynamic/bind_dynamic_algorithms.h new file mode 100644 index 0000000000..d1a0216c57 --- /dev/null +++ b/src/python_bindings/dynamic/bind_dynamic_algorithms.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +namespace python_bindings { +void BindDynamicAlgorithms(pybind11::module_& main_module); +} // namespace python_bindings diff --git a/src/python_bindings/py_util/py_to_any.cpp b/src/python_bindings/py_util/py_to_any.cpp index d8eb12e923..338dcfb362 100644 --- a/src/python_bindings/py_util/py_to_any.cpp +++ b/src/python_bindings/py_util/py_to_any.cpp @@ -15,8 +15,10 @@ #include "config/exceptions.h" #include "config/tabular_data/input_table_type.h" #include "config/tabular_data/input_tables_type.h" +#include "model/table/table_row.h" #include "parser/csv_parser/csv_parser.h" #include "py_util/create_dataframe_reader.h" +#include "util/dynamic_collection.h" #include "util/enum_to_available_values.h" namespace { @@ -128,6 +130,7 @@ std::unordered_map const kConverters{ {typeid(config::InputTables), InputTablesToAny}, kNormalConvPair, kNormalConvPair>, + kNormalConvPair>, }; } // namespace