Bläddra i källkod

Merge pull request #35 from fiffty-50/develop-cleanup

Testing pull request creation from gitkraken with some minor commits
Felix Turowsky 4 år sedan
förälder
incheckning
a757a38768

BIN
assets/database/logbook.db


+ 13 - 2
main.cpp

@@ -18,11 +18,13 @@
 #include "mainwindow.h"
 #include "src/gui/dialogues/firstrundialog.h"
 #include "src/classes/arunguard.h"
+#include "src/experimental/adatabase.h"
+#include "src/classes/asettings.h"
 #include <QApplication>
 #include <QProcess>
 #include <QSettings>
 #include <QFileInfo>
-#include "src/experimental/adatabase.h"
+
 
 const auto DATA_DIR = QLatin1String("data");
 /*!
@@ -38,6 +40,10 @@ bool setup()
     QString   settingsfile = QCoreApplication::applicationName() + QLatin1String(".ini");
     QFileInfo check_file(settingspath,settingsfile);
 
+    QSettings settings;
+    settings.setValue("setup/touch", true);
+    settings.sync();
+
     return check_file.exists() && check_file.isFile();
 };
 
@@ -51,16 +57,21 @@ int main(int argc, char *argv[])
     QSettings::setDefaultFormat(QSettings::IniFormat);
     QSettings settings;
 
-//    Db::connect();
     experimental::aDB()->connect();
 
     QApplication openPilotLog(argc, argv);
     if(!setup()){
+        DEB("error creating required directories");
+        return 0;
+    }
+
+    if (!ASettings::read("setup/setup_complete").toBool()) {
         FirstRunDialog dialog;
         dialog.exec();
     }
 
 
+
     //Theming
     int selectedtheme = settings.value("main/theme").toInt();
     QDir::setCurrent("/themes");

+ 3 - 3
mainwindow.cpp

@@ -132,11 +132,11 @@ void MainWindow::on_actionDebug_triggered()
 
 void MainWindow::connectWidgets()
 {
-    QObject::connect(experimental::aDB(), &experimental::ADataBase::dataBaseUpdated,
+    QObject::connect(experimental::aDB(), &experimental::ADatabase::dataBaseUpdated,
                      logbookWidget, &LogbookWidget::onDisplayModel_dataBaseUpdated);
-    QObject::connect(experimental::aDB(), &experimental::ADataBase::dataBaseUpdated,
+    QObject::connect(experimental::aDB(), &experimental::ADatabase::dataBaseUpdated,
                      pilotsWidget, &PilotsWidget::onDisplayModel_dataBaseUpdated);
-    QObject::connect(experimental::aDB(), &experimental::ADataBase::dataBaseUpdated,
+    QObject::connect(experimental::aDB(), &experimental::ADatabase::dataBaseUpdated,
                      aircraftWidget, &AircraftWidget::onDisplayModel_dataBaseUpdated);
 
     QObject::connect(settingsWidget, &SettingsWidget::viewSelectionChanged,

+ 96 - 49
src/experimental/adatabase.cpp

@@ -20,16 +20,25 @@
 
 namespace experimental {
 
-ADataBase* ADataBase::instance = nullptr;
+ADatabaseError::ADatabaseError(QString msg_)
+    : QSqlError::QSqlError(msg_)
+{}
 
-ADataBase* ADataBase::getInstance()
+QString ADatabaseError::text() const
+{
+    return "Database Error: " + QSqlError::text();
+}
+
+ADatabase* ADatabase::instance = nullptr;
+
+ADatabase* ADatabase::getInstance()
 {
     if(!instance)
-        instance = new ADataBase();
+        instance = new ADatabase();
     return instance;
 }
 
-bool ADataBase::connect()
+bool ADatabase::connect()
 {
     const QString driver("QSQLITE");
 
@@ -63,20 +72,20 @@ bool ADataBase::connect()
     return true;
 }
 
-void ADataBase::disconnect()
+void ADatabase::disconnect()
 {
-    auto db = ADataBase::database();
+    auto db = ADatabase::database();
     db.close();
     db.removeDatabase(db.connectionName());
     DEB("Database connection closed.");
 }
 
-QSqlDatabase ADataBase::database()
+QSqlDatabase ADatabase::database()
 {
     return QSqlDatabase::database("qt_sql_default_connection");
 }
 
-bool ADataBase::commit(AEntry entry)
+bool ADatabase::commit(AEntry entry)
 {
     if (exists(entry)) {
         return update(entry);
@@ -85,16 +94,16 @@ bool ADataBase::commit(AEntry entry)
     }
 }
 
-bool ADataBase::remove(AEntry entry)
+bool ADatabase::remove(AEntry entry)
 {
     if (!exists(entry)) {
         DEB("Error: Database entry not found.");
-        lastError = "Database Error: Database entry not found.";
+        lastError = ADatabaseError("Database entry not found.");
         return false;
     }
 
     QString statement = "DELETE FROM " + entry.getPosition().tableName +
-            " WHERE ROWID?";
+            " WHERE ROWID=?";
 
     QSqlQuery query;
     query.prepare(statement);
@@ -116,22 +125,23 @@ bool ADataBase::remove(AEntry entry)
     }
 }
 
-bool ADataBase::removeMany(QList<DataPosition> data_position_list)
+bool ADatabase::removeMany(QList<DataPosition> data_position_list)
 {
     int errorCount = 0;
     QSqlQuery query;
     query.prepare("BEGIN EXCLUSIVE TRANSACTION");
     query.exec();
 
-    for (const auto data_position : data_position_list) {
+    for (const auto& data_position : data_position_list) {
         if (!exists(data_position)) {
-            lastError = "Database Error: Database entry not found.";
+            lastError = ADatabaseError("Database entry not found.");
             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))
@@ -157,7 +167,7 @@ bool ADataBase::removeMany(QList<DataPosition> data_position_list)
     }
 }
 
-bool ADataBase::exists(AEntry entry)
+bool ADatabase::exists(AEntry entry)
 {
     if(entry.getPosition().second == 0)
         return false;
@@ -182,12 +192,12 @@ bool ADataBase::exists(AEntry entry)
         return true;
     } else {
         DEB("Database entry not found.");
-        lastError = "Database Error: Database entry not found.";
+        lastError = ADatabaseError("Database entry not found.");
         return false;
     }
 }
 
-bool ADataBase::exists(DataPosition data_position)
+bool ADatabase::exists(DataPosition data_position)
 {
     if(data_position.second == 0)
         return false;
@@ -211,13 +221,13 @@ bool ADataBase::exists(DataPosition data_position)
         return true;
     } else {
         DEB("No entry exists at DataPosition: " << data_position);
-        lastError = "Database Error: Database entry not found.";
+        lastError = ADatabaseError("Database entry not found.");
         return false;
     }
 }
 
 
-bool ADataBase::update(AEntry updated_entry)
+bool ADatabase::update(AEntry updated_entry)
 {
     auto data = updated_entry.getData();
     QString statement = "UPDATE " + updated_entry.getPosition().tableName + " SET ";
@@ -253,7 +263,7 @@ bool ADataBase::update(AEntry updated_entry)
     }
 }
 
-bool ADataBase::insert(AEntry new_entry)
+bool ADatabase::insert(AEntry new_entry)
 {
     auto data = new_entry.getData();
     QString statement = "INSERT INTO " + new_entry.getPosition().tableName + QLatin1String(" (");
@@ -299,7 +309,7 @@ bool ADataBase::insert(AEntry new_entry)
 
 }
 
-TableData ADataBase::getEntryData(DataPosition data_position)
+TableData ADatabase::getEntryData(DataPosition data_position)
 {
     // check table exists
     if (!tableNames.contains(data_position.first)) {
@@ -326,7 +336,7 @@ TableData ADataBase::getEntryData(DataPosition data_position)
     check_query.next();
     if (check_query.value(0).toInt() == 0) {
         DEB("No Entry found for row id: " << data_position.second );
-        lastError = "Database Error: Database entry not found.";
+        lastError = ADatabaseError("Database entry not found.");
         return TableData();
     }
 
@@ -356,61 +366,61 @@ TableData ADataBase::getEntryData(DataPosition data_position)
     return entry_data;
 }
 
-AEntry ADataBase::getEntry(DataPosition data_position)
+AEntry ADatabase::getEntry(DataPosition data_position)
 {
     AEntry entry(data_position);
     entry.setData(getEntryData(data_position));
     return entry;
 }
 
-APilotEntry ADataBase::getPilotEntry(RowId row_id)
+APilotEntry ADatabase::getPilotEntry(RowId row_id)
 {
     APilotEntry pilot_entry(row_id);
     pilot_entry.setData(getEntryData(pilot_entry.getPosition()));
     return pilot_entry;
 }
 
-ATailEntry ADataBase::getTailEntry(RowId row_id)
+ATailEntry ADatabase::getTailEntry(RowId row_id)
 {
     ATailEntry tail_entry(row_id);
     tail_entry.setData(getEntryData(tail_entry.getPosition()));
     return tail_entry;
 }
 
-AAircraftEntry ADataBase::getAircraftEntry(RowId row_id)
+AAircraftEntry ADatabase::getAircraftEntry(RowId row_id)
 {
     AAircraftEntry aircraft_entry(row_id);
     aircraft_entry.setData(getEntryData(aircraft_entry.getPosition()));
     return aircraft_entry;
 }
 
-AFlightEntry ADataBase::getFlightEntry(RowId row_id)
+AFlightEntry ADatabase::getFlightEntry(RowId row_id)
 {
     AFlightEntry flight_entry(row_id);
     flight_entry.setData(getEntryData(flight_entry.getPosition()));
     return flight_entry;
 }
 
-const QStringList ADataBase::getCompletionList(ADataBase::DatabaseTarget target)
+const QStringList ADatabase::getCompletionList(ADatabaseTarget target)
 {
     QString statement;
 
     switch (target) {
-    case pilots:
+    case ADatabaseTarget::pilots:
         statement.append("SELECT piclastname||', '||picfirstname FROM pilots");
         break;
-    case aircraft:
+    case ADatabaseTarget::aircraft:
         statement.append("SELECT make||' '||model FROM aircraft WHERE model IS NOT NULL "
                          "UNION "
                          "SELECT make||' '||model||'-'||variant FROM aircraft WHERE variant IS NOT NULL");
         break;
-    case airport_identifier_all:
+    case ADatabaseTarget::airport_identifier_all:
         statement.append("SELECT icao FROM airports UNION SELECT iata FROM airports");
         break;
-    case registrations:
+    case ADatabaseTarget::registrations:
         statement.append("SELECT registration FROM tails");
         break;
-    case companies:
+    case ADatabaseTarget::companies:
         statement.append("SELECT company FROM pilots");
         break;
     default:
@@ -439,29 +449,29 @@ const QStringList ADataBase::getCompletionList(ADataBase::DatabaseTarget target)
     return completer_list;
 }
 
-const QMap<QString, int> ADataBase::getIdMap(ADataBase::DatabaseTarget target)
+const QMap<QString, int> ADatabase::getIdMap(ADatabaseTarget target)
 {
     QString statement;
 
     switch (target) {
-    case pilots:
+    case ADatabaseTarget::pilots:
         statement.append("SELECT ROWID, piclastname||', '||picfirstname FROM pilots");
         break;
-    case aircraft:
+    case ADatabaseTarget::aircraft:
         statement.append("SELECT ROWID, make||' '||model FROM aircraft WHERE model IS NOT NULL "
                          "UNION "
                          "SELECT ROWID, make||' '||model||'-'||variant FROM aircraft WHERE variant IS NOT NULL");
         break;
-    case airport_identifier_icao:
+    case ADatabaseTarget::airport_identifier_icao:
         statement.append("SELECT ROWID, icao FROM airports");
         break;
-    case airport_identifier_iata:
+    case ADatabaseTarget::airport_identifier_iata:
         statement.append("SELECT ROWID, iata FROM airports WHERE iata NOT NULL");
         break;
-    case airport_names:
+    case ADatabaseTarget::airport_names:
         statement.append("SELECT ROWID, name FROM airports");
         break;
-    case tails:
+    case ADatabaseTarget::tails:
         statement.append("SELECT ROWID, registration FROM tails");
         break;
     default:
@@ -486,19 +496,19 @@ const QMap<QString, int> ADataBase::getIdMap(ADataBase::DatabaseTarget target)
     }
 }
 
-int ADataBase::getLastEntry(ADataBase::DatabaseTarget target)
+int ADatabase::getLastEntry(ADatabaseTarget target)
 {
     QString statement = "SELECT MAX(ROWID) FROM ";
 
     switch (target) {
-    case pilots:
+    case ADatabaseTarget::pilots:
         statement.append(DB_TABLE_PILOTS);
         break;
-    case aircraft:
+    case ADatabaseTarget::aircraft:
         statement.append(DB_TABLE_AIRCRAFT);
         break;
-    case tails:
-        statement.append(DB_TABLE_AIRCRAFT);
+    case ADatabaseTarget::tails:
+        statement.append(DB_TABLE_TAILS);
         break;
     default:
         DEB("Not a valid completer target for this function.");
@@ -508,13 +518,50 @@ int ADataBase::getLastEntry(ADataBase::DatabaseTarget target)
     if (query.first()) {
         return query.value(0).toInt();
     } else {
-        lastError = "Database Error: Database entry not found.";
+        lastError = ADatabaseError("Database entry not found.");
         DEB("No entry found.");
         return 0;
     }
 }
 
-QVector<QString> ADataBase::customQuery(QString statement, int return_values)
+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);
     query.exec();
@@ -540,6 +587,6 @@ QVector<QString> ADataBase::customQuery(QString statement, int return_values)
     }
 }
 
-ADataBase* aDB() { return ADataBase::getInstance(); }
+ADatabase* aDB() { return ADatabase::getInstance(); }
 
 }// namespace experimental

+ 53 - 19
src/experimental/adatabase.h

@@ -35,29 +35,56 @@
 
 namespace experimental {
 
+
+// [G]: Suspicious documentation -,O
+/*!
+ * \brief The DBTarget enum provides the items for which QCompleter
+ * completion lists are provided from the database.
+ */
+enum class ADatabaseTarget
+{
+    airport_identifier_icao,
+    airport_identifier_iata,
+    airport_identifier_all,
+    airport_names,
+    pilots,
+    registrations,
+    aircraft,
+    companies,
+    tails
+};
+
+// [G]: This is how we should handle custom "events" in the program.
+// In this case a custom error doesnt need to be built from scratch.
+// Find the type of error you want and extend it with a few tweaks.
+/*!
+ * \brief Custom Database Error derived from QSqlError.
+ * Extends text() adding "Database Error: " before the text.
+ */
+class ADatabaseError : public QSqlError {
+public:
+  ADatabaseError() = default;
+  ADatabaseError(QString msg);
+  QString text() const;
+};
+
 /*!
  * \brief The DB class encapsulates the SQL database by providing fast access
  * to hot database data.
  */
-class ADataBase : public QObject {
+class ADatabase : public QObject {
     Q_OBJECT
 private:
     TableNames tableNames;
     TableColumns tableColumns;
-    static ADataBase* instance;
-    ADataBase() = default;
+    static ADatabase* instance;
+    ADatabase() = default;
 public:
     // Ensure DB is not copiable or assignable
-    ADataBase(const ADataBase&) = delete;
-    void operator=(const ADataBase&) = delete;
-    static ADataBase* getInstance();
-    QString lastError;
-
-    /*!
-     * \brief The CompleterTarget enum provides the items for which QCompleter
-     * completion lists are provided from the database.
-     */
-    enum DatabaseTarget {airport_identifier_icao, airport_identifier_iata, airport_identifier_all, airport_names, pilots, registrations, aircraft, companies, tails};
+    ADatabase(const ADatabase&) = delete;
+    void operator=(const ADatabase&) = delete;
+    static ADatabase* getInstance();
+    ADatabaseError lastError;
 
     /*!
      * \brief Connect to the database and populate database information.
@@ -168,18 +195,25 @@ public:
     /*!
      * \brief getCompletionList returns a QStringList of values for a
      * QCompleter based on database values
-     * \return
      */
-    const QStringList getCompletionList(DatabaseTarget);
+    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(DatabaseTarget);
+    const QMap<QString, int> getIdMap(ADatabaseTarget);
 
-    int getLastEntry(DatabaseTarget);
+    /*!
+     * \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:
     /*!
@@ -198,7 +232,7 @@ signals:
  * Write this:
  * aDB()->commit(...)
  */
-ADataBase* aDB();
+ADatabase* aDB();
 
 }  // namespace experimental
 

+ 5 - 0
src/experimental/aentry.h

@@ -31,6 +31,10 @@ 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
+/// [F]: In the way we are using the entries atm, we do access and edit the
+/// TableData quite frequently. Maybe the data could be public, but the
+/// position private? Except for creating a new entry, the position should
+/// never really be changed.
 /*!
  * \brief The Entry class encapsulates table metadata(table name, row id)
  *  and data for new and existing entries in the database to operate on.
@@ -38,6 +42,7 @@ namespace experimental {
 class AEntry {
 protected:
     DataPosition position;
+public:
     TableData tableData;
 public:
     AEntry() = delete; // Demand specificity from default constructor

+ 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

+ 102 - 42
src/gui/dialogues/firstrundialog.cpp

@@ -1,6 +1,10 @@
 #include "firstrundialog.h"
 #include "ui_firstrundialog.h"
 
+const auto TEMPLATE_URL = QLatin1String("https://raw.githubusercontent.com/fiffty-50/openpilotlog/develop/assets/database/templates/");
+
+using namespace experimental;
+
 FirstRunDialog::FirstRunDialog(QWidget *parent) :
     QDialog(parent),
     ui(new Ui::FirstRunDialog)
@@ -52,67 +56,123 @@ void FirstRunDialog::on_finishButton_clicked()
         mb->setText("You have to enter a valid first and last name for the logbook.");
         mb->show();
     } else {
-        ASettings::write("userdata/piclastname",ui->piclastnameLineEdit->text());
-        ASettings::write("userdata/picfirstname",ui->picfirstnameLineEdit->text());
-        ASettings::write("userdata/employeeid",ui->employeeidLineEdit->text());
-        ASettings::write("userdata/phone",ui->phoneLineEdit->text());
-        ASettings::write("userdata/email",ui->emailLineEdit->text());
+        ASettings::write("userdata/piclastname", ui->piclastnameLineEdit->text());
+        ASettings::write("userdata/picfirstname", ui->picfirstnameLineEdit->text());
+        ASettings::write("userdata/employeeid", ui->employeeidLineEdit->text());
+        ASettings::write("userdata/phone", ui->phoneLineEdit->text());
+        ASettings::write("userdata/email", ui->emailLineEdit->text());
 
         ASettings::write("flightlogging/function", ui->functionComboBox->currentText());
-        ASettings::write("flightlogging/rules", ui->rulesComboBox->currentText());
         ASettings::write("flightlogging/approach", ui->approachComboBox->currentText());
         ASettings::write("flightlogging/nightlogging", ui->nightComboBox->currentIndex());
+        ASettings::write("flightlogging/logIfr", ui->rulesComboBox->currentIndex());
         ASettings::write("flightlogging/flightnumberPrefix", ui->prefixLineEdit->text());
 
-        QMap<QString,QString> data;
+        ASettings::write("flightlogging/numberTakeoffs", 1);
+        ASettings::write("flightlogging/numberLandings", 1);
+        ASettings::write("flightlogging/popupCalendar", true);
+        ASettings::write("flightlogging/pilotFlying", true);
+
+
+        QMap<QString, QVariant> data;
         switch (ui->aliasComboBox->currentIndex()) {
         case 0:
-            ASettings::write("userdata/displayselfas",ui->aliasComboBox->currentIndex());
-            data.insert("displayname","self");
+            ASettings::write("userdata/displayselfas", ui->aliasComboBox->currentIndex());
             break;
         case 1:
-            ASettings::write("userdata/displayselfas",ui->aliasComboBox->currentIndex());
-            data.insert("displayname","SELF");
+            ASettings::write("userdata/displayselfas", ui->aliasComboBox->currentIndex());
             break;
         case 2:{
-            ASettings::write("userdata/displayselfas",ui->aliasComboBox->currentIndex());
-            QString name;
-            name.append(ui->piclastnameLineEdit->text());
-            name.append(QLatin1String(", "));
-            name.append(ui->picfirstnameLineEdit->text().left(1));
-            name.append(QLatin1Char('.'));
-            data.insert("displayname",name);
+            ASettings::write("userdata/displayselfas", ui->aliasComboBox->currentIndex());
         }
             break;
         default:
             break;
         }
-        data.insert("piclastname",ui->piclastnameLineEdit->text());
-        data.insert("picfirstname",ui->picfirstnameLineEdit->text());
-        data.insert("alias", ASettings::read("userdata/displayselfas").toString());
-        data.insert("employeeid",ui->employeeidLineEdit->text());
-        data.insert("phone",ui->phoneLineEdit->text());
-        data.insert("email",ui->emailLineEdit->text());
-
-        Pilot pic(1);
-        pic.setData(data);
-        pic.commit();
-
-        switch(ASettings::read("main/theme").toInt()){
-        case 0:
-            accept();
-            break;
-        case 1:
-            qApp->quit();
-            QProcess::startDetached(qApp->arguments()[0], qApp->arguments());
-            break;
-        case 2:
+        data.insert("piclastname", ui->piclastnameLineEdit->text());
+        data.insert("picfirstname", ui->picfirstnameLineEdit->text());
+        data.insert("alias", "self");
+        data.insert("employeeid", ui->employeeidLineEdit->text());
+        data.insert("phone", ui->phoneLineEdit->text());
+        data.insert("email", ui->emailLineEdit->text());
+
+        if (!finishSetup()) {
+            QMessageBox message_box(this);
+            message_box.setText("Errors have ocurred creating the database. Without a working database The application will not be usable.");
+        }
+        ASettings::write("setup/setup_complete", true);
+        aDB()->disconnect(); // reset db connection to refresh layout after initial setup.
+        aDB()->connect();
+        auto pilot = APilotEntry(1);
+        pilot.setData(data);
+        if (aDB()->commit(pilot)) {
             qApp->quit();
             QProcess::startDetached(qApp->arguments()[0], qApp->arguments());
-            break;
-        default:
-            ASettings::write("main/theme", 0);
-            accept();
+        } else {
+            QMessageBox message_box(this);
+            message_box.setText("Errors have ocurred creating the database. Without a working database The application will not be usable.");
+        }
+
+    }
+}
+
+bool FirstRunDialog::finishSetup()
+{
+
+    //check if template dir exists and create if needed.
+    QDir dir("data/templates");
+    DEB(dir.path());
+    if (!dir.exists())
+        dir.mkpath(".");
+
+    QMessageBox confirm;
+    confirm.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
+    confirm.setDefaultButton(QMessageBox::No);
+    confirm.setIcon(QMessageBox::Question);
+    confirm.setWindowTitle("Create Database");
+    confirm.setText("We are now going to create the database. Would you like to download the latest database information?\n(Recommended, Internet connection required)\n");
+    int reply = confirm.exec();
+    if (reply == QMessageBox::Yes) {
+        // download latest csv
+        QStringList templateTables = {"aircraft", "airports", "changelog"};
+        QString linkStub = TEMPLATE_URL;
+        for (const auto& table : templateTables) {
+            QEventLoop loop;
+            ADownload* dl = new ADownload;
+            connect(dl, &ADownload::done, &loop, &QEventLoop::quit );
+            dl->setTarget(QUrl(linkStub + table + ".csv"));
+            dl->setFileName("data/templates/" + table + ".csv");
+            dl->download();
+            loop.exec(); // event loop waits for download done signal before allowing loop to continue
+            dl->deleteLater();
+        }
+    }
+
+    //close database connection
+    aDB()->disconnect();
+
+    // back up old database
+    auto oldDatabase = QFile("data/logbook.db");
+    if (oldDatabase.exists()) {
+        auto dateString = QDateTime::currentDateTime().toString(Qt::ISODate);
+        DEB("Backing up old database as: " << "logbook-backup-" + dateString);
+        if (!oldDatabase.rename("data/logbook-backup-" + dateString)) {
+            DEB("Warning: Creating backup of old database has failed.");
+        }
+    }
+    // re-connect and create new database
+    aDB()->connect();
+
+    if (ADataBaseSetup::createDatabase()) {
+        if (ADataBaseSetup::importDefaultData()) {
+            ASettings::write("setup/setup_complete", true);
+            return true;
+        } else {
+            return false;
         }
+    } else {
+        return false;
     }
 }
+
+

+ 7 - 1
src/gui/dialogues/firstrundialog.h

@@ -5,7 +5,11 @@
 #include <QButtonGroup>
 #include <QMessageBox>
 #include "src/classes/asettings.h"
-#include "src/classes/pilot.h"
+#include "src/testing/adebug.h"
+#include "src/experimental/apilotentry.h"
+#include "src/experimental/adatabase.h"
+#include "src/classes/adownload.h"
+#include "src/database/adatabasesetup.h"
 
 namespace Ui {
 class FirstRunDialog;
@@ -29,6 +33,8 @@ private slots:
 
     void on_finishButton_clicked();
 
+    bool finishSetup();
+
 private:
     Ui::FirstRunDialog *ui;
 

+ 55 - 52
src/gui/dialogues/newflightdialog.cpp

@@ -1,4 +1,4 @@
-/*
+/*
  *openPilot Log - A FOSS Pilot Logbook Application
  *Copyright (C) 2020  Felix Turowsky
  *
@@ -103,6 +103,14 @@ NewFlightDialog::NewFlightDialog(QWidget *parent) :
     ui->setupUi(this);
     flightEntry = AFlightEntry();
     setup();
+    if (ASettings::read("NewFlight/FunctionComboBox").toString() == "SIC") {
+        ui->picNameLineEdit->setText(DB_NULL);
+        ui->secondPilotNameLineEdit->setText("self");
+    }
+    if(ASettings::read("NewFlight/FunctionComboBox").toString() == "PIC"){
+        ui->picNameLineEdit->setText("self");
+        emit ui->picNameLineEdit->editingFinished();
+    }
 }
 
 NewFlightDialog::NewFlightDialog(int row_id, QWidget *parent) :
@@ -142,54 +150,46 @@ void NewFlightDialog::setup()
 
     ui->doftLineEdit->setText(QDate::currentDate().toString(Qt::ISODate));
     emit ui->doftLineEdit->editingFinished();
-
-    if(ASettings::read("NewFlight/FunctionComboBox").toString() == "PIC"){
-        ui->picNameLineEdit->setText("self");
-        emit ui->picNameLineEdit->editingFinished();
-    }
     ui->deptLocLineEdit->setFocus();
 }
 void NewFlightDialog::readSettings()
 {
     DEB("Reading Settings...");
     QSettings settings;
-    ui->FunctionComboBox->setCurrentText(ASettings::read("NewFlight/FunctionComboBox").toString());
-    ui->ApproachComboBox->setCurrentText(ASettings::read("NewFlight/ApproachComboBox").toString());
-    ui->PilotFlyingCheckBox->setChecked(ASettings::read("NewFlight/PilotFlyingCheckBox").toBool());
-    ui->PilotMonitoringCheckBox->setChecked(ASettings::read("NewFlight/PilotMonitoringCheckBox").toBool());
-    ui->TakeoffSpinBox->setValue(ASettings::read("NewFlight/TakeoffSpinBox").toInt());
-    ui->TakeoffCheckBox->setChecked(ASettings::read("NewFlight/TakeoffCheckBox").toBool());
-    ui->LandingSpinBox->setValue(ASettings::read("NewFlight/LandingSpinBox").toInt());
-    ui->LandingCheckBox->setChecked(ASettings::read("NewFlight/LandingCheckBox").toBool());
-    ui->AutolandSpinBox->setValue(ASettings::read("NewFlight/AutolandSpinBox").toInt());
-    ui->AutolandCheckBox->setChecked(ASettings::read("NewFlight/AutolandCheckBox").toBool());
-    ui->IfrCheckBox->setChecked(ASettings::read("NewFlight/IfrCheckBox").toBool());
-    ui->VfrCheckBox->setChecked(ASettings::read("NewFlight/VfrCheckBox").toBool());
-    ui->FlightNumberLineEdit->setText(ASettings::read("flightlogging/flightnumberPrefix").toString());
-    ui->calendarCheckBox->setChecked(ASettings::read("NewFlight/calendarCheckBox").toBool());
-    if (ASettings::read("NewFlight/FunctionComboBox").toString() == "Co-Pilot") {
-        ui->picNameLineEdit->setText(DB_NULL);
-        ui->secondPilotNameLineEdit->setText("self");
+    ui->FunctionComboBox->setCurrentText(ASettings::read("flightlogging/function").toString());
+    ui->ApproachComboBox->setCurrentText(ASettings::read("flightlogging/approach").toString());
+
+    ASettings::read("flightlogging/pilotFlying").toBool() ? ui->PilotFlyingCheckBox->setChecked(true)
+                                                          : ui->PilotMonitoringCheckBox->setChecked(true);
+
+    ui->TakeoffSpinBox->setValue(ASettings::read("flightlogging/numberTakeoffs").toInt());
+    ui->TakeoffSpinBox->value() > 0 ? ui->TakeoffCheckBox->setChecked(true)
+                                    : ui->TakeoffCheckBox->setChecked(false);
+    ui->LandingSpinBox->setValue(ASettings::read("flightlogging/numberLandings").toInt());
+    ui->LandingSpinBox->value() > 0 ? ui->LandingCheckBox->setChecked(true)
+                                    : ui->LandingCheckBox->setChecked(false);
+    if (ASettings::read("flightlogging/logIfr").toBool()) {
+        ui->IfrCheckBox->setChecked(true);
+    } else {
+        ui->VfrCheckBox->setChecked(true);
     }
+
+    ui->FlightNumberLineEdit->setText(ASettings::read("flightlogging/flightnumberPrefix").toString());
+    ui->calendarCheckBox->setChecked(ASettings::read("flightlogging/popupCalendar").toBool());
+
 }
 
 void NewFlightDialog::writeSettings()
 {
     DEB("Writing Settings...");
 
-    ASettings::write("NewFlight/FunctionComboBox",ui->FunctionComboBox->currentText());
-    ASettings::write("NewFlight/ApproachComboBox",ui->ApproachComboBox->currentText());
-    ASettings::write("NewFlight/PilotFlyingCheckBox",ui->PilotFlyingCheckBox->isChecked());
-    ASettings::write("NewFlight/PilotMonitoringCheckBox",ui->PilotMonitoringCheckBox->isChecked());
-    ASettings::write("NewFlight/TakeoffSpinBox",ui->TakeoffSpinBox->value());
-    ASettings::write("NewFlight/TakeoffCheckBox",ui->TakeoffCheckBox->isChecked());
-    ASettings::write("NewFlight/LandingSpinBox",ui->LandingSpinBox->value());
-    ASettings::write("NewFlight/LandingCheckBox",ui->LandingCheckBox->isChecked());
-    ASettings::write("NewFlight/AutolandSpinBox",ui->AutolandSpinBox->value());
-    ASettings::write("NewFlight/AutolandCheckBox",ui->AutolandCheckBox->isChecked());
-    ASettings::write("NewFlight/IfrCheckBox",ui->IfrCheckBox->isChecked());
-    ASettings::write("NewFlight/VfrCheckBox",ui->VfrCheckBox->isChecked());
-    ASettings::write("NewFlight/calendarCheckBox",ui->calendarCheckBox->isChecked());
+    ASettings::write("flightlogging/function", ui->FunctionComboBox->currentText());
+    ASettings::write("flightlogging/approach", ui->ApproachComboBox->currentText());
+    ASettings::write("flightlogging/pilotFlying", ui->PilotFlyingCheckBox->isChecked());
+    ASettings::write("flightlogging/numberTakeoffs", ui->TakeoffSpinBox->value());
+    ASettings::write("flightlogging/numberLandings", ui->LandingSpinBox->value());
+    ASettings::write("flightlogging/logIfr", ui->IfrCheckBox->isChecked());
+    ASettings::write("flightlogging/popupCalendar", ui->calendarCheckBox->isChecked());
 }
 
 void NewFlightDialog::setupButtonGroups()
@@ -206,15 +206,15 @@ void NewFlightDialog::setupButtonGroups()
 void NewFlightDialog::setupRawInputValidation()
 {
     // get Maps
-    pilotsIdMap      = aDB()->getIdMap(ADataBase::pilots);
-    tailsIdMap       = aDB()->getIdMap(ADataBase::tails);
-    airportIcaoIdMap = aDB()->getIdMap(ADataBase::airport_identifier_icao);
-    airportIataIdMap = aDB()->getIdMap(ADataBase::airport_identifier_iata);
-    airportNameIdMap = aDB()->getIdMap(ADataBase::airport_names);
+    pilotsIdMap      = aDB()->getIdMap(ADatabaseTarget::pilots);
+    tailsIdMap       = aDB()->getIdMap(ADatabaseTarget::tails);
+    airportIcaoIdMap = aDB()->getIdMap(ADatabaseTarget::airport_identifier_icao);
+    airportIataIdMap = aDB()->getIdMap(ADatabaseTarget::airport_identifier_iata);
+    airportNameIdMap = aDB()->getIdMap(ADatabaseTarget::airport_names);
     //get Completer Lists
-    pilotList   = aDB()->getCompletionList(ADataBase::pilots);
-    tailsList   = aDB()->getCompletionList(ADataBase::registrations);
-    airportList = aDB()->getCompletionList(ADataBase::airport_identifier_all);
+    pilotList   = aDB()->getCompletionList(ADatabaseTarget::pilots);
+    tailsList   = aDB()->getCompletionList(ADatabaseTarget::registrations);
+    airportList = aDB()->getCompletionList(ADatabaseTarget::airport_identifier_all);
     auto tempList = QStringList();
     // define tuples
     const std::tuple<QString, QStringList*, QRegularExpression>
@@ -802,10 +802,13 @@ void NewFlightDialog::addNewTail(QLineEdit *parent_line_edit)
         auto na = NewTailDialog(ui->acftLineEdit->text(), this);
         na.exec();
         // update map and list, set line edit
-        tailsIdMap  = aDB()->getIdMap(ADataBase::tails);
-        tailsList   = aDB()->getCompletionList(ADataBase::registrations);
+        tailsIdMap  = aDB()->getIdMap(ADatabaseTarget::tails);
+        tailsList   = aDB()->getCompletionList(ADatabaseTarget::registrations);
+
+        DEB("New Entry added. Id:" << aDB()->getLastEntry(ADatabaseTarget::tails));
+        DEB("AC Map: " << tailsIdMap);
 
-        parent_line_edit->setText(tailsIdMap.key(aDB()->getLastEntry(ADataBase::tails)));
+        parent_line_edit->setText(tailsIdMap.key(aDB()->getLastEntry(ADatabaseTarget::tails)));
         emit parent_line_edit->editingFinished();
     } else {
         parent_line_edit->setText(DB_NULL);
@@ -831,10 +834,10 @@ void NewFlightDialog::addNewPilot(QLineEdit *parent_line_edit)
         auto np = NewPilotDialog(this);
         np.exec();
         // update map and list, set line edit
-        pilotsIdMap  = aDB()->getIdMap(ADataBase::pilots);
-        pilotList    = aDB()->getCompletionList(ADataBase::pilots);
-        DEB("Setting new entry: " << pilotsIdMap.key(aDB()->getLastEntry(ADataBase::pilots)));
-        parent_line_edit->setText(pilotsIdMap.key(aDB()->getLastEntry(ADataBase::pilots)));
+        pilotsIdMap  = aDB()->getIdMap(ADatabaseTarget::pilots);
+        pilotList    = aDB()->getCompletionList(ADatabaseTarget::pilots);
+        DEB("Setting new entry: " << pilotsIdMap.key(aDB()->getLastEntry(ADatabaseTarget::pilots)));
+        parent_line_edit->setText(pilotsIdMap.key(aDB()->getLastEntry(ADatabaseTarget::pilots)));
         emit parent_line_edit->editingFinished();
     } else {
         parent_line_edit->setText(DB_NULL);
@@ -882,7 +885,7 @@ void NewFlightDialog::on_submitButton_clicked()
     if (!aDB()->commit(flightEntry)) {
         auto message_box = QMessageBox(this);
         message_box.setText("The following error has ocurred:\n\n"
-                            + aDB()->lastError
+                            + aDB()->lastError.text()
                             + "\n\nYour entry has not been saved.");
         message_box.setIcon(QMessageBox::Warning);
         message_box.exec();

+ 2 - 2
src/gui/dialogues/newpilotdialog.cpp

@@ -108,7 +108,7 @@ void NewPilotDialog::setup()
     }
 
     DEB("Setting up completer...");
-    auto completer = new QCompleter(aDB()->getCompletionList(ADataBase::companies), ui->companyLineEdit);
+    auto completer = new QCompleter(aDB()->getCompletionList(ADatabaseTarget::companies), ui->companyLineEdit);
     completer->setCompletionMode(QCompleter::InlineCompletion);
     completer->setCaseSensitivity(Qt::CaseSensitive);
     ui->companyLineEdit->setCompleter(completer);
@@ -154,7 +154,7 @@ void NewPilotDialog::submitForm()
     if (!aDB()->commit(pilotEntry)) {
         auto message_box = QMessageBox(this);
         message_box.setText("The following error has ocurred:\n\n"
-                            + aDB()->lastError
+                            + aDB()->lastError.text()
                             + "\n\nThe entry has not been saved.");
         message_box.exec();
         return;

+ 5 - 4
src/gui/dialogues/newtaildialog.cpp

@@ -81,8 +81,8 @@ NewTailDialog::~NewTailDialog()
 void NewTailDialog::setupCompleter()
 {
     using namespace experimental;
-    idMap = aDB()->getIdMap(ADataBase::aircraft);
-    aircraftList = aDB()->getCompletionList(experimental::ADataBase::aircraft);
+    idMap = aDB()->getIdMap(ADatabaseTarget::aircraft);
+    aircraftList = aDB()->getCompletionList(ADatabaseTarget::aircraft);
 
     QCompleter *completer = new QCompleter(aircraftList, ui->searchLineEdit);
     completer->setCaseSensitivity(Qt::CaseInsensitive);
@@ -215,12 +215,13 @@ void NewTailDialog::submitForm()
     if (!aDB()->commit(entry)) {
         auto message_box = QMessageBox(this);
         message_box.setText("The following error has ocurred:\n\n"
-                            + aDB()->lastError
+                            + aDB()->lastError.text()
                             + "\n\nThe entry has not been saved.");
         message_box.exec();
         return;
     } else {
-        ACalc::updateAutoTimes(entry.getPosition().second);
+        if (entry.getPosition().second != 0)
+            ACalc::updateAutoTimes(entry.getPosition().second);
         QDialog::accept();
     }
 }

+ 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

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

@@ -67,7 +67,7 @@ void DebugWidget::on_resetDatabasePushButton_clicked()
     if (oldDatabase.exists()) {
         auto dateString = QDateTime::currentDateTime().toString(Qt::ISODate);
         DEB("Backing up old database as: " << "logbook-backup-" + dateString);
-        if (!oldDatabase.rename("data/logbook-backup-" + dateString)) {
+        if (!oldDatabase.rename("data/logbook-backup-" + dateString + ".db")) {
             DEB("Warning: Creating backup of old database has failed.");
         }
     }

+ 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);