Ver Fonte

Merge pull request #19 from fiffty-50/devel-db-entry-architecture

Devel db entry architecture
Felix Turowsky há 4 anos atrás
pai
commit
f4e808f718

+ 2 - 0
debug.h

@@ -1,6 +1,8 @@
 #ifndef DEBUG_H
 #define DEBUG_H
 
+#include <QDebug>
+
 // Debug Makro
 #define DEB(expr) \
     qDebug() << __PRETTY_FUNCTION__ << "\t" << expr

+ 3 - 1
main.cpp

@@ -22,6 +22,7 @@
 #include <QProcess>
 #include <QSettings>
 #include <QFileInfo>
+#include "src/experimental/DataBase.h"
 
 const auto DATA_DIR = QLatin1String("data");
 /*!
@@ -50,7 +51,8 @@ int main(int argc, char *argv[])
     QSettings::setDefaultFormat(QSettings::IniFormat);
     QSettings settings;
 
-    Db::connect();
+//    Db::connect();
+    experimental::DB()->connect();
 
     QApplication openPilotLog(argc, argv);
     if(!setup()){

+ 5 - 0
mainwindow.cpp

@@ -139,5 +139,10 @@ void MainWindow::on_actionNewAircraft_triggered()
 void MainWindow::on_actionNewPilot_triggered()
 {
     NewPilotDialog np =NewPilotDialog(Db::createNew, this);
+    using namespace experimental;
+    QObject::connect(DB(), &DataBase::commitSuccessful,
+                     &np, &NewPilotDialog::onCommitSuccessful);
+    QObject::connect(DB(), &DataBase::commitUnsuccessful,
+                     &np, &NewPilotDialog::onCommitUnsuccessful);
     np.exec();
 }

+ 1 - 0
mainwindow.h

@@ -36,6 +36,7 @@
 #include "src/gui/dialogues/newtaildialog.h"
 #include "src/gui/dialogues/newpilotdialog.h"
 #include "src/classes/runguard.h"
+#include "src/experimental/DataBase.h"
 
 QT_BEGIN_NAMESPACE
 namespace Ui {

+ 6 - 0
openPilotLog.pro

@@ -33,6 +33,8 @@ SOURCES += \
     src/database/dbinfo.cpp \
     src/database/dbsetup.cpp \
     src/database/entry.cpp \
+    src/experimental/DataBase.cpp \
+    src/experimental/Entry.cpp \
     src/gui/dialogues/firstrundialog.cpp \
     src/gui/dialogues/newflightdialog.cpp \
     src/gui/dialogues/newpilotdialog.cpp \
@@ -63,6 +65,10 @@ HEADERS += \
     src/database/dbinfo.h \
     src/database/dbsetup.h \
     src/database/entry.h \
+    src/experimental/DataBase.h \
+    src/experimental/Decl.h \
+    src/experimental/Entry.h \
+    src/experimental/UserInput.h \
     src/gui/dialogues/firstrundialog.h \
     src/gui/dialogues/newflightdialog.h \
     src/gui/dialogues/newpilotdialog.h \

+ 4 - 1
src/database/db.h

@@ -81,7 +81,8 @@ class Db
          * \return
          */
         static bool             exists(QString column, QString table, QString checkColumn,
-                                       QString value, Db::matchType match){
+                                       QString value, Db::matchType match)
+        {
             return get().iexists(column, table, checkColumn, value, match);
         }
         /*!
@@ -142,6 +143,7 @@ class Db
         static QVector<QString> customQuery(QString query, int returnValues){
             return get().icustomQuery(query, returnValues);
         }
+
     private:
         Db() {}
         void             iconnect();
@@ -159,6 +161,7 @@ class Db
         QVector<QString> icustomQuery(QString query, int returnValues);
 
     public:
+        /// [George]: Why delete these in particular?
         Db(Db const&)              = delete;
         void operator=(Db const&)  = delete;
 };

+ 6 - 5
src/database/dbsetup.cpp

@@ -248,6 +248,9 @@ const QStringList templateTables= {
 
 bool DbSetup::createDatabase()
 {
+    /// [George]: Not necessary to heap allocate for such a trivial task
+    /// TODO: Since you want to be fancy well do it with some cheeky bit operations
+    /// for the lolz.
     QVector<bool> returnValues;
 
     DEB("Creating tables...");
@@ -258,14 +261,12 @@ bool DbSetup::createDatabase()
     returnValues << importDefaultData();
 
     for (const auto& allGood : returnValues) {
-        if (!allGood) {
+        if (!allGood){
             return false;
-        } else {
-            DEB("Database successfully created!");
-            return true;
         }
     }
-    return false;
+    DEB("Database successfully created!");
+    return true;
 }
 
 

+ 1 - 1
src/database/entry.cpp

@@ -17,10 +17,10 @@
  */
 #include "entry.h"
 #include "debug.h"
+#include "db.h"
 
 Entry::Entry()
 {
-
 }
 
 Entry::Entry(QString table, int row)

+ 4 - 4
src/database/entry.h

@@ -15,12 +15,12 @@
  *You should have received a copy of the GNU General Public License
  *along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
-#ifndef ENTRY_H
-#define ENTRY_H
+#ifndef ENTRY1_H
+#define ENTRY1_H
 
 #include <QCoreApplication>
-#include "src/database/db.h"
-#include "src/database/dbinfo.h"
+#include "db.h"
+#include "dbinfo.h"
 /*!
  * \brief The Entry class is the base class for database entries.
  * It can be seen as a row in a table within the database.

+ 241 - 0
src/experimental/DataBase.cpp

@@ -0,0 +1,241 @@
+#include "DataBase.h"
+
+namespace experimental {
+
+DataBase* DataBase::instance = nullptr;
+
+DataBase* DataBase::getInstance()
+{
+    if(!instance)
+        instance = new DataBase();
+    return instance;
+}
+
+bool DataBase::connect()
+{
+    const QString driver("QSQLITE");
+
+    if (!QSqlDatabase::isDriverAvailable(driver))
+        return false;
+
+    QDir directory("data");
+    QString databaseLocation = directory.filePath("logbook.db");
+    QSqlDatabase db = QSqlDatabase::addDatabase(driver);
+    db.setDatabaseName(databaseLocation);
+
+    if (!db.open())
+        return false;
+
+    DEB("Database connection established.");
+    // Enable foreign key restrictions
+    QSqlQuery query("PRAGMA foreign_keys = ON;");
+    tableNames = db.tables();
+
+    QStringList columnNames;
+    for (const auto &table : tableNames) {
+        columnNames.clear();
+        QSqlRecord fields = db.record(table);
+        for (int i = 0; i < fields.count(); i++) {
+            columnNames.append(fields.field(i).name());
+            tableColumns.insert(table, columnNames);
+        }
+    }
+    DEB("Database Tables: " << tableNames);
+    return true;
+}
+
+void DataBase::disconnect()
+{
+    auto db = DataBase::database();
+    db.close();
+    db.removeDatabase(db.connectionName());
+    DEB("Database connection closed.");
+}
+
+QSqlDatabase DataBase::database()
+{
+    return QSqlDatabase::database("qt_sql_default_connection");
+}
+
+bool DataBase::commit(Entry entry)
+{
+    if (exists(entry)) {
+        return update(entry);
+    } else {
+        return insert(entry);
+    }
+}
+
+bool DataBase::remove(Entry entry)
+{
+    if (!exists(entry)) {
+        DEB("Error: Entry does not exist.");
+        return false;
+    }
+
+    QString statement = "DELETE FROM " + entry.getPosition().tableName +
+            " WHERE _rowid_=" + QString::number(entry.getPosition().rowId);
+    QSqlQuery q(statement);
+
+    if (q.lastError().type() == QSqlError::NoError)
+    {
+        DEB("Entry " << entry.getPosition().tableName << entry.getPosition().rowId << " removed.");
+        return true;
+    } else {
+        DEB("Unable to delete.");
+        DEB("Query: " << statement);
+        DEB("Query Error: " << q.lastError().text());
+        return false;
+    }
+}
+
+bool DataBase::exists(Entry entry)
+{
+    if(entry.getPosition() == DEFAULT_PILOT_POSITION)
+        return false;
+
+    //Check database for row id
+    QString statement = "SELECT COUNT(*) FROM " + entry.getPosition().tableName +
+            " WHERE _rowid_=" + QString::number(entry.getPosition().rowId);
+    //this returns either 1 or 0 since row ids are unique
+    QSqlQuery q(statement);
+    q.next();
+    int rowId = q.value(0).toInt();
+    if (rowId) {
+        DEB("Entry " << entry.getPosition() << " exists.");
+        return true;
+    } else {
+        DEB("Entry does not exist.");
+        return false;
+    }
+}
+
+
+bool DataBase::update(Entry updated_entry)
+{
+    auto data = updated_entry.getData();
+    QString statement = "UPDATE " + updated_entry.getPosition().tableName + " SET ";
+    for (auto i = data.constBegin(); i != data.constEnd(); ++i) {
+        if (i.key() != QString()) {
+            statement += i.key() + QLatin1String("='") + i.value() + QLatin1String("', ");
+        } else {
+            DEB(i.key() << "is empty key. skipping.");
+        }
+    }
+    statement.chop(2); // Remove last comma
+    statement.append(QLatin1String(" WHERE _rowid_=") + QString::number(updated_entry.getPosition().rowId));
+
+    DEB("UPDATE QUERY: " << statement);
+    QSqlQuery q(statement);
+
+    if (q.lastError().type() == QSqlError::NoError)
+    {
+        DEB("Entry successfully committed.");
+        emit commitSuccessful();
+        return true;
+    } else {
+        DEB("Unable to commit.");
+        DEB("Query: " << statement);
+        DEB("Query Error: " << q.lastError().text());
+        emit commitUnsuccessful(q.lastError(), statement);
+        return false;
+    }
+}
+
+bool DataBase::insert(Entry new_entry)
+{
+    auto data = new_entry.getData();
+    DEB("Inserting...");
+    QString statement = "INSERT INTO " + new_entry.getPosition().tableName + QLatin1String(" (");
+    QMap<QString, QString>::iterator i;
+    for (i = data.begin(); i != data.end(); ++i) {
+        statement += i.key() + QLatin1String(", ");
+    }
+    statement.chop(2);
+    statement += QLatin1String(") VALUES (");
+    for (i = data.begin(); i != data.end(); ++i) {
+        statement += QLatin1String("'") + i.value() + QLatin1String("', ");
+    }
+    statement.chop(2);
+    statement += QLatin1String(")");
+
+    QSqlQuery q(statement);
+    //check result.
+    if (q.lastError().type() == QSqlError::NoError)
+    {
+        DEB("Entry successfully committed.");
+        emit commitSuccessful();
+        return true;
+    } else {
+        DEB("Unable to commit.");
+        DEB("Query: " << statement);
+        DEB("Query Error: " << q.lastError().text());
+        emit commitUnsuccessful(q.lastError(), statement);
+        return false;
+    }
+
+}
+
+TableData DataBase::getEntryData(DataPosition data_position)
+{
+    // check table exists
+    if (!tableNames.contains(data_position.first)) {
+        DEB(data_position.first << " not a table in the database. Unable to retreive Entry data.");
+        return TableData();
+    }
+
+    //Check Database for rowId
+    QString statement = "SELECT COUNT(*) FROM " + data_position.first
+                      + " WHERE _rowid_=" + QString::number(data_position.second);
+    QSqlQuery check_query(statement);
+
+    if (check_query.lastError().type() != QSqlError::NoError) {
+        DEB("SQL error: " << check_query.lastError().text());
+        DEB("Statement: " << statement);
+        return TableData();
+    }
+
+    check_query.next();
+    if (check_query.value(0).toInt() == 0) {
+        DEB("No Entry found for row id: " << data_position.second );
+        return TableData();
+    }
+
+    // Retreive TableData
+    DEB("Retreiving data for row id: " << data_position.second);
+    statement = "SELECT * FROM " + data_position.first
+              + " WHERE _rowid_=" + QString::number(data_position.second);
+
+    QSqlQuery select_query(statement);
+    if (select_query.lastError().type() != QSqlError::NoError) {
+        DEB("SQL error: " << select_query.lastError().text());
+        DEB("Statement: " << statement);
+        return TableData();
+    }
+
+    select_query.next();
+    TableData entry_data;
+
+    for (const auto &column : tableColumns.value(data_position.first)) {
+        entry_data.insert(column, select_query.value(column).toString());
+    }
+    return entry_data;
+}
+
+Entry DataBase::getEntry(DataPosition data_position)
+{
+    Entry entry(data_position);
+    entry.setData(getEntryData(data_position));
+    return entry;
+}
+
+PilotEntry DataBase::getPilotEntry(RowId row_id)
+{
+    PilotEntry pilotEntry(row_id);
+    pilotEntry.setData(getEntryData(pilotEntry.getPosition()));
+    return pilotEntry;
+}
+
+DataBase* DB() { return DataBase::getInstance(); }
+
+}

+ 119 - 0
src/experimental/DataBase.h

@@ -0,0 +1,119 @@
+#ifndef __DB_H__
+#define __DB_H__
+
+#include <QPair>
+#include <QMap>
+#include <QString>
+#include <QSqlQuery>
+#include <QSqlError>
+#include <QSqlTableModel>
+#include "src/database/dbinfo.h"
+#include "debug.h"
+
+#include "Entry.h"
+
+namespace experimental {
+
+/*!
+ * \brief The DB class encapsulates the SQL database by providing fast access
+ * to hot database data.
+ */
+class DataBase : public QObject {
+    Q_OBJECT
+private:
+    TableNames tableNames;
+    TableColumns tableColumns;
+    static DataBase* instance;
+    DataBase() = default;
+public:
+    // Ensure DB is not copiable or assignable
+    DataBase(const DataBase&) = delete;
+    void operator=(const DataBase&) = delete;
+    static DataBase* getInstance();
+
+    /*!
+     * \brief Connect to the database and populate database information.
+     */
+    bool connect();
+
+    /*!
+     * \brief closes the database connection.
+     */
+    void disconnect();
+
+    /*!
+     * \brief Can be used to access the database connection.
+     * \return The QSqlDatabase object pertaining to the connection.
+     */
+    static
+    QSqlDatabase database();
+
+    /*!
+     * \brief Checks if an entry exists in the database, based on position data
+     */
+    bool exists(Entry entry);
+
+    /*!
+     * \brief commits an entry to the database, calls either insert or update,
+     * based on position data
+     */
+    bool commit(Entry entry);
+
+    /*!
+     * \brief Create new entry in the databse based on UserInput
+     */
+    bool insert(Entry new_entry);
+
+    /*!
+     * \brief Updates entry in database from existing entry tweaked by the user.
+     */
+    bool update(Entry updated_entry);
+
+    /*!
+     * \brief deletes an entry from the database.
+     */
+    bool remove(Entry entry);
+
+    /*!
+     * \brief retreive entry data from the database to create an entry object
+     */
+    TableData getEntryData(DataPosition data_position);
+
+    /*!
+     * \brief retreive an Entry from the database.
+     */
+    Entry getEntry(DataPosition data_position);
+
+    /*!
+     * \brief retreives a PilotEntry from the database.
+     *
+     * This function is a wrapper for DataBase::getEntry(DataPosition),
+     * where the table is already set and which returns a PilotEntry
+     * instead of an Entry. It allows for easy access to a pilot entry
+     * with only the RowId required as input.
+     */
+    PilotEntry getPilotEntry(RowId row_id);
+    // [G] TODO: Ensure PilotDialog works great and slowly move to
+    // other dialogs
+signals:
+    void commitSuccessful();
+
+    // [G] small nitpick but i believe we should return the error in its pure SqlError form.
+    // its better for the interested object to do get any relevant data from the error itself.
+    // The database doesnt know what part of the error is "interesting", just that it happened.
+    void commitUnsuccessful(const QSqlError &sqlError, const QString &sqlStatement);
+
+};
+
+/*!
+ * \brief Convinience function that returns instance of DataBase.
+ * Instead of this:
+ * DataBase::getInstance().commit(...)
+ * Write this:
+ * DB()->commit(...)
+ */
+DataBase* DB();
+
+}  // namespace experimental
+
+#endif

+ 44 - 0
src/experimental/Decl.h

@@ -0,0 +1,44 @@
+#ifndef DECL_H
+#define DECL_H
+
+#include <QString>
+#include <QPair>
+#include <QMap>
+#include <QObject>
+
+namespace experimental {
+
+using ColName = QString;
+using ColData = QString;
+using TableName = QString;
+using RowId = int;
+
+using TableNames = QStringList;
+/// [G]: May lead to some confusion. TableData suggest data for the entire table.
+/// but in reallity it is data per column *of single row* (unless i misunderstand)
+using TableData = QMap<ColName, ColData>;
+using ColumnData = QPair<ColName, ColData>;
+using ColumnNames = QStringList;
+using TableColumns = QMap<TableName, ColumnNames>;
+
+/// [G]: Needs some work. Inheriting from QPair may be helpful but
+/// may also be overkill. Lets determine the specific uses of DataPosition
+/// and provide our own interface i would say.
+struct DataPosition : QPair<TableName, RowId> {
+    TableName tableName;
+    RowId rowId;
+    DataPosition()
+        : tableName(first), rowId(second)
+    {}
+    DataPosition(TableName table_name, RowId row_id)
+        : QPair<TableName, RowId>::QPair(table_name, row_id),
+          tableName(first), rowId(second)
+    {}
+    DataPosition(const DataPosition& other) = default;
+    DataPosition& operator=(const DataPosition& other) = default;
+};
+auto const DEFAULT_PILOT_POSITION = DataPosition("pilots", 0);
+
+}
+
+#endif // DECL_H

+ 44 - 0
src/experimental/Entry.cpp

@@ -0,0 +1,44 @@
+#include "Entry.h"
+
+namespace experimental {
+
+Entry::Entry(DataPosition position_)
+    : position(position_)
+{}
+
+Entry::Entry(TableData table_data)
+    : tableData(table_data)
+{}
+
+Entry::Entry(DataPosition position_, TableData table_data)
+    : position(position_), tableData(table_data)
+{}
+
+void Entry::setData(TableData table_data)
+{
+    tableData = table_data;
+}
+
+const DataPosition& Entry::getPosition()
+{
+    return position;
+}
+
+const TableData& Entry::getData()
+{
+    return tableData;
+}
+
+PilotEntry::PilotEntry()
+    : Entry::Entry(DEFAULT_PILOT_POSITION)
+{}
+
+PilotEntry::PilotEntry(int row_id)
+    : Entry::Entry(DataPosition("pilots", row_id))
+{}
+
+PilotEntry::PilotEntry(TableData table_data)
+    : Entry::Entry(DEFAULT_PILOT_POSITION, table_data)
+{}
+
+}  // namespace experimental

+ 51 - 0
src/experimental/Entry.h

@@ -0,0 +1,51 @@
+#ifndef ENTRY_H
+#define ENTRY_H
+
+#include <QString>
+#include <QStringList>
+#include <QMap>
+#include <QPair>
+
+#include "Decl.h"
+
+namespace experimental {
+
+/// [G]: Define what data is public and what not. For objects such as
+/// DataPosition which are consumable its no biggy. Are entries the same?
+/// If so we could avoid getters and setters
+/*!
+ * \brief The Entry class encapsulates table metadata(table name, row id)
+ *  and data for new and existing entries in the database to operate on.
+ */
+class Entry {
+protected:
+    DataPosition position;
+    TableData tableData;
+public:
+    Entry() = delete; // Demand specificity from default constructor
+    Entry(const Entry&) = default;
+    Entry& operator=(const Entry&) = default;
+    Entry(DataPosition position_);
+    Entry(TableData table_data);
+    Entry(DataPosition position_, TableData table_data);
+
+    void setData(TableData table_data);
+    void setPosition(DataPosition position_);
+
+    const DataPosition& getPosition();
+    const TableData& getData();
+
+};
+
+struct PilotEntry : public Entry {
+public:
+    PilotEntry();
+    PilotEntry(const PilotEntry& pe) = default;
+    PilotEntry& operator=(const PilotEntry& pe) = default;
+    PilotEntry(int row_id);
+    PilotEntry(TableData table_data);
+};
+
+}
+
+#endif // ENTRY_H

+ 47 - 0
src/experimental/UserInput.h

@@ -0,0 +1,47 @@
+#ifndef __USERINPUT_H__
+#define __USERINPUT_H__
+
+#include <QPair>
+#include <QString>
+#include <QBitArray>
+#include <QMap>
+#include <QStringList>
+#include <algorithm>
+
+namespace experimental {
+
+using EntryData = QMap<QString, QString>;
+
+/*!
+ * \brief The EntryData struct. Contains ALL possible data.
+ * However depending on who is constructing it, different types are initialised.
+ * FUTURE: Would it be necessary to able to change data?
+ * 	 George: I would say no because we dont want to fuck around with the entry.
+ * Collect data from user -> Pack it up in the entry -> Consume it.
+ * \todo Figure out exactly what the database would prefer as return value
+ *   George: I would assume key: value pairs where the keys are what you would
+ * put in the queries. This will affect data aswell (and propably simplify it)
+ */
+class UserInput {
+private:
+    const EntryData data;
+public:
+    const enum class MetaTag {Pilot, Flight, Aircraft} meta_tag;
+
+public:
+    UserInput() = delete;
+    explicit
+    UserInput(EntryData new_data, MetaTag tag)
+        :  data(new_data), meta_tag(tag) {}
+
+    QString only(QString data_type) const { return data.value(data_type); }
+    const EntryData& all() const { return data; }
+};
+
+UserInput newPilotInput(EntryData ed) { return UserInput(ed, UserInput::MetaTag::Pilot); }
+UserInput newFlightInput(EntryData ed) { return UserInput(ed, UserInput::MetaTag::Flight); }
+UserInput newAircraftInput(EntryData ed) { return UserInput(ed, UserInput::MetaTag::Aircraft); }
+
+}
+
+#endif

+ 84 - 77
src/gui/dialogues/newpilotdialog.cpp

@@ -19,6 +19,8 @@
 #include "ui_newpilot.h"
 #include "debug.h"
 
+#include "src/experimental/DataBase.h"
+#include "src/experimental/Entry.h"
 /* Examples for names around the world:
  * José Eduardo Santos Tavares Melo Silva
  * María José Carreño Quiñones
@@ -33,50 +35,53 @@
  * Mathias d'Arras
  * Martin Luther King, Jr.
  */
-static const auto NAME_RX = QLatin1String("(\\p{L}+(\\s|'|\\-)?\\s?(\\p{L}+)?\\s?)");
-static const auto FIRSTNAME_VALID = QPair<QString, QRegularExpression> {
+// [F] I think we don't even need static here at all, as it should be implicitly static anyway?
+const auto NAME_RX = QLatin1String("(\\p{L}+(\\s|'|\\-)?\\s?(\\p{L}+)?\\s?)");
+const auto FIRSTNAME_VALID = QPair<QString, QRegularExpression> {
     "picfirstnameLineEdit", QRegularExpression(NAME_RX + NAME_RX + NAME_RX)};
-static const auto LASTNAME_VALID = QPair<QString, QRegularExpression> {
+const auto LASTNAME_VALID = QPair<QString, QRegularExpression> {
     "piclastnameLineEdit", QRegularExpression(NAME_RX + NAME_RX + NAME_RX)};
-static const auto PHONE_VALID = QPair<QString, QRegularExpression> {
+const auto PHONE_VALID = QPair<QString, QRegularExpression> {
     "phoneLineEdit", QRegularExpression("^[+]{0,1}[0-9\\-\\s]+")};
-static const auto EMAIL_VALID = QPair<QString, QRegularExpression> {
+const auto EMAIL_VALID = QPair<QString, QRegularExpression> {
     "emailLineEdit", QRegularExpression("\\A[a-z0-9!#$%&'*+/=?^_‘{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_‘{|}~-]+)*@"
                                         "(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\z")};
-static const auto COMPANY_VALID = QPair<QString, QRegularExpression> {
+const auto COMPANY_VALID = QPair<QString, QRegularExpression> {
     "companyLineEdit", QRegularExpression("\\w+(\\s|\\-)?(\\w+(\\s|\\-)?)?(\\w+(\\s|\\-)?)?")};
-static const auto EMPLOYEENR_VALID = QPair<QString, QRegularExpression> {
+const auto EMPLOYEENR_VALID = QPair<QString, QRegularExpression> {
     "employeeidLineEdit", QRegularExpression("\\w+")};
 
+const auto LINE_EDIT_VALIDATORS = QVector{
+        FIRSTNAME_VALID,
+        LASTNAME_VALID,
+        PHONE_VALID,
+        EMAIL_VALID,
+        COMPANY_VALID,
+        EMPLOYEENR_VALID
+};
 
-static const auto LINE_EDIT_VALIDATORS = QVector({FIRSTNAME_VALID, LASTNAME_VALID,
-                                           PHONE_VALID,     EMAIL_VALID,
-                                           COMPANY_VALID,     EMPLOYEENR_VALID});
 // For creating a new entry
-NewPilotDialog::NewPilotDialog(Db::editRole edRole, QWidget *parent) :
+NewPilotDialog::NewPilotDialog(QWidget *parent) :
     QDialog(parent),
     ui(new Ui::NewPilot)
 {
     DEB("New NewPilotDialog\n");
-    role = edRole;
-    ui->setupUi(this);
+    setup();
 
-    setupValidators();
-    setupCompleter();
+    using namespace experimental;
+    pilotEntry = PilotEntry();
+    ui->piclastnameLineEdit->setFocus();
 }
-// For editing an existing entry
-NewPilotDialog::NewPilotDialog(Pilot existingEntry, Db::editRole edRole, QWidget *parent) :
+
+NewPilotDialog::NewPilotDialog(int rowId, QWidget *parent) :
     QDialog(parent),
     ui(new Ui::NewPilot)
 {
-    DEB("New NewPilotDialog\n");
-    oldEntry = existingEntry;
-    role = edRole;
-    ui->setupUi(this);
-
-    setupValidators();
-    setupCompleter();
+    setup();
 
+    using namespace experimental;
+    pilotEntry = DB()->getPilotEntry(rowId);
+    DEB("Pilot Entry position: " << pilotEntry.getPosition());
     formFiller();
     ui->piclastnameLineEdit->setFocus();
 }
@@ -87,20 +92,12 @@ NewPilotDialog::~NewPilotDialog()
     delete ui;
 }
 
-void NewPilotDialog::on_buttonBox_accepted()
+// [G]: Mover the two setup functions in one. The debug explains what happens
+// and we avoid 2 function calls with 1 potentially inlined one.
+void NewPilotDialog::setup()
 {
-    if (ui->piclastnameLineEdit->text().isEmpty() || ui->picfirstnameLineEdit->text().isEmpty()) {
-        auto mb = QMessageBox(this);
-        mb.setText("Last Name and First Name are required.");
-        mb.show();
-    } else {
-        submitForm();
-        accept();
-    }
-}
+    ui->setupUi(this);
 
-void NewPilotDialog::setupValidators()
-{
     DEB("Setting up Validators...");
     for(const auto& pair : LINE_EDIT_VALIDATORS){
         auto line_edit = parent()->findChild<QLineEdit*>(pair.first);
@@ -111,33 +108,47 @@ void NewPilotDialog::setupValidators()
             DEB("Error: Line Edit not found: "<< pair.first << " - skipping.");
         }
     }
-}
 
-void NewPilotDialog::setupCompleter()
-{
     DEB("Setting up completer...");
-
     auto companies = new CompletionList(CompleterTarget::companies);
     auto completer = new QCompleter(companies->list, ui->companyLineEdit);
     completer->setCompletionMode(QCompleter::InlineCompletion);
     completer->setCaseSensitivity(Qt::CaseSensitive);
-
     ui->companyLineEdit->setCompleter(completer);
 }
 
+void NewPilotDialog::on_buttonBox_accepted()
+{
+    if (ui->piclastnameLineEdit->text().isEmpty() || ui->picfirstnameLineEdit->text().isEmpty()) {
+        auto mb = QMessageBox(this);
+        mb.setText("Last Name and First Name are required.");
+        mb.show();
+    } else {
+        submitForm();
+    }
+}
+
+void NewPilotDialog::onCommitSuccessful()
+{
+    accept();
+}
+
+void NewPilotDialog::onCommitUnsuccessful(const QSqlError &sqlError, const QString &)
+{
+    auto mb = QMessageBox(this);
+    mb.setText("The following error has ocurred. Your entry has not been saved./n/n"
+               + sqlError.text());
+}
+
 void NewPilotDialog::formFiller()
 {
     DEB("Filling Form...");
-    DEB(oldEntry);
-    auto line_edits = parent()->findChildren<QLineEdit *>();
+    auto line_edits = this->findChildren<QLineEdit *>();
 
     for (const auto &le : line_edits) {
-        QString key = le->objectName();
-        key.chop(8);//remove "LineEdit"
-        QString value = oldEntry.data.value(key);
-        if (!value.isEmpty()) {
-            le->setText(value);
-        }
+        QString key = le->objectName().remove("LineEdit");
+        QString value = pilotEntry.getData().value(key);
+        le->setText(value);
     }
 }
 
@@ -146,34 +157,30 @@ void NewPilotDialog::submitForm()
     DEB("Creating Database Object...");
     QMap<QString, QString> newData;
 
-    auto line_edits = parent()->findChildren<QLineEdit *>();
+    auto line_edits = this->findChildren<QLineEdit *>();
 
-    for (const auto &le : line_edits) {
-        QString key = le->objectName();
-        key.chop(8);//remove "LineEdit"
-        QString value = le->text();
-        if (!key.isEmpty()) {
-            newData.insert(key, value);
-        }
-    }
-    QString displayName;
-    displayName.append(ui->piclastnameLineEdit->text());
-    displayName.append(QLatin1String(", "));
-    displayName.append(ui->picfirstnameLineEdit->text().left(1));
-    displayName.append(QLatin1Char('.'));
-    newData.insert("displayname",displayName);
-    //create db object
-    switch (role) {
-    case Db::createNew: {
-        auto newEntry = Pilot(newData);;
-        DEB("New Object: " << newEntry);
-        newEntry.commit();
-        break;
-    }
-    case Db::editExisting:
-        oldEntry.setData(newData);
-        DEB("updated entry: " << oldEntry);
-        oldEntry.commit();
-        break;
+    for(auto& le : line_edits) {
+        auto key = le->objectName().remove("LineEdit");
+        auto value = le->text();
+        newData.insert(key, value);
     }
+
+    /// [G]: If this formating is Entry-Subclass specific
+    /// shouldnt PilotEntry know what to do with the database-centric pilot name?
+    /// [F]: That's one way of looking at it - I see it more as something derived
+    /// from a QLineEdit included in the 'package' of data the entry gets from the
+    /// Dialo. Where in the PilotEntry would you see it as more appropriate?
+    QString display_name;
+    display_name.append(ui->piclastnameLineEdit->text());
+    display_name.append(QLatin1String(", "));
+    display_name.append(ui->picfirstnameLineEdit->text().left(1));
+    display_name.append(QLatin1Char('.'));
+    newData.insert("displayname", display_name);
+
+    using namespace experimental;
+
+    pilotEntry.setData(newData);
+    DEB("Pilot entry position: " << pilotEntry.getPosition());
+    DEB("Pilot entry data: " << pilotEntry.getData());
+    DB()->commit(pilotEntry);
 }

+ 14 - 10
src/gui/dialogues/newpilotdialog.h

@@ -26,6 +26,10 @@
 #include "src/classes/pilot.h"
 #include "src/classes/completionlist.h"
 
+#include "src/experimental/DataBase.h"
+#include "src/experimental/Entry.h"
+#include "src/experimental/Decl.h"
+
 namespace Ui {
 class NewPilot;
 }
@@ -33,29 +37,29 @@ class NewPilot;
 class NewPilotDialog : public QDialog
 {
     Q_OBJECT
-
 public:
-    explicit NewPilotDialog(Db::editRole, QWidget *parent = nullptr);
-    explicit NewPilotDialog(Pilot, Db::editRole, QWidget *parent = nullptr);
+    explicit NewPilotDialog(QWidget *parent = nullptr);
+    explicit NewPilotDialog(int rowId, QWidget *parent = nullptr);
     ~NewPilotDialog();
-
 private slots:
     void on_buttonBox_accepted();
 
-private:
-    Ui::NewPilot *ui;
+public slots:
 
-    Db::editRole role;
+    void onCommitSuccessful();
 
-    Pilot oldEntry;
+    void onCommitUnsuccessful(const QSqlError &sqlError, const QString &);
+private:
+    Ui::NewPilot *ui;
 
-    void setupValidators();
+    experimental::PilotEntry pilotEntry;
 
-    void setupCompleter();
+    inline void setup();
 
     void formFiller();
 
     void submitForm();
 };
 
+
 #endif // NEWPILOT_H

+ 1 - 2
src/gui/widgets/homewidget.cpp

@@ -38,6 +38,5 @@ HomeWidget::~HomeWidget()
 
 void HomeWidget::on_pushButton_clicked()
 {
-    NewFlightDialog nf(this, Flight(11), Db::editExisting);
-    nf.exec();
+    // do debug stuff
 }

+ 2 - 0
src/gui/widgets/homewidget.h

@@ -34,6 +34,8 @@
 #include "src/gui/dialogues/firstrundialog.h"
 #include "src/gui/dialogues/newflightdialog.h"
 
+#include "src/experimental/DataBase.h"
+#include "src/experimental/Decl.h"
 
 namespace Ui {
 class HomeWidget;

+ 15 - 5
src/gui/widgets/pilotswidget.cpp

@@ -39,12 +39,17 @@ void PilotsWidget::tableView_selectionChanged()//const QItemSelection &index, co
     selectedPilots.clear();
 
     for (const auto& row : selection->selectedRows()) {
-        selectedPilots << row.data().toInt();
+        selectedPilots.append(row.data().toInt());
         DEB("Selected Tails(s) with ID: " << selectedPilots);
     }
     if(selectedPilots.length() == 1) {
 
-        NewPilotDialog* np = new NewPilotDialog(Pilot(selectedPilots.first()), Db::editExisting, this);
+        NewPilotDialog* np = new NewPilotDialog(selectedPilots.first(), this);
+        using namespace experimental;
+        QObject::connect(DB(), &DataBase::commitSuccessful,
+                         np,   &NewPilotDialog::onCommitSuccessful);
+        QObject::connect(DB(), &DataBase::commitUnsuccessful,
+                         np,   &NewPilotDialog::onCommitUnsuccessful);
         connect(np, SIGNAL(accepted()), this, SLOT(pilot_editing_finished()));
         connect(np, SIGNAL(rejected()), this, SLOT(pilot_editing_finished()));
         np->setWindowFlag(Qt::Widget);
@@ -63,10 +68,15 @@ void PilotsWidget::tableView_headerClicked(int column)
 
 void PilotsWidget::on_newButton_clicked()
 {
-    NewPilotDialog* np = new NewPilotDialog(Db::createNew, this);
+    NewPilotDialog* np = new NewPilotDialog(this);
     np->setAttribute(Qt::WA_DeleteOnClose);
-    connect(np, SIGNAL(accepted()),
-            this, SLOT(pilot_editing_finished()));
+    connect(np, SIGNAL(accepted()), this, SLOT(pilot_editing_finished()));
+    connect(np, SIGNAL(rejected()), this, SLOT(pilot_editing_finished()));
+    using namespace experimental;
+    QObject::connect(DB(), &DataBase::commitSuccessful,
+                     np,   &NewPilotDialog::onCommitSuccessful);
+    QObject::connect(DB(), &DataBase::commitUnsuccessful,
+                     np,   &NewPilotDialog::onCommitUnsuccessful);
     np->exec();
 }