diff --git a/plugins/code_generator/README b/plugins/code_generator/README new file mode 100644 index 0000000000..0c37bdf0de --- /dev/null +++ b/plugins/code_generator/README @@ -0,0 +1,36 @@ +# +# Created by: Riccardo Campisano riccardo.campisano AT gmail DOT com +# Original code: https://github.com/pgmodeler/pgmodeler/tree/develop/plugins/xml2object +# +# NOTE: pgmodeler plugins needs 'qttools5-dev' system package +# +### +# Example of pgmodeler compilation (under debian): +# from http://www.campisano.org/wiki/en/Postgresql#Pgmodeler : +### +# sudo su - +# apt-get install g++ pkg-config qt5-qmake qt5-default qtchooser libpq-dev libxml2-dev +# exit +# +# cd $HOME +# git clone https://github.com/campisano/pgmodeler +# cd $HOME/pgmodeler/ +# qmake PREFIX=$HOME/pgmodeler_dev pgmodeler.pro +# make +# make install +# $HOME/pgmodeler_dev/bin/pgmodeler # to run +# +### +# Example of plugin compilation +### +# sudo su - +# apt-get install qttools5-dev +# exit +# cd $HOME/pgmodeler/plugins +# qmake PREFIX=$HOME/pgmodeler_dev plugins.pro +# make +# make install +# cd $HOME +# rm -rf pgmodeler +# $HOME/pgmodeler_dev/bin/pgmodeler +# # open a model and press CTRL+ALT+G to generate the code diff --git a/plugins/code_generator/code_generator.json b/plugins/code_generator/code_generator.json new file mode 100644 index 0000000000..f85bf837ca --- /dev/null +++ b/plugins/code_generator/code_generator.json @@ -0,0 +1 @@ +{ "Keys": [ "code_generator" ] } diff --git a/plugins/code_generator/code_generator.pro b/plugins/code_generator/code_generator.pro new file mode 100644 index 0000000000..8b4e2a1842 --- /dev/null +++ b/plugins/code_generator/code_generator.pro @@ -0,0 +1,66 @@ +# code_generator.pro +# + +# Unix or Windows directory configuration +PGMODELER_SRC_DIR=../../ + +!exists($$PGMODELER_SRC_DIR) { + warning("The pgModeler source code directory '$$PGMODELER_SRC_DIR' could not be found! Make sure the variable PGMODELER_SRC_DIR points to a valid location!") + error("qmake aborted.") +} + +include(../plugins.pro) + +CONFIG += plugin qt uic4 +QT += core gui uitools +TEMPLATE = lib +TARGET = code_generator +OTHER_FILES += code_generator.json +CODECFORTR = UTF8 +DEPENDPATH = ". res src ui moc obj" +MOC_DIR = moc +OBJECTS_DIR = obj +UI_DIR = src + +windows: DESTDIR += $$PWD + +unix|windows: LIBS += -L$$OUT_PWD/../../libpgmodeler_ui/ -lpgmodeler_ui \ + -L$$OUT_PWD/../../libobjrenderer/ -lobjrenderer \ + -L$$OUT_PWD/../../libpgconnector/ -lpgconnector \ + -L$$OUT_PWD/../../libpgmodeler/ -lpgmodeler \ + -L$$OUT_PWD/../../libparsers/ -lparsers \ + -L$$OUT_PWD/../../libutils/ -lutils + +INCLUDEPATH += $$PWD/../../libpgmodeler_ui \ + $$PWD/../../libpgmodeler_ui/src \ + $$PWD/../../libobjrenderer/src \ + $$PWD/../../libpgconnector/src \ + $$PWD/../../libpgmodeler/src \ + $$PWD/../../libparsers/src \ + $$PWD/../../libutils/src + +DEPENDPATH += $$PWD/../../libpgmodeler_ui \ + $$PWD/../../libobjrenderer \ + $$PWD/../../libpgconnector \ + $$PWD/../../libpgmodeler \ + $$PWD/../../libparsers \ + $$PWD/../../libutils + +HEADERS += src/code_generator_plugin.h \ + src/code_generator_widget.h \ + src/base_code_generator.h \ + src/python_daos_generator.h \ + src/base_logger.h + +SOURCES += src/code_generator_plugin.cpp \ + src/code_generator_widget.cpp \ + src/python_daos_generator.cpp + +FORMS += ui/code_generator_widget.ui + +target.path = $$PLUGINSDIR/$$TARGET + +resources.path = $$PLUGINSDIR/$$TARGET +resources.files += res/code_generator.png lang code_generator.json + +INSTALLS += target resources diff --git a/plugins/code_generator/res/code_generator.png b/plugins/code_generator/res/code_generator.png new file mode 100644 index 0000000000..3108548f43 Binary files /dev/null and b/plugins/code_generator/res/code_generator.png differ diff --git a/plugins/code_generator/src/base_code_generator.h b/plugins/code_generator/src/base_code_generator.h new file mode 100644 index 0000000000..85ecb92f20 --- /dev/null +++ b/plugins/code_generator/src/base_code_generator.h @@ -0,0 +1,15 @@ +#ifndef BASE_CODE_GENERATOR_H +#define BASE_CODE_GENERATOR_H + +#include "base_logger.h" +#include "pgmodelerplugin.h" +#include +#include + +class BaseCodeGenerator { + + public: + virtual void generateCode(DatabaseModel &model, BaseLogger &logger, std::map< std::string, std::string > &files_to_generate) = 0; +}; + +#endif diff --git a/plugins/code_generator/src/base_logger.h b/plugins/code_generator/src/base_logger.h new file mode 100644 index 0000000000..8f7970c474 --- /dev/null +++ b/plugins/code_generator/src/base_logger.h @@ -0,0 +1,12 @@ +#ifndef BASE_LOGGER_H +#define BASE_LOGGER_H + +#include + +class BaseLogger { + + public: + virtual void log(std::string text) = 0; +}; + +#endif diff --git a/plugins/code_generator/src/code_generator_plugin.cpp b/plugins/code_generator/src/code_generator_plugin.cpp new file mode 100644 index 0000000000..f69704d39b --- /dev/null +++ b/plugins/code_generator/src/code_generator_plugin.cpp @@ -0,0 +1,68 @@ +#include "code_generator_plugin.h" +#include "code_generator_widget.h" +#include "exception.h" +#include "messagebox.h" +#include "python_daos_generator.h" + +CodeGeneratorPlugin::CodeGeneratorPlugin(void) +{ + this->widget = new CodeGeneratorWidget(); + this->generator = new PythonDAOsGenerator(); + ((CodeGeneratorWidget*)this->widget)->setGenerator((BaseCodeGenerator *) this->generator); + + configurePluginInfo( + getPluginTitle(), + getPluginVersion(), + getPluginAuthor(), + getPluginDescription(), + + GlobalAttributes::PLUGINS_DIR + + GlobalAttributes::DIR_SEPARATOR + + QString("code_generator") + + GlobalAttributes::DIR_SEPARATOR + QString("code_generator.png") + ); +} + +CodeGeneratorPlugin::~CodeGeneratorPlugin(void) +{ + delete ((CodeGeneratorWidget*)this->widget); + this->widget = 0; +} + +QString CodeGeneratorPlugin::getPluginTitle(void) +{ + return(trUtf8("CodeGenerator")); +} + +QString CodeGeneratorPlugin::getPluginVersion(void) +{ + return(QString("0.1")); +} + +QString CodeGeneratorPlugin::getPluginAuthor(void) +{ + return(QString("Riccardo Campisano")); +} + +QString CodeGeneratorPlugin::getPluginDescription(void) +{ + return(trUtf8("This plugin automatically gerate code.")); +} + +void CodeGeneratorPlugin::showPluginInfo(void) +{ + plugin_info_frm->show(); +} + +void CodeGeneratorPlugin::executePlugin(ModelWidget *model) +{ + if(!model) + throw Exception(trUtf8("This plugin must be executed with at least one model opened!"),ERR_CUSTOM,__PRETTY_FUNCTION__,__FILE__,__LINE__); + + ((CodeGeneratorWidget*)this->widget)->show(model->getDatabaseModel(), model->getOperationList()); +} + +QKeySequence CodeGeneratorPlugin::getPluginShortcut(void) +{ + return(QKeySequence(QString("Ctrl+Alt+G"))); +} diff --git a/plugins/code_generator/src/code_generator_plugin.h b/plugins/code_generator/src/code_generator_plugin.h new file mode 100644 index 0000000000..5167adad02 --- /dev/null +++ b/plugins/code_generator/src/code_generator_plugin.h @@ -0,0 +1,39 @@ +/** +\ingroup code_generator_plugin +\class CodeGeneratorPlugin +\brief Code generator plugin for pgModeler +*/ + +#ifndef CODE_GENERATOR_PLUGINH +#define CODE_GENERATOR_PLUGIN_H + +#include "pgmodelerplugin.h" + +class CodeGeneratorPlugin: public QObject, public PgModelerPlugin { + private: + Q_OBJECT + + Q_PLUGIN_METADATA(IID "br.com.pgmodeler.PgModelerPlugin" FILE "code_generator.json") + + //! \brief Declares the interface which is used to implement the plugin + Q_INTERFACES(PgModelerPlugin) + + void *widget; + void *generator; + + public: + CodeGeneratorPlugin(void); + ~CodeGeneratorPlugin(void); + + QString getPluginTitle(void); + QString getPluginVersion(void); + QString getPluginAuthor(void); + QString getPluginDescription(void); + QKeySequence getPluginShortcut(void); + void executePlugin(ModelWidget *); + + public slots: + void showPluginInfo(void); +}; + +#endif diff --git a/plugins/code_generator/src/code_generator_widget.cpp b/plugins/code_generator/src/code_generator_widget.cpp new file mode 100644 index 0000000000..915ef83ea2 --- /dev/null +++ b/plugins/code_generator/src/code_generator_widget.cpp @@ -0,0 +1,76 @@ +#include "code_generator_widget.h" + +#include +#include +#include +#include + +CodeGeneratorWidget::CodeGeneratorWidget(QWidget *parent, Qt::WindowFlags f) : QDialog(parent,f) +{ + setupUi(this); + + code_hl=new SyntaxHighlighter(code_txt); + code_hl->loadConfiguration(GlobalAttributes::XML_HIGHLIGHT_CONF_PATH); + + connect(close_btn, SIGNAL(clicked(void)), this, SLOT(close(void))); + connect(clear_btn, SIGNAL(clicked(void)), this, SLOT(doClearCode(void))); + connect(generate_btn, SIGNAL(clicked(void)), this, SLOT(doGenerateCode(void))); + + this->generator = 0; +} + +void CodeGeneratorWidget::setGenerator(BaseCodeGenerator *generator) +{ + this->generator = generator; +} + +void CodeGeneratorWidget::show(DatabaseModel *model, OperationList *op_list) +{ + doClearCode(); + this->setEnabled(model!=nullptr && op_list!=nullptr); + this->op_list = op_list; + this->model = model; + QDialog::show(); +} + +void CodeGeneratorWidget::doClearCode(void) +{ + code_txt->setPlainText(QString("Ready..")); +} + +void CodeGeneratorWidget::doGenerateCode(void) +{ + std::map< std::string, std::string > files_to_generate; + std::map< std::string, std::string >::iterator it; + + try + { + if(!op_list->isOperationChainStarted()) + op_list->startOperationChain(); + + this->generator->generateCode(*model, *this, files_to_generate); + + for(it = files_to_generate.begin(); it != files_to_generate.end(); ++it) + { + std::ofstream out_file("/tmp/" + it->first); + out_file << it->second; + out_file.close(); + } + + op_list->finishOperationChain(); + } + catch(Exception &e) + { + if(op_list->isOperationChainStarted()) + op_list->finishOperationChain(); + + op_list->undoOperation(); + op_list->removeLastOperation(); + throw Exception(e.getErrorMessage(), e.getErrorType(),__PRETTY_FUNCTION__,__FILE__,__LINE__, &e); + } +} + +void CodeGeneratorWidget::log(std::string text) +{ + code_txt->setPlainText(QString::fromStdString(text)); +} diff --git a/plugins/code_generator/src/code_generator_widget.h b/plugins/code_generator/src/code_generator_widget.h new file mode 100644 index 0000000000..35fa2c65d2 --- /dev/null +++ b/plugins/code_generator/src/code_generator_widget.h @@ -0,0 +1,33 @@ +#ifndef CODE_GENERATOR_WIDGET_H +#define CODE_GENERATOR_WIDGET_H + +#include +#include "ui_code_generator_widget.h" +#include "syntaxhighlighter.h" +#include "databasemodel.h" +#include "operationlist.h" +#include "base_code_generator.h" +#include "base_logger.h" + +class CodeGeneratorWidget: public QDialog, public Ui::CodeGeneratorWidget, public BaseLogger { + private: + Q_OBJECT + SyntaxHighlighter *code_hl; + DatabaseModel *model; + OperationList *op_list; + BaseCodeGenerator *generator; + + public: + CodeGeneratorWidget(QWidget *parent = 0, Qt::WindowFlags f = 0); + void setGenerator(BaseCodeGenerator *generator); + void log(std::string text); + + public slots: + void show(DatabaseModel *model, OperationList *op_list); + + private slots: + void doClearCode(void); + void doGenerateCode(void); +}; + +#endif diff --git a/plugins/code_generator/src/python_daos_generator.cpp b/plugins/code_generator/src/python_daos_generator.cpp new file mode 100644 index 0000000000..9e64ecd054 --- /dev/null +++ b/plugins/code_generator/src/python_daos_generator.cpp @@ -0,0 +1,692 @@ +#include "python_daos_generator.h" +#include "pgmodelerplugin.h" +#include + +void PythonDAOsGenerator::generateCode(DatabaseModel &model, BaseLogger &logger, std::map< std::string, std::string > &files_to_generate) +{ + // loop all tables + std::vector< BaseObject * > * tables = model.getObjectList(OBJ_TABLE); + std::vector< BaseObject * >::iterator it; + std::string schema_name; + std::string table_name; + std::string table_code; + + std::stringstream text; + + for(it = tables->begin(); it != tables->end(); ++it) + { + schema_name = (*it)->getSchema()->getName().toUtf8().constData(); + table_name = (*it)->getName().toUtf8().constData(); + table_code = this->generateCompleteTableCode(*(Table *)(* it)); + files_to_generate[schema_name + "_" + table_name + ".py"] = table_code; + text << table_code; + } + + logger.log(text.str()); +} + +// Starting code generation +std::string PythonDAOsGenerator::generateCompleteTableCode(Table &table) +{ + std::stringstream text; + + // Header: + this->generateHeader(text, table); + + // Insert + this->generateInsertAutoPK(text, table); + this->generateInsertAll(text, table); + + // Update + this->generateUpdateFromPK(text, table); + + // Delete + this->generateDeleteFromPK(text, table); + + // Select + this->generateSelectFromPK(text, table); + + // Select + this->generateSelectFromUKs(text, table); + + // end newline + text << '\n'; + + return text.str(); +} + +void PythonDAOsGenerator::generateHeader(std::stringstream &text, Table &table) +{ + // schema and table name + std::string schema_name = table.getSchema()->getName().toUtf8().constData(); + std::string table_name = table.getName().toUtf8().constData(); + + // DAO class name + std::string dao_class_name = this->getDAONameFromSchemaTableName(schema_name, table_name); + + text << "#!/usr/bin/env python"; + text << "\n# -*- coding: utf-8 -*-"; + text << "\n\"\"\""; + text << "\n@package " << dao_class_name << " Data Access Object"; + text << "\n for '" << schema_name << '.' << table_name << "' table"; + text << "\n\"\"\""; + text << '\n'; + text << '\n'; + text << "\nclass " << dao_class_name << "():"; + text << '\n'; + text << "\n def __init__(self, database):"; + text << "\n self.db = database"; +} + +void PythonDAOsGenerator::generateInsertAutoPK(std::stringstream &text, Table &table) +{ + this->generateInsert(text, table, false); +} + +void PythonDAOsGenerator::generateInsertAll(std::stringstream &text, Table &table) +{ + this->generateInsert(text, table, true); +} + +void PythonDAOsGenerator::generateInsert(std::stringstream &text, Table &table, bool all) +{ + // schema and table name + std::string schema_name = table.getSchema()->getName().toUtf8().constData(); + std::string table_name = table.getName().toUtf8().constData(); + std::string schema_table_name = schema_name + '.' + table_name; + + // get table columns and iterator to loop under all table columns + std::vector< Column * > table_columns = this->getTableColumns(table); + std::vector< Column * >::iterator table_columns_it; + + // get primary key columns and iterator + std::vector< Column * > pk_columns = this->getTablePrimaryKeyColumns(table); + std::vector< Column * >::iterator pk_columns_it; + + std::string method_name = "insert"; + std::vector< Column * > pk_auto_columns; + std::vector< Column * >::iterator pk_auto_columns_it; + + if(all) + { + method_name += "All"; + } + else + { + // get primary key columns that have a sequence + pk_auto_columns = this->getTablePrimaryKeyAutoColumns(table); + } + + // in the case that there are no columns to insert + if(table_columns.size() - pk_auto_columns.size() <= 0) + { + return; + } + + // auxiliary vars + Column *column; + std::string col_name; + bool is_auto_pk; + bool is_first_column_writed; + + // funcion definition and parameters + text << '\n'; + text << "\n def " << method_name << "("; + text << "\n self"; + is_first_column_writed = true; // already write self above + for(table_columns_it = table_columns.begin(); table_columns_it != table_columns.end(); ++table_columns_it) + { + is_auto_pk = std::find(pk_auto_columns.begin(), pk_auto_columns.end(), *table_columns_it) != pk_auto_columns.end(); + if(! is_auto_pk) + { + column = *table_columns_it; + col_name = column->getName().toUtf8().constData(); + if(!is_first_column_writed) + { + text << '\n'; + is_first_column_writed = true; + } + else + { + text << ",\n"; + } + text << " " << col_name; + } + } + text << "\n ):"; + + // commentary + text << "\n \"\"\" Insert a '" << schema_table_name << "' record"; + text << "\n and get back the DB generated ID \"\"\""; + text << '\n'; + + // query arguments + text << "\n args = {"; + is_first_column_writed = false; + for(table_columns_it = table_columns.begin(); table_columns_it != table_columns.end(); ++table_columns_it) + { + is_auto_pk = std::find(pk_auto_columns.begin(), pk_auto_columns.end(), *table_columns_it) != pk_auto_columns.end(); + if(! is_auto_pk) + { + column = *table_columns_it; + col_name = column->getName().toUtf8().constData(); + if(!is_first_column_writed) + { + text << '\n'; + is_first_column_writed = true; + } + else + { + text << ",\n"; + } + text << " \"" << col_name << "\": " << col_name; + } + } + text << "\n }"; + text << '\n'; + + //TODO [CMP] put \n at the start of the line + + // sql query + text << "\n sql = ("; + text << "\n \"INSERT INTO " << schema_table_name << " ("; + is_first_column_writed = false; + for(table_columns_it = table_columns.begin(); table_columns_it != table_columns.end(); ++table_columns_it) + { + is_auto_pk = std::find(pk_auto_columns.begin(), pk_auto_columns.end(), *table_columns_it) != pk_auto_columns.end(); + if(! is_auto_pk) + { + column = *table_columns_it; + col_name = column->getName().toUtf8().constData(); + if(!is_first_column_writed) + { + text << "\"\n"; + is_first_column_writed = true; + } + else + { + text << ", \"\n"; + } + text << " \"" << col_name; + } + } + text << '\"'; + text << "\n \") VALUES ("; + is_first_column_writed = false; + for(table_columns_it = table_columns.begin(); table_columns_it != table_columns.end(); ++table_columns_it) + { + is_auto_pk = std::find(pk_auto_columns.begin(), pk_auto_columns.end(), *table_columns_it) != pk_auto_columns.end(); + if(! is_auto_pk) + { + column = *table_columns_it; + col_name = column->getName().toUtf8().constData(); + if(!is_first_column_writed) + { + text << "\"\n"; + is_first_column_writed = true; + } + else + { + text << ", \"\n"; + } + text << " \"%(" << col_name << ")s"; + } + } + text << '\"'; + text << "\n \")"; + if(!pk_columns.empty()) + { + text << " RETURNING "; + for(pk_columns_it = pk_columns.begin(); pk_columns_it != pk_columns.end(); ++pk_columns_it) + { + column = *pk_columns_it; + col_name = column->getName().toUtf8().constData(); + text << col_name; + if(std::next(pk_columns_it, 1) != pk_columns.end()) text << ", "; + } + } + text << '\"'; + text << "\n )"; + text << '\n'; + // execution + text << "\n try:"; + text << "\n result = self.db.execute(sql, args)"; + if(!pk_columns.empty()) + { + text << "\n return dict(result.fetchone())"; + } + else + { + text << "\n result.close()"; + text << "\n return dict()"; + } + text << "\n except Exception, error:"; + text << "\n self.db.logger.error(error)"; + text << "\n raise"; +} + +void PythonDAOsGenerator::generateUpdateFromPK(std::stringstream &text, Table &table) +{ + // schema and table name + std::string schema_name = table.getSchema()->getName().toUtf8().constData(); + std::string table_name = table.getName().toUtf8().constData(); + std::string schema_table_name = schema_name + '.' + table_name; + + // get table columns and iterator to loop under all table columns + std::vector< Column * > table_columns = this->getTableColumns(table); + std::vector< Column * >::iterator table_columns_it; + + // get primary key columns and iterator + std::vector< Column * > pk_columns = this->getTablePrimaryKeyColumns(table); + std::vector< Column * >::iterator pk_columns_it; + + if(pk_columns.empty()) + { + return; // will be no update from pk + } + + std::string pk_names = this->getKeyNamesFromColumns(pk_columns); + + // auxiliary vars + Column *column; + std::string col_name; + bool is_pk; + + // funcion definition and parameters + text << '\n'; + text << "\n def update_by_" << pk_names << "("; + text << "\n self,"; + // pk first + for(pk_columns_it = pk_columns.begin(); pk_columns_it != pk_columns.end(); ++pk_columns_it) + { + column = *pk_columns_it; + col_name = column->getName().toUtf8().constData(); + text << "\n " << col_name; + if(!table_columns.empty() || std::next(pk_columns_it, 1) != pk_columns.end()) text << ','; + } + // then the rest + for(table_columns_it = table_columns.begin(); table_columns_it != table_columns.end(); ++table_columns_it) + { + is_pk = std::find(pk_columns.begin(), pk_columns.end(), *table_columns_it) != pk_columns.end(); + if(! is_pk) + { + column = *table_columns_it; + col_name = column->getName().toUtf8().constData(); + text << "\n " << col_name; + if(std::next(table_columns_it, 1) != table_columns.end()) text << ','; + } + } + text << "\n ):"; + + // commentary + text << "\n \"\"\" Update a '" << schema_table_name << "' record"; + text << "\n using primary key \"\"\""; + text << '\n'; + + // query arguments + text << "\n args = {"; + for(table_columns_it = table_columns.begin(); table_columns_it != table_columns.end(); ++table_columns_it) + { + column = *table_columns_it; + col_name = column->getName().toUtf8().constData(); + text << "\n \"" << col_name << "\": " << col_name; + if(std::next(table_columns_it, 1) != table_columns.end()) text << ','; + } + text << "\n }"; + text << '\n'; + + // sql query + text << "\n sql = ("; + text << "\n \"UPDATE " << schema_table_name << " SET \""; + for(table_columns_it = table_columns.begin(); table_columns_it != table_columns.end(); ++table_columns_it) + { + column = *table_columns_it; + col_name = column->getName().toUtf8().constData(); + text << "\n \"" << col_name << " = %(" << col_name << ")s"; + if(std::next(table_columns_it, 1) != table_columns.end()) text << ", "; + text << '\"'; + } + text << "\n \" WHERE \""; + for(pk_columns_it = pk_columns.begin(); pk_columns_it != pk_columns.end(); ++pk_columns_it) + { + column = *pk_columns_it; + col_name = column->getName().toUtf8().constData(); + text << "\n \"" << col_name << " = %(" << col_name << ")s"; + if(std::next(pk_columns_it, 1) != pk_columns.end()) text << " AND "; + text << '\"'; + } + text << "\n )"; + text << '\n'; + // execution + text << "\n try:"; + text << "\n result = self.db.execute(sql, args)"; + text << "\n result.close()"; + text << "\n return result.rowcount"; + text << "\n except Exception, error:"; + text << "\n self.db.logger.error(error)"; + text << "\n raise"; +} + +void PythonDAOsGenerator::generateDeleteFromPK(std::stringstream &text, Table &table) +{ + // schema and table name + std::string schema_name = table.getSchema()->getName().toUtf8().constData(); + std::string table_name = table.getName().toUtf8().constData(); + std::string schema_table_name = schema_name + '.' + table_name; + + // get primary key columns and iterator + std::vector< Column * > pk_columns = this->getTablePrimaryKeyColumns(table); + std::vector< Column * >::iterator pk_columns_it; + + if(pk_columns.empty()) + { + return; // will be no delete from pk + } + + std::string pk_names = this->getKeyNamesFromColumns(pk_columns); + + // auxiliary vars + Column *column; + std::string col_name; + + // funcion definition and parameters + text << '\n'; + text << "\n def delete_by_" << pk_names << "("; + text << "\n self,"; + // pk first + for(pk_columns_it = pk_columns.begin(); pk_columns_it != pk_columns.end(); ++pk_columns_it) + { + column = *pk_columns_it; + col_name = column->getName().toUtf8().constData(); + text << "\n " << col_name; + if(std::next(pk_columns_it, 1) != pk_columns.end()) text << ','; + } + text << "\n ):"; + + // commentary + text << "\n \"\"\" Delete a '" << schema_table_name << "' record"; + text << "\n using primary key \"\"\""; + text << '\n'; + + // query arguments + text << "\n args = {"; + for(pk_columns_it = pk_columns.begin(); pk_columns_it != pk_columns.end(); ++pk_columns_it) + { + column = *pk_columns_it; + col_name = column->getName().toUtf8().constData(); + text << "\n \"" << col_name << "\": " << col_name; + if(std::next(pk_columns_it, 1) != pk_columns.end()) text << ','; + } + text << "\n }"; + text << '\n'; + + // sql query + text << "\n sql = ("; + text << "\n \"DELETE FROM " << schema_table_name << " WHERE \""; + for(pk_columns_it = pk_columns.begin(); pk_columns_it != pk_columns.end(); ++pk_columns_it) + { + column = *pk_columns_it; + col_name = column->getName().toUtf8().constData(); + text << "\n \"" << col_name << " = %(" << col_name << ")s"; + if(std::next(pk_columns_it, 1) != pk_columns.end()) text << " AND "; + text << '\"'; + } + text << "\n )"; + text << '\n'; + // execution + text << "\n try:"; + text << "\n result = self.db.execute(sql, args)"; + text << "\n result.close()"; + text << "\n return result.rowcount"; + text << "\n except Exception, error:"; + text << "\n self.db.logger.error(error)"; + text << "\n raise"; +} + +void PythonDAOsGenerator::generateSelectFromPK(std::stringstream &text, Table &table) +{ + // schema and table name + std::string schema_name = table.getSchema()->getName().toUtf8().constData(); + std::string table_name = table.getName().toUtf8().constData(); + std::string schema_table_name = schema_name + '.' + table_name; + + // get table columns and iterator to loop under all table columns + std::vector< Column * > table_columns = this->getTableColumns(table); + + // get primary key columns and iterator + std::vector< Column * > pk_columns = this->getTablePrimaryKeyColumns(table); + + // generate for pk + generateSelectFromConstraintColumns(text, schema_table_name, table_columns, pk_columns); +} + +void PythonDAOsGenerator::generateSelectFromUKs(std::stringstream &text, Table &table) +{ + // schema and table name + std::string schema_name = table.getSchema()->getName().toUtf8().constData(); + std::string table_name = table.getName().toUtf8().constData(); + std::string schema_table_name = schema_name + '.' + table_name; + + // get table columns and iterator to loop under all table columns + std::vector< Column * > table_columns = this->getTableColumns(table); + + // get all unique keys + std::vector< std::vector< Column * > > uk_keys = getTableUniqueKeyColumns(table); + std::vector< std::vector< Column * > >::iterator uk_keys_it; + + // generate for all uk + for(uk_keys_it = uk_keys.begin(); uk_keys_it != uk_keys.end(); ++uk_keys_it) + { + generateSelectFromConstraintColumns(text, schema_table_name, table_columns, *uk_keys_it); + } +} + +void PythonDAOsGenerator::generateSelectFromConstraintColumns(std::stringstream &text, std::string &schema_table_name, std::vector< Column * > &table_columns, std::vector< Column * > &constr_columns) +{ + if(constr_columns.empty()) + { + return; // will be no select from this primary/unique key + } + + std::vector< Column * >::iterator table_columns_it; + std::vector< Column * >::iterator constr_columns_it; + std::string constr_names = this->getKeyNamesFromColumns(constr_columns); + + // auxiliary vars + Column *column; + std::string col_name; + + // funcion definition and parameters + text << '\n'; + text << "\n def select_by_" << constr_names << "("; + text << "\n self,"; + for(constr_columns_it = constr_columns.begin(); constr_columns_it != constr_columns.end(); ++constr_columns_it) + { + column = *constr_columns_it; + col_name = column->getName().toUtf8().constData(); + text << "\n " << col_name; + if(std::next(constr_columns_it, 1) != constr_columns.end()) text << ','; + } + text << "\n ):"; + + // commentary + text << "\n \"\"\" Retreive a '" << schema_table_name << "' record"; + text << "\n using primary or unique key \"\"\""; + text << '\n'; + + // query arguments + text << "\n args = {"; + for(constr_columns_it = constr_columns.begin(); constr_columns_it != constr_columns.end(); ++constr_columns_it) + { + column = *constr_columns_it; + col_name = column->getName().toUtf8().constData(); + text << "\n \"" << col_name << "\": " << col_name; + if(std::next(constr_columns_it, 1) != constr_columns.end()) text << ','; + } + text << "\n }"; + text << '\n'; + + // sql query + text << "\n sql = ("; + text << "\n \"SELECT \""; + for(table_columns_it = table_columns.begin(); table_columns_it != table_columns.end(); ++table_columns_it) + { + column = *table_columns_it; + col_name = column->getName().toUtf8().constData(); + text << "\n \"" << col_name; + if(std::next(table_columns_it, 1) != table_columns.end()) text << ", "; + text << '\"'; + } + text << "\n \" FROM " << schema_table_name << " WHERE \""; + for(constr_columns_it = constr_columns.begin(); constr_columns_it != constr_columns.end(); ++constr_columns_it) + { + column = *constr_columns_it; + col_name = column->getName().toUtf8().constData(); + text << "\n \"" << col_name << " = %(" << col_name << ")s"; + if(std::next(constr_columns_it, 1) != constr_columns.end()) text << " AND "; + text << '\"'; + } + text << "\n )"; + text << '\n'; + // execution + text << "\n try:"; + text << "\n results = self.db.connection.execute(sql, args)"; + text << "\n if results.rowcount > 1:"; + text << "\n raise RuntimeError("; + text << "\n \"ERROR: Select by primary/unique key\""; + text << "\n \"returned multiple rows!\")"; + text << "\n if results.rowcount == 1:"; + text << "\n row = results.fetchone()"; + text << "\n result = dict(zip(row.keys(), row.values()))"; + text << "\n else:"; + text << "\n result = None"; + text << "\n results.close()"; + text << "\n return result"; + text << "\n except Exception, error:"; + text << "\n self.db.logger.error(error)"; + text << "\n raise"; +} + +std::string PythonDAOsGenerator::getDAONameFromSchemaTableName(std::string &schema_name, std::string &table_name) +{ + stringstream in(schema_name + '_' + table_name); + stringstream out; + + while(!in.eof()) + { + string x; + std::getline(in, x, '_' ); + + if(!x.empty()) + { + x[0] = std::toupper(x[0]); + } + + out << x; + } + + return out.str(); +} + +std::vector< Column * > PythonDAOsGenerator::getTableColumns(Table &table) +{ + std::vector< Column * > table_columns; + std::vector< TableObject * > *table_objects = table.getObjectList(OBJ_COLUMN); + std::vector< TableObject * >::iterator table_objects_it; + + for(table_objects_it = table_objects->begin(); table_objects_it != table_objects->end(); ++table_objects_it) + { + table_columns.push_back((Column *)(*table_objects_it)); + } + + return table_columns; +} + +std::vector< Column * > PythonDAOsGenerator::getTablePrimaryKeyColumns(Table &table) +{ + std::vector< Column * > pk_columns; + Constraint *primary_key = table.getPrimaryKey(); + + if(primary_key) + { + unsigned pk_num_columns = primary_key->getColumnCount(Constraint::SOURCE_COLS); + + for(unsigned i = 0; igetColumn(i, Constraint::SOURCE_COLS)); + } + } + + return pk_columns; +} + +std::vector< Column * > PythonDAOsGenerator::getTablePrimaryKeyAutoColumns(Table &table) +{ + std::vector< Column * > pk_columns; + Constraint *primary_key = table.getPrimaryKey(); + Column *column; + + if(primary_key) + { + unsigned pk_num_columns = primary_key->getColumnCount(Constraint::SOURCE_COLS); + + for(unsigned i = 0; igetColumn(i, Constraint::SOURCE_COLS); + + if(column->getSequence() == nullptr) + { + pk_columns.push_back(column); + } + } + } + + return pk_columns; +} + +std::vector< std::vector< Column * > > PythonDAOsGenerator::getTableUniqueKeyColumns(Table &table) +{ + std::vector< std::vector< Column * > > uk_columns; + + std::vector< TableObject * > *table_objects = table.getObjectList(OBJ_CONSTRAINT); + std::vector< TableObject * >::iterator table_objects_it; + + Constraint* unique_key; + unsigned uk_num_columns; + + for(table_objects_it = table_objects->begin(); table_objects_it != table_objects->end(); ++table_objects_it) + { + unique_key = (Constraint *)(*table_objects_it); + + if(unique_key->getConstraintType() == ConstraintType::unique) + { + uk_num_columns = unique_key->getColumnCount(Constraint::SOURCE_COLS); + + if(uk_num_columns > 0) + { + std::vector< Column * > columns; + for(unsigned i = 0; igetColumn(i, Constraint::SOURCE_COLS)); + } + uk_columns.push_back(columns); + } + } + } + + return uk_columns; +} + +std::string PythonDAOsGenerator::getKeyNamesFromColumns(std::vector< Column * > &pk_columns) +{ + std::string pk_names; + std::vector< Column * >::iterator pk_columns_it; + + for(pk_columns_it = pk_columns.begin(); pk_columns_it != pk_columns.end(); ++pk_columns_it) + { + pk_names += (*pk_columns_it)->getName().toUtf8().constData(); + if(std::next(pk_columns_it, 1) != pk_columns.end()) pk_names += "_"; + } + + return pk_names; +} diff --git a/plugins/code_generator/src/python_daos_generator.h b/plugins/code_generator/src/python_daos_generator.h new file mode 100644 index 0000000000..2a3d36728b --- /dev/null +++ b/plugins/code_generator/src/python_daos_generator.h @@ -0,0 +1,35 @@ +#ifndef PYTHON_DAOS_GENERATOR_H +#define PYTHON_DAOS_GENERATOR_H + +#include "base_code_generator.h" +#include +#include +#include + +class PythonDAOsGenerator: public BaseCodeGenerator { + + public: + void generateCode(DatabaseModel &model, BaseLogger &logger, std::map< std::string, std::string > &files_to_generate); + + private: + std::string generateCompleteTableCode(Table &table); + + void generateHeader(std::stringstream &text, Table &table); + void generateInsertAutoPK(std::stringstream &text, Table &table); + void generateInsertAll(std::stringstream &text, Table &table); + void generateUpdateFromPK(std::stringstream &text, Table &table); + void generateDeleteFromPK(std::stringstream &text, Table &table); + void generateSelectFromPK(std::stringstream &text, Table &table); + void generateSelectFromUKs(std::stringstream &text, Table &table); + + void generateInsert(std::stringstream &text, Table &table, bool all); + void generateSelectFromConstraintColumns(std::stringstream &text, std::string &schema_table_name, std::vector< Column * > &table_columns, std::vector< Column * > &constr_columns); + std::string getDAONameFromSchemaTableName(std::string &schema_name, std::string &table_name); + std::vector< Column * > getTableColumns(Table &table); + std::vector< Column * > getTablePrimaryKeyColumns(Table &table); + std::vector< Column * > getTablePrimaryKeyAutoColumns(Table &table); + std::vector< std::vector< Column * > > getTableUniqueKeyColumns(Table &table); + std::string getKeyNamesFromColumns(std::vector< Column * > &pk_columns); +}; + +#endif diff --git a/plugins/code_generator/ui/code_generator_widget.ui b/plugins/code_generator/ui/code_generator_widget.ui new file mode 100644 index 0000000000..d38ded7a93 --- /dev/null +++ b/plugins/code_generator/ui/code_generator_widget.ui @@ -0,0 +1,139 @@ + + + CodeGeneratorWidget + + + + 0 + 0 + 628 + 557 + + + + CodeGenerator + + + true + + + + 2 + + + 2 + + + 2 + + + 2 + + + 6 + + + + + Qt::Horizontal + + + + 262 + 20 + + + + + + + + + + + 30 + 0 + + + + Clear + + + + :/icones/icones/limpartexto.png:/icones/icones/limpartexto.png + + + Qt::ToolButtonTextBesideIcon + + + + + + + + 30 + 0 + + + + Generate + + + + :/icones/icones/novoobjeto.png:/icones/icones/novoobjeto.png + + + + 16 + 16 + + + + Qt::ToolButtonTextBesideIcon + + + + + + + + 30 + 0 + + + + Close + + + + :/icones/icones/fechar1.png:/icones/icones/fechar1.png + + + + 16 + 16 + + + + Qt::ToolButtonTextBesideIcon + + + + + + + + + + DejaVu Sans Mono + + + + + + + + + + + diff --git a/plugins/plugins.pro b/plugins/plugins.pro index a408ae956a..0049418fb9 100644 --- a/plugins/plugins.pro +++ b/plugins/plugins.pro @@ -5,4 +5,4 @@ include(../pgmodeler.pri) -SUBDIRS += dummy xml2object +SUBDIRS += dummy xml2object code_generator