Browse Source

Added error information to widgets

Added error boxes and moved database interface to new api in pilotswidget and aircraftwidget.
Felix Turo 4 years ago
parent
commit
e5fa5a442d

+ 40 - 2
src/experimental/adatabase.cpp

@@ -103,7 +103,7 @@ bool ADatabase::remove(AEntry entry)
     }
 
     QString statement = "DELETE FROM " + entry.getPosition().tableName +
-            " WHERE ROWID?";
+            " WHERE ROWID=?";
 
     QSqlQuery query;
     query.prepare(statement);
@@ -138,9 +138,10 @@ bool ADatabase::removeMany(QList<DataPosition> data_position_list)
             errorCount++;
         }
         QString statement = "DELETE FROM " + data_position.first +
-                " WHERE ROWID=" + QString::number(data_position.second);
+                " WHERE ROWID=?";
 
         query.prepare(statement);
+        query.addBindValue(data_position.second);
         query.exec();
 
         if (!(query.lastError().type() == QSqlError::NoError))
@@ -523,6 +524,43 @@ int ADatabase::getLastEntry(ADatabaseTarget target)
     }
 }
 
+QList<int> ADatabase::getForeignKeyConstraints(int foreign_row_id, ADatabaseTarget target)
+{
+    QString statement = "SELECT ROWID FROM flights WHERE ";
+
+    switch (target) {
+    case ADatabaseTarget::pilots:
+        statement.append("pic=?");
+        break;
+    case ADatabaseTarget::tails:
+        statement.append("acft=?");
+        break;
+    default:
+        DEB("Not a valid target for this function.");
+        return QList<int>();
+        break;
+    }
+
+    QSqlQuery query;
+    query.prepare(statement);
+    query.addBindValue(foreign_row_id);
+    query.exec();
+
+    if (!query.isActive()) {
+        lastError = query.lastError().text();
+        DEB("Error");
+        DEB(statement);
+        DEB(query.lastError().text());
+        return QList<int>();
+    }
+
+    QList<int> row_ids;
+    while (query.next()) {
+        row_ids.append(query.value(0).toInt());
+    }
+    return row_ids;
+}
+
 QVector<QString> ADatabase::customQuery(QString statement, int return_values)
 {
     QSqlQuery query(statement);

+ 9 - 2
src/experimental/adatabase.h

@@ -195,19 +195,26 @@ public:
     /*!
      * \brief getCompletionList returns a QStringList of values for a
      * QCompleter based on database values
-     * \return
      */
     const QStringList getCompletionList(ADatabaseTarget);
 
     /*!
      * \brief returns a QMap<QString, int> of a human-readable database value and
      * its row id. Used in the Dialogs to map user input to unique database entries.
-     * \return
      */
     const QMap<QString, int> getIdMap(ADatabaseTarget);
 
+    /*!
+     * \brief returns the ROWID for the newest entry in the respective database.
+     */
     int getLastEntry(ADatabaseTarget);
 
+    /*!
+     * \brief returns a list of ROWID's in the flights table for which foreign key constraints
+     * exist.
+     */
+    QList<int> getForeignKeyConstraints(int foreign_row_id, ADatabaseTarget target);
+
 signals:
     /*!
      * \brief updated is emitted whenever the database contents have been updated.

+ 10 - 1
src/experimental/apilotentry.cpp

@@ -24,11 +24,20 @@ APilotEntry::APilotEntry()
 {}
 
 APilotEntry::APilotEntry(int row_id)
-    : AEntry::AEntry(DataPosition(QLatin1String("pilots"), row_id))
+    : AEntry::AEntry(DataPosition(QStringLiteral("pilots"), row_id))
 {}
 
 APilotEntry::APilotEntry(TableData table_data)
     : AEntry::AEntry(DEFAULT_PILOT_POSITION, table_data)
 {}
 
+const QString APilotEntry::name()
+{
+    if (tableData.isEmpty())
+        return QLatin1String("");
+
+    return tableData.value(QStringLiteral("piclastname")).toString() + ','
+           +tableData.value(QStringLiteral("picfirstname")).toString().left(1) + '.';
+}
+
 } // namespace experimental

+ 2 - 0
src/experimental/apilotentry.h

@@ -30,6 +30,8 @@ public:
     APilotEntry& operator=(const APilotEntry& pe) = default;
     APilotEntry(int row_id);
     APilotEntry(TableData table_data);
+
+    const QString name();
 };
 
 } // namespace experimental

+ 57 - 43
src/gui/widgets/aircraftwidget.cpp

@@ -22,6 +22,8 @@
 #define DEB(expr) \
     qDebug() << __PRETTY_FUNCTION__ << "\t" << expr
 
+using namespace experimental;
+
 AircraftWidget::AircraftWidget(QWidget *parent) :
     QWidget(parent),
     ui(new Ui::AircraftWidget)
@@ -81,51 +83,63 @@ void AircraftWidget::setupModelAndView()
  */
 void AircraftWidget::on_deleteButton_clicked()
 {
-    if (selectedTails.length() > 0) {
-        for(const auto& selectedTail : selectedTails){
-            auto ac = Aircraft(selectedTail);
-            auto& warningMsg = "Deleting Aircraft with registration:<br><center><b>"
-                    + ac.data.value("registration")
-                    + "<br></center></b>Do you want to proceed?";
-            QMessageBox confirm;
-            confirm.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
-            confirm.setDefaultButton(QMessageBox::No);
-            confirm.setIcon(QMessageBox::Question);
-            confirm.setWindowTitle("Delete Aircraft");
-            confirm.setText(warningMsg);
-            int reply = confirm.exec();
-            if(reply == QMessageBox::Yes) {
-                if(!ac.remove()) {
-                    QVector<QString> columns = {"doft","dept","dest"};
-                    QVector<QString> details = Db::multiSelect(columns, "flights", "acft",
-                                                               QString::number(selectedTail), Db::exactMatch);
-                    auto mb = QMessageBox(this);
-                    QString message = "\nUnable to delete. The following error has ocurred:\n\n";
-                    if(!details.isEmpty()){
-                        message += ac.error + QLatin1String("\n\n");
-                        message += "This is most likely the case because a flight exists with the aircaft "
-                                   "you are trying to delete. In order to maintain database integrity, you have to\n"
-                                   "change or remove this flight before being able to remove this aircraft.\n\n"
-                                   "The following flight(s) with this tail 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.show();
-                }
-            }
-        }
-        model->select();
-    } else {
+    if (selectedTails.length() == 0) {
+        auto mb = QMessageBox(this);
+        mb.setText(QStringLiteral("No Aircraft selected."));
+        mb.exec();
+
+    } else if (selectedTails.length() > 1) {
         auto mb = QMessageBox(this);
-        mb.setText("No aircraft selected.");
-        mb.show();
+        mb.setText(QStringLiteral("Deleting multiple entries is currently not supported"));
+        mb.exec();
+        /// [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.
+        /// I think batch-editing should be implemented at some point, but batch-deleting should not.
+
+    } else if (selectedTails.length() == 1) {
+        auto entry = aDB()->getTailEntry(selectedTails.first());
+        auto message_box = QMessageBox(this);
+        QString message = "You are deleting the following aircraft:<br><br><b><tt>";
+        message.append(entry.registration() + QStringLiteral(" - (") + entry.type() + ')');
+        message.append(QStringLiteral("</b></tt><br><br>Are you sure?"));
+        message_box.setText(message);
+        message_box.exec();
+        if(!aDB()->remove(entry))
+            onDeleteUnsuccessful();
+        }
+    model->select();
+}
+
+void AircraftWidget::onDeleteUnsuccessful()
+{
+    /// [F]: To do: Some logic to display a warning if too many entries exists, so that
+    /// the messagebox doesn't grow too tall.
+    QList<int> foreign_key_constraints = aDB()->getForeignKeyConstraints(selectedTails.first(), ADatabaseTarget::tails);
+    QList<AFlightEntry> constrained_flights;
+    for (const auto &row_id : foreign_key_constraints) {
+        constrained_flights.append(aDB()->getFlightEntry(row_id));
+    }
+
+    QString message = "<br>Unable to delete.<br><br>";
+    if(!constrained_flights.isEmpty()){
+        message.append(QStringLiteral("This is most likely the case because a flight exists with the aircraft "
+                   "you are trying to delete.<br>"
+                   "The following flight(s) with this aircraft have been found:<br><br><br><b><tt>"));
+        for (auto &flight : constrained_flights) {
+            message.append(flight.summary() + QStringLiteral("&nbsp;&nbsp;&nbsp;&nbsp;<br>"));
+        }
     }
+    message.append(QStringLiteral("</b></tt><br><br>You have to change or remove the conflicting flight(s) "
+                                  "before removing this aircraft from the database.<br><br>"));
+    QMessageBox message_box(this);
+    message_box.setText(message);
+    message_box.setIcon(QMessageBox::Critical);
+    message_box.exec();
 }
 
 void AircraftWidget::on_newAircraftButton_clicked()

+ 7 - 2
src/gui/widgets/aircraftwidget.h

@@ -26,8 +26,11 @@
 
 #include "src/classes/asettings.h"
 #include "src/gui/dialogues/newtaildialog.h"
-#include "src/classes/aircraft.h"
-#include "src/database/db.h"
+//#include "src/classes/aircraft.h"
+//#include "src/database/db.h"
+#include "src/experimental/adatabase.h"
+#include "src/experimental/atailentry.h"
+#include "src/experimental/aflightentry.h"
 
 
 namespace Ui {
@@ -72,6 +75,8 @@ private:
     QVector<qint32> selectedTails;
 
     void setupModelAndView();
+
+    void onDeleteUnsuccessful();
 };
 
 #endif // AIRCRAFTWIDGET_H

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

@@ -228,7 +228,7 @@ void LogbookWidget::on_deleteFlightPushButton_clicked()
             for (auto& flight : flights_list) {
                 DEB("Deleting flight: " << flight.summary());
                 if(!aDB()->remove(flight)) {
-                    messageBox->setText(" Error "); // [F]: To Do: error info
+                    messageBox->setText(aDB()->lastError.text());
                     messageBox->exec();
                     return;
                 }
@@ -255,7 +255,7 @@ void LogbookWidget::on_deleteFlightPushButton_clicked()
             }
             if (!aDB()->removeMany(selected_flights)) {
 
-                messageBox->setText(" Error "); // [F]: To Do: error info
+                messageBox->setText(aDB()->lastError.text()); // [F]: To Do: error info
                 messageBox->exec();
                 return;
             }

+ 30 - 28
src/gui/widgets/pilotswidget.cpp

@@ -150,41 +150,43 @@ void PilotsWidget::on_deletePilotButton_clicked()
 
     } else if (selectedPilots.length() == 1) {
         auto entry = aDB()->getPilotEntry(selectedPilots.first());
-        aDB()->remove(entry);
+        auto message_box = QMessageBox(this);
+        QString message = "You are deleting the following pilot:<br><br><b><tt>";
+        message.append(entry.name());
+        message.append(QStringLiteral("</b></tt><br><br>Are you sure?"));
+        message_box.setText(message);
+        message_box.exec();
+        if(!aDB()->remove(entry))
+            onDeleteUnsuccessful();
         }
     model->select();
 }
 
-void PilotsWidget::on_deleteUnsuccessful()
+void PilotsWidget::onDeleteUnsuccessful()
 {
-    /// [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");
+    /// [F]: To do: Some logic to display a warning if too many entries exists, so that
+    /// the messagebox doesn't grow too tall.
+    QList<int> foreign_key_constraints = aDB()->getForeignKeyConstraints(selectedPilots.first(), ADatabaseTarget::pilots);
+    QList<AFlightEntry> constrained_flights;
+    for (const auto &row_id : foreign_key_constraints) {
+        constrained_flights.append(aDB()->getFlightEntry(row_id));
+    }
+
+    QString message = "<br>Unable to delete.<br><br>";
+    if(!constrained_flights.isEmpty()){
+        message.append(QStringLiteral("This is most likely the case because a flight exists with the Pilot "
+                   "you are trying to delete as PIC.<br>"
+                   "The following flight(s) with this pilot have been found:<br><br><br><b><tt>"));
+        for (auto &flight : constrained_flights) {
+            message.append(flight.summary() + QStringLiteral("&nbsp;&nbsp;&nbsp;&nbsp;<br>"));
         }
     }
-    mb.setText(message);
-    mb.setIcon(QMessageBox::Critical);
-    mb.exec();
+    message.append(QStringLiteral("</b></tt><br><br>You have to change or remove the conflicting flight(s) "
+                                  "before removing this pilot from the database.<br><br>"));
+    QMessageBox message_box(this);
+    message_box.setText(message);
+    message_box.setIcon(QMessageBox::Critical);
+    message_box.exec();
 }
 
 void PilotsWidget::pilot_editing_finished()

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

@@ -44,7 +44,7 @@ private slots:
     void tableView_headerClicked(int);
     void on_newPilotButton_clicked();
     void on_deletePilotButton_clicked();
-    void on_deleteUnsuccessful();
+    void onDeleteUnsuccessful();
     void pilot_editing_finished();
     void on_pilotSearchLineEdit_textChanged(const QString &arg1);