Browse Source

Tweaked newpilotdialog and pilotwidget

various small tweaks to newpilot dialogs and widgets
Felix Turo 4 years ago
parent
commit
610a6fdfc3

+ 0 - 5
mainwindow.cpp

@@ -139,10 +139,5 @@ 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();
 }

+ 0 - 1
src/database/dbsetup.cpp

@@ -29,7 +29,6 @@ const QString createTablePilots = "CREATE TABLE \"pilots\" ( "
             "\"employeeid\"     TEXT, "
             "\"phone\"          TEXT, "
             "\"email\"          TEXT, "
-            "\"displayname\"	TEXT, "
             "PRIMARY KEY(\"pilot_id\" AUTOINCREMENT)"
             ")";
 

+ 11 - 9
src/experimental/DataBase.cpp

@@ -74,17 +74,19 @@ bool DataBase::remove(Entry entry)
     }
 
     QString statement = "DELETE FROM " + entry.getPosition().tableName +
-            " WHERE _rowid_=" + QString::number(entry.getPosition().rowId);
+            " 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.");
+        emit commitSuccessful();
         return true;
     } else {
         DEB("Unable to delete.");
         DEB("Query: " << statement);
         DEB("Query Error: " << q.lastError().text());
+        emit sqlError(q.lastError(), statement);
         return false;
     }
 }
@@ -96,7 +98,7 @@ bool DataBase::exists(Entry entry)
 
     //Check database for row id
     QString statement = "SELECT COUNT(*) FROM " + entry.getPosition().tableName +
-            " WHERE _rowid_=" + QString::number(entry.getPosition().rowId);
+            " WHERE ROWID=" + QString::number(entry.getPosition().rowId);
     //this returns either 1 or 0 since row ids are unique
     QSqlQuery q(statement);
     q.next();
@@ -117,13 +119,13 @@ bool DataBase::update(Entry updated_entry)
     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("', ");
+            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));
+    statement.append(QLatin1String(" WHERE ROWID=") + QString::number(updated_entry.getPosition().rowId));
 
     DEB("UPDATE QUERY: " << statement);
     QSqlQuery q(statement);
@@ -137,7 +139,7 @@ bool DataBase::update(Entry updated_entry)
         DEB("Unable to commit.");
         DEB("Query: " << statement);
         DEB("Query Error: " << q.lastError().text());
-        emit commitUnsuccessful(q.lastError(), statement);
+        emit sqlError(q.lastError(), statement);
         return false;
     }
 }
@@ -154,7 +156,7 @@ bool DataBase::insert(Entry new_entry)
     statement.chop(2);
     statement += QLatin1String(") VALUES (");
     for (i = data.begin(); i != data.end(); ++i) {
-        statement += QLatin1String("'") + i.value() + QLatin1String("', ");
+        statement += QLatin1String("\"") + i.value() + QLatin1String("\", ");
     }
     statement.chop(2);
     statement += QLatin1String(")");
@@ -170,7 +172,7 @@ bool DataBase::insert(Entry new_entry)
         DEB("Unable to commit.");
         DEB("Query: " << statement);
         DEB("Query Error: " << q.lastError().text());
-        emit commitUnsuccessful(q.lastError(), statement);
+        emit sqlError(q.lastError(), statement);
         return false;
     }
 
@@ -186,7 +188,7 @@ TableData DataBase::getEntryData(DataPosition data_position)
 
     //Check Database for rowId
     QString statement = "SELECT COUNT(*) FROM " + data_position.first
-                      + " WHERE _rowid_=" + QString::number(data_position.second);
+                      + " WHERE ROWID=" + QString::number(data_position.second);
     QSqlQuery check_query(statement);
 
     if (check_query.lastError().type() != QSqlError::NoError) {
@@ -204,7 +206,7 @@ TableData DataBase::getEntryData(DataPosition data_position)
     // Retreive TableData
     DEB("Retreiving data for row id: " << data_position.second);
     statement = "SELECT * FROM " + data_position.first
-              + " WHERE _rowid_=" + QString::number(data_position.second);
+              + " WHERE ROWID=" + QString::number(data_position.second);
 
     QSqlQuery select_query(statement);
     if (select_query.lastError().type() != QSqlError::NoError) {

+ 1 - 4
src/experimental/DataBase.h

@@ -98,10 +98,7 @@ public:
 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);
+    void sqlError(const QSqlError &sqlError, const QString &sqlStatement);
 
 };
 

+ 6 - 6
src/gui/dialogues/newpilot.ui

@@ -15,28 +15,28 @@
   </property>
   <layout class="QGridLayout" name="gridLayout">
    <item row="0" column="0">
-    <widget class="QLabel" name="picfirstnameLabel">
+    <widget class="QLabel" name="piclastnameLabel">
      <property name="text">
-      <string>First Name</string>
+      <string>Last Name</string>
      </property>
     </widget>
    </item>
    <item row="0" column="1">
-    <widget class="QLineEdit" name="picfirstnameLineEdit">
+    <widget class="QLineEdit" name="piclastnameLineEdit">
      <property name="maxLength">
       <number>40</number>
      </property>
     </widget>
    </item>
    <item row="1" column="0">
-    <widget class="QLabel" name="piclastnameLabel">
+    <widget class="QLabel" name="picfirstnameLabel">
      <property name="text">
-      <string>Last Name</string>
+      <string>First Name</string>
      </property>
     </widget>
    </item>
    <item row="1" column="1">
-    <widget class="QLineEdit" name="piclastnameLineEdit">
+    <widget class="QLineEdit" name="picfirstnameLineEdit">
      <property name="maxLength">
       <number>40</number>
      </property>

+ 22 - 8
src/gui/dialogues/newpilotdialog.cpp

@@ -44,8 +44,9 @@ static const auto LASTNAME_VALID = QPair<QString, QRegularExpression> {
 static const auto PHONE_VALID = QPair<QString, QRegularExpression> {
      "phoneLineEdit", QRegularExpression("^[+]{0,1}[0-9\\-\\s]+")};
 static 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")};
+     "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> {
      "companyLineEdit", QRegularExpression("\\w+(\\s|\\-)?(\\w+(\\s|\\-)?)?(\\w+(\\s|\\-)?)?")};
 static const auto EMPLOYEENR_VALID = QPair<QString, QRegularExpression> {
@@ -60,6 +61,8 @@ static const auto LINE_EDIT_VALIDATORS = QVector{
         EMPLOYEENR_VALID
 };
 
+using namespace experimental;
+
 // For creating a new entry
 NewPilotDialog::NewPilotDialog(QWidget *parent) :
     QDialog(parent),
@@ -68,7 +71,6 @@ NewPilotDialog::NewPilotDialog(QWidget *parent) :
     DEB("New NewPilotDialog (newEntry)");
     setup();
 
-    using namespace experimental;
     pilotEntry = PilotEntry();
     ui->piclastnameLineEdit->setFocus();
 }
@@ -80,7 +82,6 @@ NewPilotDialog::NewPilotDialog(int rowId, QWidget *parent) :
     DEB("New NewPilotDialog (editEntry)");
     setup();
 
-    using namespace experimental;
     pilotEntry = DB()->getPilotEntry(rowId);
     DEB("Pilot Entry position: " << pilotEntry.getPosition());
     formFiller();
@@ -114,6 +115,17 @@ void NewPilotDialog::setup()
     completer->setCompletionMode(QCompleter::InlineCompletion);
     completer->setCaseSensitivity(Qt::CaseSensitive);
     ui->companyLineEdit->setCompleter(completer);
+
+    ///[F] moved connecting the slots here because
+    /// - no need to declare the slots public as would be the case if connected in mainwindow
+    /// - only one place where slots are connected vs. several places (mainwindow, pilotswidget),
+    ///   makes it easier to maintain.
+    /// - these signals and slots are specific to this dialog, for communication with
+    ///   other widgets we have the QDialog::accepted() and QDialog::rejected signals.
+    QObject::connect(DB(), &DataBase::commitSuccessful,
+                     this, &NewPilotDialog::onCommitSuccessful);
+    QObject::connect(DB(), &DataBase::sqlError,
+                     this, &NewPilotDialog::onCommitUnsuccessful);
 }
 
 void NewPilotDialog::on_buttonBox_accepted()
@@ -135,8 +147,11 @@ void NewPilotDialog::onCommitSuccessful()
 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());
+    mb.setIcon(QMessageBox::Critical);
+    mb.setText("The following error has ocurred.\n\n"
+               + sqlError.text()
+               + "\n\nYour entry has not been saved.");
+    mb.exec();
 }
 
 void NewPilotDialog::formFiller()
@@ -154,9 +169,8 @@ void NewPilotDialog::formFiller()
 void NewPilotDialog::submitForm()
 {
     DEB("Collecting User Input...");
-    using namespace experimental;
-    TableData new_data;
 
+    TableData new_data;
     auto line_edits = this->findChildren<QLineEdit *>();
     for(auto& le : line_edits) {
         auto key = le->objectName().remove("LineEdit");

+ 2 - 1
src/gui/dialogues/newpilotdialog.h

@@ -41,10 +41,11 @@ public:
     explicit NewPilotDialog(QWidget *parent = nullptr);
     explicit NewPilotDialog(int rowId, QWidget *parent = nullptr);
     ~NewPilotDialog();
+
 private slots:
     void on_buttonBox_accepted();
 
-public slots:
+//public slots:
 
     void onCommitSuccessful();
 

+ 2 - 2
src/gui/widgets/debugwidget.cpp

@@ -45,7 +45,7 @@ void DebugWidget::on_resetDatabasePushButton_clicked()
         dir.mkpath(".");
     // download latest csv
     QStringList templateTables = {"aircraft", "airports", "changelog"};
-    const auto& linkStub = "https://raw.githubusercontent.com/fiffty-50/openpilotlog/devel/assets/database/templates/";
+    const auto& linkStub = "https://raw.githubusercontent.com/fiffty-50/openpilotlog/develop/assets/database/templates/";
     for (const auto& table : templateTables) {
         QEventLoop loop;
         Download* dl = new Download;
@@ -96,7 +96,7 @@ void DebugWidget::on_fillUserDataPushButton_clicked()
         dir.mkpath(".");
     // download latest csv
     QStringList userTables = {"pilots", "tails", "flights"};
-    const auto& linkStub = "https://raw.githubusercontent.com/fiffty-50/openpilotlog/devel/assets/database/templates/sample_";
+    const auto& linkStub = "https://raw.githubusercontent.com/fiffty-50/openpilotlog/develop/assets/database/templates/sample_";
     for (const auto& table : userTables) {
         QEventLoop loop;
         Download* dl = new Download;

+ 86 - 75
src/gui/widgets/pilotswidget.cpp

@@ -19,13 +19,21 @@
 #include "ui_pilotswidget.h"
 #include "debug.h"
 
+using namespace experimental;
 PilotsWidget::PilotsWidget(QWidget *parent) :
     QWidget(parent),
     ui(new Ui::PilotsWidget)
 {
     ui->setupUi(this);
     sortColumn = Settings::read("userdata/pilSortColumn").toInt();
-    refreshModelAndView();         
+
+    setupModelAndView();
+    refreshModelAndView();
+
+    QObject::connect(ui->tableView->selectionModel(), &QItemSelectionModel::selectionChanged,
+                     this, &PilotsWidget::tableView_selectionChanged);
+    QObject::connect(ui->tableView->horizontalHeader(), &QHeaderView::sectionClicked,
+                     this, &PilotsWidget::tableView_headerClicked);
 }
 
 PilotsWidget::~PilotsWidget()
@@ -33,11 +41,44 @@ PilotsWidget::~PilotsWidget()
     delete ui;
 }
 
+void PilotsWidget::setupModelAndView()
+{
+    model->setTable("viewPilots");
+    model->setFilter("ID > 1");//to not allow editing of self, shall be done via settings
+    model->select();
+
+    view = ui->tableView;
+    view->setModel(model);
+    view->setSelectionBehavior(QAbstractItemView::SelectRows);
+    view->setSelectionMode(QAbstractItemView::SingleSelection);
+    view->setEditTriggers(QAbstractItemView::NoEditTriggers);
+    view->horizontalHeader()->setStretchLastSection(QHeaderView::Stretch);
+    view->hideColumn(0);
+    view->setColumnWidth(1, 180);
+    view->setColumnWidth(2, 180);
+    view->verticalHeader()->hide();
+    view->setAlternatingRowColors(true);
+    view->setSortingEnabled(true);
+    view->sortByColumn(sortColumn, Qt::AscendingOrder);
+}
+
+void PilotsWidget::refreshModelAndView()
+{
+    ui->stackedWidget->setCurrentWidget(this->findChild<QWidget*>("welcomePage"));
+    model->select();
+    view->show();
+}
+
+void PilotsWidget::on_searchLineEdit_textChanged(const QString &arg1)
+{
+    model->setFilter("\"" + ui->searchComboBox->currentText() + "\" LIKE \"%" + arg1 + "%\" AND ID > 1");
+}
+
 void PilotsWidget::tableView_selectionChanged()//const QItemSelection &index, const QItemSelection &
 {   
     if (this->findChild<NewPilotDialog*>("NewPilot") != nullptr) {
         DEB("Selection changed. Deleting orphaned dialog.");
-        delete this->findChild<NewPilotDialog*>("NewPilot");
+        delete this->findChild<NewPilotDialog*>();
         /// [F] if the user changes the selection without making any changes,
         /// if(selectedPilots.length() == 1) spawns a new dialog without the
         /// old one being deleted, since neither accept() nor reject() was emitted.
@@ -54,13 +95,10 @@ void PilotsWidget::tableView_selectionChanged()//const QItemSelection &index, co
 
         NewPilotDialog* np = new NewPilotDialog(selectedPilots.first(), this);
         DEB("new dialog: " << np->objectName());
-        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()));
+        QObject::connect(np, &QDialog::accepted,
+                         this, &PilotsWidget::pilot_editing_finished);
+        QObject::connect(np, &QDialog::rejected,
+                         this, &PilotsWidget::pilot_editing_finished);
         np->setWindowFlag(Qt::Widget);
         np->setAttribute(Qt::WA_DeleteOnClose);
         ui->stackedWidget->addWidget(np);
@@ -81,11 +119,6 @@ void PilotsWidget::on_newButton_clicked()
     np->setAttribute(Qt::WA_DeleteOnClose);
     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();
 }
 
@@ -100,77 +133,55 @@ void PilotsWidget::on_deletePushButton_clicked()
         auto mb = QMessageBox(this);
         mb.setText("Deleting multiple entries is currently not supported");
         mb.exec();
-        // to do: for (const auto& selectedPilot : selectedPilots) { do batchDelete }
+        /// [F] to do: for (const auto& row_id : selectedPilots) { do batchDelete }
+        /// I am not sure if enabling this functionality for this widget is a good idea.
+        /// On the one hand, deleting many entries could be useful in a scenario where
+        /// for example, the user has changed airlines and does not want to have his 'old'
+        /// colleagues polluting his logbook anymore.
+        /// On the other hand we could run into issues with foreign key constraints on the
+        /// flights table (see on_delete_unsuccessful) below
 
     } else if (selectedPilots.length() == 1) {
-        using namespace experimental;
         auto entry = DB()->getPilotEntry(selectedPilots.first());
-        if (!DB()->remove(entry)) {
-            QVector<QString> columns = {"doft","dept","dest"};
-            QVector<QString> details = Db::multiSelect(columns, "flights", "pic",
-                                                       QString::number(selectedPilots.first()), Db::exactMatch);
-            auto mb = QMessageBox(this);
-            QString message = "\nUnable to delete.\n\n";
-            // [F] to do: create unsuccessful delete signal and include error info
-            if(!details.isEmpty()){
-                message += "This is most likely the case because a flight exists with the Pilot "
-                           "you are trying to delete. You have to change or remove this flight "
-                           "before being able to remove this pilot from the database.\n\n"
-                           "The following flight(s) with this pilot have been found:\n\n";
-                auto space = QLatin1Char(' ');
-                for(int i = 0; i <= 30 && i <=details.length()-3; i+=3){
-                    message += details[i] + space
-                            + details[i+1] + space
-                            + details[i+2] + QLatin1String("\n");
-                }
-            }
-            mb.setText(message);
-            mb.setIcon(QMessageBox::Critical);
-            mb.exec();
+        DB()->remove(entry);
         }
-    }
-
     refreshModelAndView();
 
 }
 
-void PilotsWidget::pilot_editing_finished()
+void PilotsWidget::on_deleteUnsuccessful()
 {
-    refreshModelAndView();
-}
-
-void PilotsWidget::refreshModelAndView()
-{
-    ui->stackedWidget->setCurrentWidget(this->findChild<QWidget*>("welcomePage"));
-
-    model->setTable("viewPilots");
-    model->setFilter("ID > 1");//to not allow editing of self, shall be done via settings
-    model->select();
-
-    QTableView *view = ui->tableView;
-    view->setModel(model);
-    view->setSelectionBehavior(QAbstractItemView::SelectRows);
-    view->setSelectionMode(QAbstractItemView::SingleSelection);
-    view->setEditTriggers(QAbstractItemView::NoEditTriggers);
-    view->horizontalHeader()->setStretchLastSection(QHeaderView::Stretch);
-    view->hideColumn(0);
-    view->setColumnWidth(1, 180);
-    view->setColumnWidth(2, 180);
-    view->verticalHeader()->hide();
-    view->setAlternatingRowColors(true);
-    view->setSortingEnabled(true);
-
-    view->sortByColumn(sortColumn, Qt::AscendingOrder);
-
-    view->show();
-
-    connect(ui->tableView->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)),
-            this, SLOT(tableView_selectionChanged()));
-    connect(ui->tableView->horizontalHeader(), SIGNAL(sectionClicked(int)),
-            this, SLOT(tableView_headerClicked(int)));
+    /// [F] to do: migrate to new DB class
+    ///
+    /// This query should normally only fail because of a foreign key constraint,
+    /// i.e. a flight exists with this pilot. So we need to make the user aware
+    /// of this and need to display some flight information regarding the flight
+    /// causing the constraint.
+    ///
+    /// The information that needs to be displayed could be extracted from
+    /// a FlightEntry.
+    QVector<QString> columns = {"doft","dept","dest"};
+    QVector<QString> details = Db::multiSelect(columns, "flights", "pic",
+                                               QString::number(selectedPilots.first()), Db::exactMatch);
+    auto mb = QMessageBox(this);
+    QString message = "\nUnable to delete.\n\n";
+    if(!details.isEmpty()){
+        message += "This is most likely the case because a flight exists with the Pilot "
+                   "you are trying to delete. You have to change or remove this flight "
+                   "before being able to remove this pilot from the database.\n\n"
+                   "The following flight(s) with this pilot have been found:\n\n";
+        for(int i = 0; i <= 30 && i <=details.length()-3; i+=3){
+            message += details[i] + QLatin1Char(' ')
+                    + details[i+1] + QLatin1Char(' ')
+                    + details[i+2] + QLatin1String("\n");
+        }
+    }
+    mb.setText(message);
+    mb.setIcon(QMessageBox::Critical);
+    mb.exec();
 }
 
-void PilotsWidget::on_searchLineEdit_textChanged(const QString &arg1)
+void PilotsWidget::pilot_editing_finished()
 {
-    model->setFilter("\"" + ui->searchComboBox->currentText() + "\" LIKE \"%" + arg1 + "%\" AND ID > 1");
+    refreshModelAndView();
 }

+ 8 - 1
src/gui/widgets/pilotswidget.h

@@ -23,6 +23,7 @@
 #include <QSqlTableModel>
 #include <QDebug>
 #include <QLabel>
+#include <QTableView>
 #include "src/classes/settings.h"
 #include "src/classes/pilot.h"
 #include "src/gui/dialogues/newpilotdialog.h"
@@ -48,6 +49,8 @@ private slots:
 
     void on_deletePushButton_clicked();
 
+    void on_deleteUnsuccessful();
+
     void pilot_editing_finished();
 
     void on_searchLineEdit_textChanged(const QString &arg1);
@@ -55,12 +58,16 @@ private slots:
 private:
     Ui::PilotsWidget *ui;
 
-    QSqlTableModel *model = new QSqlTableModel;
+    QSqlTableModel *model = new QSqlTableModel(this);
+
+    QTableView *view = new QTableView(this);
 
     qint32 sortColumn;
 
     QVector<qint32> selectedPilots;
 
+    void setupModelAndView();
+
     void refreshModelAndView();
 };