From: APTX Date: Tue, 28 Aug 2012 13:53:17 +0000 (+0200) Subject: Add scriptable MyList Reports (currently just allows you to run an SQL query) X-Git-Url: https://gitweb.aptx.org/?a=commitdiff_plain;h=1c158e741665d69c857fce108df88a92b9438a37;p=localmylist.git Add scriptable MyList Reports (currently just allows you to run an SQL query) --- diff --git a/localmylist/database.cpp b/localmylist/database.cpp index 34cb6ce..e652597 100644 --- a/localmylist/database.cpp +++ b/localmylist/database.cpp @@ -118,6 +118,10 @@ OpenFileData::OpenFileData() epno = 0; } +Report::Report() +{ + reportId = 0; +} DatabaseConnectionSettings::DatabaseConnectionSettings() { @@ -1188,6 +1192,7 @@ bool Database::clearPendingMyListUpdate(const PendingMyListUpdate &request) QStringList Database::getWatchedDirectories(int hostId) { QSqlQuery &q = prepare("SELECT directory FROM watched_directory WHERE host_id = :hostId"); + q.bindValue(":hostId", hostId); if (!exec(q)) @@ -1205,6 +1210,62 @@ QStringList Database::getWatchedDirectories(int hostId) return ret; } +QList Database::getReports() +{ + QList reports; + + QSqlQuery &q = prepare("SELECT report_id, name, script FROM report"); + + if (!exec(q)) + return reports; + + while (q.next()) + { + Report report; + report.reportId = q.value(0).toInt(); + report.name = q.value(1).toString(); + report.script = q.value(2).toString(); + + reports << report; + } + + return reports; +} + +bool Database::addReport(const Report &report) +{ + QSqlQuery &q = prepare("INSERT INTO report VALUES (DEFAULT, :name, :script)"); + q.bindValue(":name", report.name); + q.bindValue(":script", report.script); + + return exec(q); +} + +bool Database::setReport(const Report &report) +{ + QSqlQuery &q = prepare( + "UPDATE report " + " SET name = :name, script = :script " + " WHERE report_id = :reportId"); + + q.bindValue(":reportId", report.reportId); + q.bindValue(":name", report.name); + q.bindValue(":script", report.script); + + return exec(q); +} + +bool Database::removeReport(int reportId) +{ + QSqlQuery &q = prepare( + "DELETE FROM report " + " WHERE report_id = :reportId"); + + q.bindValue(":reportId", reportId); + + return exec(q); +} + bool Database::clearStartedPendingRequests() { return exec( @@ -1361,7 +1422,10 @@ void Database::disconnect() qDebug() << "Not connected"; return; } + + d->preparedQueries.clear(); d->db.close(); + emit disconnected(); } diff --git a/localmylist/database.h b/localmylist/database.h index 23ea7ca..21b563a 100644 --- a/localmylist/database.h +++ b/localmylist/database.h @@ -207,6 +207,15 @@ struct LOCALMYLISTSHARED_EXPORT OpenFileData OpenFileData(); }; +struct LOCALMYLISTSHARED_EXPORT Report +{ + int reportId; + QString name; + QString script; + + Report(); +}; + struct LOCALMYLISTSHARED_EXPORT DatabaseConnectionSettings { QString host; @@ -285,6 +294,11 @@ public slots: QStringList getWatchedDirectories(int hostId); + QList getReports(); + bool addReport(const LocalMyList::Report &report); + bool setReport(const LocalMyList::Report &report); + bool removeReport(int reportId); + bool clearStartedPendingRequests(); bool clearStartedMyListUpdateRequests(); bool clearFileRenames(); @@ -399,6 +413,10 @@ Q_DECLARE_METATYPE(LocalMyList::OpenFileData) Q_DECLARE_METATYPE(LocalMyList::OpenFileData*) Q_DECLARE_METATYPE(QList) Q_DECLARE_METATYPE(QList) +Q_DECLARE_METATYPE(LocalMyList::Report) +Q_DECLARE_METATYPE(LocalMyList::Report*) +Q_DECLARE_METATYPE(QList) +Q_DECLARE_METATYPE(QList) Q_DECLARE_METATYPE(LocalMyList::DatabaseConnectionSettings) Q_DECLARE_METATYPE(LocalMyList::DatabaseConnectionSettings*) Q_DECLARE_METATYPE(QList) diff --git a/localmylist/localmylist.pro b/localmylist/localmylist.pro index df032e4..9dee8a8 100644 --- a/localmylist/localmylist.pro +++ b/localmylist/localmylist.pro @@ -26,7 +26,9 @@ SOURCES += \ unknownfilelookuptask.cpp \ renameutils.cpp \ directorywatcher.cpp \ - addrelatedepisodestask.cpp + addrelatedepisodestask.cpp \ + scriptablesql.cpp \ + reportengine.cpp HEADERS += \ localmylist_global.h \ @@ -46,7 +48,9 @@ HEADERS += \ unknownfilelookuptask.h \ renameutils.h \ directorywatcher.h \ - addrelatedepisodestask.h + addrelatedepisodestask.h \ + scriptablesql.h \ + reportengine.h CONV_HEADERS += \ include/LocalMyList/AbstractTask \ diff --git a/localmylist/reportengine.cpp b/localmylist/reportengine.cpp new file mode 100644 index 0000000..eecfeb2 --- /dev/null +++ b/localmylist/reportengine.cpp @@ -0,0 +1,43 @@ +#include "reportengine.h" + +#include +#include +#include "database.h" +#include "scriptablesql.h" + +#include + +namespace LocalMyList { + +ReportEngine::ReportEngine(QObject *parent) : + QObject(parent) +{ + m_engine = new QScriptEngine(this); + registerSqlTypes(m_engine); +} + +QSqlQuery ReportEngine::query() const +{ + QSqlQuery q = m_engine->globalObject().property("query").toVariant().value(); + qDebug() << q.lastError(); + return q; +} + +QScriptEngine *ReportEngine::engine() const +{ + return m_engine; +} + +void ReportEngine::run(const Report &report) +{ + run(report.script); +} + +void ReportEngine::run(const QString &report) +{ + m_engine->evaluate(report); + if (m_engine->hasUncaughtException()) + qDebug() << m_engine->uncaughtException().toString(); +} + +} // namespace LocalMyList diff --git a/localmylist/reportengine.h b/localmylist/reportengine.h new file mode 100644 index 0000000..79f55c3 --- /dev/null +++ b/localmylist/reportengine.h @@ -0,0 +1,38 @@ +#ifndef REPORTENGINE_H +#define REPORTENGINE_H + +#include "localmylist_global.h" +#include + +#include + +class QScriptEngine; + +namespace LocalMyList { + +struct Report; + +class LOCALMYLISTSHARED_EXPORT ReportEngine : public QObject +{ + Q_OBJECT + Q_PROPERTY(QSqlQuery query READ query) + Q_PROPERTY(QScriptEngine *engine READ engine) + +public: + explicit ReportEngine(QObject *parent = 0); + + QSqlQuery query() const; + QScriptEngine *engine() const; +signals: + +public slots: + void run(const Report &report); + void run(const QString &report); + +private: + QScriptEngine *m_engine; +}; + +} // namespace LocalMyList + +#endif // REPORTENGINE_H diff --git a/localmylist/scriptablesql.cpp b/localmylist/scriptablesql.cpp new file mode 100644 index 0000000..373e5a5 --- /dev/null +++ b/localmylist/scriptablesql.cpp @@ -0,0 +1,84 @@ +#include "scriptablesql.h" + +#include + +#include "mylist.h" +#include "database.h" +#include + +namespace LocalMyList { + +void registerSqlTypes(QScriptEngine *engine) +{ + Scriptable::SqlQuery *SqlQueryPrototype = new Scriptable::SqlQuery(); + engine->setDefaultPrototype(qMetaTypeId(), engine->newQObject(SqlQueryPrototype)); + engine->setDefaultPrototype(qMetaTypeId(), engine->newQObject(SqlQueryPrototype)); + engine->globalObject().setProperty("SqlQuery", engine->newFunction(Scriptable::SqlQuery_ctor)); +} + +namespace Scriptable { + +SqlQuery::SqlQuery(QObject *parent) : + QObject(parent), QScriptable() +{ +} + +bool SqlQuery::prepare(const QString &sql) +{ + auto o = thisObj(); + if (!o) return false; + QString tmp = sql; + tmp.replace(QRegExp("[\\n\\r]+"), " "); + qDebug() << tmp; + *o = MyList::instance()->database()->prepareOneShot(tmp); + return true; +} + +bool SqlQuery::bindValue(const QString &name, const QVariant &value) +{ + auto o = thisObj(); + if (!o) return false; + o->bindValue(name, value); + return true; +} + +bool SqlQuery::exec() +{ + auto o = thisObj(); + if (!o) return false; + return MyList::instance()->database()->exec(*o); +} + +bool SqlQuery::next() +{ + auto o = thisObj(); + if (!o) return false; + return o->next(); +} + +QVariant SqlQuery::column(const QString &column) +{ + auto o = thisObj(); + if (!o) return QVariant(); + return o->record().value(column); +} + +QVariant SqlQuery::columnNo(int column) +{ + auto o = thisObj(); + if (!o) return QVariant(); + return o->value(column); +} + +QSqlQuery *SqlQuery::thisObj() const +{ + return qscriptvalue_cast(thisObject()); +} + +QScriptValue SqlQuery_ctor(QScriptContext *, QScriptEngine *engine) +{ + return engine->toScriptValue(QSqlQuery()); +} + +} // namespace Scriptable +} // namespace LocalMyList diff --git a/localmylist/scriptablesql.h b/localmylist/scriptablesql.h new file mode 100644 index 0000000..1f6f2ea --- /dev/null +++ b/localmylist/scriptablesql.h @@ -0,0 +1,47 @@ +#ifndef SCRIPTABLESQL_H +#define SCRIPTABLESQL_H + +#include "localmylist_global.h" + +#include +#include +#include + +#include + +namespace LocalMyList { + +void LOCALMYLISTSHARED_EXPORT registerSqlTypes(QScriptEngine *engine); + +namespace Scriptable { + +class LOCALMYLISTSHARED_EXPORT SqlQuery : public QObject, protected QScriptable +{ + Q_OBJECT +public: + SqlQuery(QObject *parent = 0); + +public slots: + bool prepare(const QString &sql); + bool bindValue(const QString &name, const QVariant &value); + + bool exec(); + + bool next(); + + QVariant column(const QString &column); + QVariant columnNo(int column); + +private: + QSqlQuery *thisObj() const; +}; + +QScriptValue SqlQuery_ctor(QScriptContext *, QScriptEngine *engine); + +} // namespace Scriptable +} // namespace LocalMyList + +Q_DECLARE_METATYPE(QSqlQuery) +Q_DECLARE_METATYPE(QSqlQuery*) + +#endif // SCRIPTABLESQL_H diff --git a/localmylist/share/schema/schema.sql b/localmylist/share/schema/schema.sql index 0c064de..a4c2be8 100644 --- a/localmylist/share/schema/schema.sql +++ b/localmylist/share/schema/schema.sql @@ -204,6 +204,14 @@ CREATE TABLE watched_directory CONSTRAINT watched_directory_pk PRIMARY KEY (host_id, directory) ); +CREATE TABLE report +( + report_id serial, + name character varying(500), + script text, + CONSTRAINT report_pk PRIMARY KEY (report_id) +); + CREATE TABLE log ( log_id serial NOT NULL, type integer, diff --git a/management-gui/mainwindow.cpp b/management-gui/mainwindow.cpp index 72dfb30..92e0e0b 100644 --- a/management-gui/mainwindow.cpp +++ b/management-gui/mainwindow.cpp @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include "mylist.h" #include "database.h" @@ -18,7 +20,9 @@ #include "mylistfiltermodel.h" #include "unknownfilelookuptask.h" #include "addrelatedepisodestask.h" +#include "reportengine.h" #include "renamesettingsdialog.h" +#include "reporteditordialog.h" #include @@ -63,6 +67,9 @@ MainWindow::MainWindow(QWidget *parent) : connect(ui->myListView, SIGNAL(renameTest(int)), this, SLOT(openRenameScriptEditor(int))); + reportResultModel = new QSqlQueryModel(this); + ui->reportResultView->setModel(reportResultModel); + setAcceptDrops(true); } @@ -397,6 +404,18 @@ void MainWindow::on_filterType_currentIndexChanged(int) on_filterInput_textChanged(ui->filterInput->text()); } +void MainWindow::on_reloadReports_clicked() +{ + ui->reports->clear(); + + QList reports = MyList::instance()->database()->getReports(); + + foreach (const Report &report, reports) + { + ui->reports->addItem(report.name, QVariant::fromValue(report)); + } +} + void MainWindow::dragEnterEvent(QDragEnterEvent *event) { if (event->mimeData()->hasFormat("text/uri-list")) @@ -417,3 +436,66 @@ void MainWindow::dropEvent(QDropEvent *event) } event->acceptProposedAction(); } + + + +void MainWindow::on_reports_currentIndexChanged(int) +{ + on_runReport_clicked(); +} + +void MainWindow::on_runReport_clicked() +{ + Report report = ui->reports->itemData(ui->reports->currentIndex()).value(); + ReportEngine e; + + e.run(report); + + reportResultModel->setQuery(e.query()); + ui->reportResultView->resizeColumnsToContents(); +} + +void MainWindow::on_editReport_clicked() +{ + if (!ui->reports->count()) + return; + + int idx = ui->reports->currentIndex(); + + ReportEditorDialog d; + d.setReport(ui->reports->itemData(idx).value()); + + if (!d.exec()) + return; + + MyList::instance()->database()->setReport(d.report()); + + ui->reports->setItemData(idx, QVariant::fromValue(d.report())); + on_runReport_clicked(); +} + +void MainWindow::on_addReport_clicked() +{ + ReportEditorDialog d; + + if (!d.exec()) + return; + + MyList::instance()->database()->addReport(d.report()); + + ui->reports->addItem(d.report().name, QVariant::fromValue(d.report())); + + ui->reports->setCurrentIndex(ui->reports->count() - 1); +} + + +void MainWindow::on_tabWidget_currentChanged(QWidget *arg1) +{ + if (arg1 != ui->reportsTab) + return; + + if (ui->reports->count()) + return; + + on_reloadReports_clicked(); +} diff --git a/management-gui/mainwindow.h b/management-gui/mainwindow.h index 42c94b0..c402ec3 100644 --- a/management-gui/mainwindow.h +++ b/management-gui/mainwindow.h @@ -10,8 +10,11 @@ class MainWindow; namespace LocalMyList { class MyListModel; + class ReportEngine; } + class QLabel; +class QSqlQueryModel; class RenameSettingsDialog; class MyListFilterModel; @@ -74,6 +77,14 @@ private slots: void on_filterInput_textChanged(const QString &filter); void on_filterType_currentIndexChanged(int); + // Reports + void on_reloadReports_clicked(); + void on_reports_currentIndexChanged(int index); + void on_runReport_clicked(); + void on_editReport_clicked(); + void on_addReport_clicked(); + void on_tabWidget_currentChanged(QWidget *arg1); + protected: void dragEnterEvent(QDragEnterEvent *event); void dropEvent(QDropEvent *event); @@ -87,6 +98,8 @@ private: LocalMyList::MyListModel *myListModel; MyListFilterModel *myListFilterModel; + + QSqlQueryModel *reportResultModel; }; #endif // MAINWINDOW_H diff --git a/management-gui/mainwindow.ui b/management-gui/mainwindow.ui index 53547d6..be80f43 100644 --- a/management-gui/mainwindow.ui +++ b/management-gui/mainwindow.ui @@ -14,43 +14,163 @@ LocalMyList Management - - - - - - - - - - - - - - + + + 0 + + + 0 + - - - - - Qt::Horizontal + + + 0 + + + true + + + + MyList + + + + 0 + + + 9 - - - 40 - 20 - + + 0 - - - - - - Refresh + + 0 - - - + + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Refresh + + + + + + + + + + Reports + + + + + + + + Report: + + + + + + + + 300 + 0 + + + + + + + + Edit... + + + + + + + Add + + + + + + + Delete + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Run + + + + + + + Reload + + + + + + + + + Qt::Horizontal + + + + + + + + @@ -254,12 +374,6 @@
mylistview.h
- - filterInput - filterType - myListView - refreshButton - diff --git a/management-gui/management-gui.pro b/management-gui/management-gui.pro index a238476..ebc27ba 100644 --- a/management-gui/management-gui.pro +++ b/management-gui/management-gui.pro @@ -1,4 +1,4 @@ -QT += core gui +QT += core gui scripttools include(../config.pri) @@ -12,13 +12,15 @@ SOURCES += main.cpp\ databaseconnectiondialog.cpp \ mylistview.cpp \ renamesettingsdialog.cpp \ - mylistfiltermodel.cpp + mylistfiltermodel.cpp \ + reporteditordialog.cpp HEADERS += mainwindow.h \ databaseconnectiondialog.h \ mylistview.h \ renamesettingsdialog.h \ - mylistfiltermodel.h + mylistfiltermodel.h \ + reporteditordialog.h FORMS += mainwindow.ui \ databaseconnectiondialog.ui \ diff --git a/management-gui/reporteditordialog.cpp b/management-gui/reporteditordialog.cpp new file mode 100644 index 0000000..53aea37 --- /dev/null +++ b/management-gui/reporteditordialog.cpp @@ -0,0 +1,66 @@ +#include "reporteditordialog.h" + +#include +#include +#include +#include +#include +#include + +ReportEditorDialog::ReportEditorDialog(QWidget *parent) : + QDialog(parent) +{ + resize(800, 600); + QLabel *nameLabel = new QLabel("Report Name:", this); + name = new QLineEdit(this); + QHBoxLayout *hLayout = new QHBoxLayout; + + hLayout->addWidget(nameLabel); + hLayout->addWidget(name); + + script = new QPlainTextEdit(this); + + buttons = new QDialogButtonBox(QDialogButtonBox::Save | QDialogButtonBox::Cancel | QDialogButtonBox::Reset, Qt::Horizontal, this); + + QVBoxLayout *vLayout = new QVBoxLayout; + + vLayout->addLayout(hLayout); + vLayout->addWidget(script); + vLayout->addWidget(buttons); + + setLayout(vLayout); + + connect(buttons, SIGNAL(clicked(QAbstractButton*)), this, SLOT(buttonClicked(QAbstractButton*))); +} + +LocalMyList::Report ReportEditorDialog::report() const +{ + m_report.name = name->text(); + m_report.script = script->toPlainText(); + return m_report; +} + +void ReportEditorDialog::setReport(const LocalMyList::Report &report) +{ + m_report = report; + name->setText(m_report.name); + script->setPlainText(m_report.script); +} + +void ReportEditorDialog::buttonClicked(QAbstractButton *button) +{ + switch (buttons->standardButton(button)) + { + case QDialogButtonBox::Save: + accept(); + break; + case QDialogButtonBox::Cancel: + reject(); + break; + case QDialogButtonBox::Reset: + name->setText(m_report.name); + script->setPlainText(m_report.script); + break; + default: ; + } +} diff --git a/management-gui/reporteditordialog.h b/management-gui/reporteditordialog.h new file mode 100644 index 0000000..e5db463 --- /dev/null +++ b/management-gui/reporteditordialog.h @@ -0,0 +1,35 @@ +#ifndef REPORTEDITORDIALOG_H +#define REPORTEDITORDIALOG_H + +#include + +#include "database.h" + +class QAbstractButton; +class QLineEdit; +class QDialogButtonBox; +class QPlainTextEdit; + +class ReportEditorDialog : public QDialog +{ + Q_OBJECT +public: + explicit ReportEditorDialog(QWidget *parent = 0); + + LocalMyList::Report report() const; + void setReport(const LocalMyList::Report &report); + +signals: + +private slots: + void buttonClicked(QAbstractButton *button); + +private: + mutable LocalMyList::Report m_report; + + QLineEdit *name; + QDialogButtonBox *buttons; + QPlainTextEdit *script; +}; + +#endif // REPORTEDITORDIALOG_H