Browse Source

Merge pull request #83 from fiffty-50/develop

Push to main
Felix Turowsky 3 years ago
parent
commit
b31f513931
53 changed files with 2201 additions and 1530 deletions
  1. 3 0
      .gitignore
  2. 25 8
      CMakeLists.txt
  3. 0 0
      DEPRECATED_INOP_openPilotLog.pro
  4. 245 231
      l10n/openpilotlog_de.ts
  5. 245 231
      l10n/openpilotlog_en.ts
  6. 247 233
      l10n/openpilotlog_es.ts
  7. 11 4
      main.cpp
  8. 69 5
      mainwindow.cpp
  9. 14 0
      mainwindow.h
  10. 54 0
      src/classes/acompletiondata.cpp
  11. 41 0
      src/classes/acompletiondata.h
  12. 7 2
      src/classes/aentry.cpp
  13. 2 2
      src/classes/aentry.h
  14. 3 2
      src/classes/apilotentry.cpp
  15. 0 2
      src/classes/asettings.cpp
  16. 0 1
      src/classes/asettings.h
  17. 1 1
      src/classes/astyle.cpp
  18. 32 14
      src/database/adatabase.cpp
  19. 40 8
      src/database/adatabase.h
  20. 106 10
      src/functions/adate.cpp
  21. 18 2
      src/functions/adate.h
  22. 18 10
      src/functions/alog.cpp
  23. 21 0
      src/functions/areadcsv.cpp
  24. 2 0
      src/functions/areadcsv.h
  25. 2 31
      src/gui/dialogues/firstrundialog.cpp
  26. 0 4
      src/gui/dialogues/firstrundialog.h
  27. 15 46
      src/gui/dialogues/firstrundialog.ui
  28. 274 329
      src/gui/dialogues/newflightdialog.cpp
  29. 67 38
      src/gui/dialogues/newflightdialog.h
  30. 1 1
      src/gui/dialogues/newpilot.ui
  31. 1 1
      src/gui/dialogues/newtail.ui
  32. 6 6
      src/gui/dialogues/newtaildialog.cpp
  33. 1 1
      src/gui/dialogues/newtaildialog.h
  34. 2 1
      src/gui/widgets/aircraftwidget.cpp
  35. 1 0
      src/gui/widgets/aircraftwidget.h
  36. 21 27
      src/gui/widgets/debugwidget.cpp
  37. 1 35
      src/gui/widgets/homewidget.cpp
  38. 2 3
      src/gui/widgets/homewidget.h
  39. 19 7
      src/gui/widgets/logbookwidget.cpp
  40. 4 1
      src/gui/widgets/logbookwidget.h
  41. 2 0
      src/gui/widgets/pilotswidget.cpp
  42. 3 24
      src/gui/widgets/settingswidget.cpp
  43. 0 4
      src/gui/widgets/settingswidget.h
  44. 119 205
      src/gui/widgets/settingswidget.ui
  45. 3 0
      src/opl.h
  46. 69 0
      src/testing/importCrewlounge/importcrewlounge.cpp
  47. 10 0
      src/testing/importCrewlounge/importcrewlounge.h
  48. 69 0
      src/testing/importCrewlounge/processaircraft.cpp
  49. 49 0
      src/testing/importCrewlounge/processaircraft.h
  50. 89 0
      src/testing/importCrewlounge/processflights.cpp
  51. 34 0
      src/testing/importCrewlounge/processflights.h
  52. 84 0
      src/testing/importCrewlounge/processpilots.cpp
  53. 49 0
      src/testing/importCrewlounge/processpilots.h

+ 3 - 0
.gitignore

@@ -82,3 +82,6 @@ openPilotLog.vcxproj
 openPilotLog.vcxproj.filters
 
 openPilotLog.vcxproj.user
+
+# Disable localization updates
+l10n/

+ 25 - 8
CMakeLists.txt

@@ -23,6 +23,7 @@ find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets Sql Network LinguistTools
 set(PROJECT_SOURCES
     main.cpp
     mainwindow.cpp
+    src/classes/acompletiondata.cpp
     src/classes/acurrencyentry.cpp
     src/classes/astyle.cpp
     src/classes/astandardpaths.cpp
@@ -60,6 +61,7 @@ set(PROJECT_SOURCES
     
     src/opl.h
     mainwindow.h
+    src/classes/acompletiondata.h
     src/classes/acurrencyentry.h
     src/classes/astyle.h
     src/classes/astandardpaths.h
@@ -115,6 +117,15 @@ set(PROJECT_SOURCES
     assets/themes/stylesheets/breeze/breeze.qrc
     assets/themes/stylesheets/qdarkstyle/qdarkstyle.qrc
     assets/templates.qrc
+
+    src/testing/importCrewlounge/importcrewlounge.h
+    src/testing/importCrewlounge/importcrewlounge.cpp
+    src/testing/importCrewlounge/processpilots.h
+    src/testing/importCrewlounge/processpilots.cpp
+    src/testing/importCrewlounge/processaircraft.h
+    src/testing/importCrewlounge/processaircraft.cpp
+    src/testing/importCrewlounge/processflights.h
+    src/testing/importCrewlounge/processflights.cpp
 )
 
 # This is currently a bit buggy, see
@@ -122,14 +133,20 @@ set(PROJECT_SOURCES
 # https://bugreports.qt.io/browse/QTBUG-76410
 # Before working on the translations themselves, check for updates and consider not updating for
 # every build due to danger of loss of translations...
-set(TS_FILES
-    l10n/openpilotlog_en.ts
-    l10n/openpilotlog_de.ts
-    l10n/openpilotlog_es.ts
-)
-set_source_files_properties(${TS_FILES} PROPERTIES OUTPUT_LOCATION "l10n")
-qt5_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TS_FILES})
-
+#if (Qt5Widgets_FOUND)
+#    if (Qt5Widgets_VERSION VERSION_LESS 5.15.0)
+#        message("Translations are available for Qt5 version >= 5.15")
+#    else()
+#        message("Qt > 5.15 detected. Enabling translations.")
+#        set(TS_FILES
+#            l10n/openpilotlog_en.ts
+#            l10n/openpilotlog_de.ts
+#            l10n/openpilotlog_es.ts
+#        )
+#        set_source_files_properties(${TS_FILES} PROPERTIES OUTPUT_LOCATION "l10n")
+#        qt5_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TS_FILES})
+#    endif(Qt5Widgets_VERSION VERSION_LESS 5.15.0)
+#endif(Qt5Widgets_FOUND)
 
 if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
     qt_add_executable(openPilotLog

+ 0 - 0
openPilotLog.pro → DEPRECATED_INOP_openPilotLog.pro


File diff suppressed because it is too large
+ 245 - 231
l10n/openpilotlog_de.ts


File diff suppressed because it is too large
+ 245 - 231
l10n/openpilotlog_en.ts


File diff suppressed because it is too large
+ 247 - 233
l10n/openpilotlog_es.ts


+ 11 - 4
main.cpp

@@ -39,6 +39,11 @@
 #define ORGNAME QStringLiteral("opl")
 #define ORGDOMAIN QStringLiteral("https://github.com/fiffty-50/openpilotlog")
 
+/*!
+ *  Helper functions that prepare and set up the application
+ */
+namespace Main {
+
 void init()
 {
     LOG << "Setting up / verifying Application Directories...";
@@ -57,8 +62,9 @@ void init()
     ASettings::setup();
     LOG << "Setting up application style...";
     AStyle::setup();
-    LOG << "Installing translator...";
-    ATranslator::installTranslator(Opl::Translations::English);
+    // Translations to be done at a later stage
+    //LOG << "Installing translator...";
+    //ATranslator::installTranslator(Opl::Translations::English);
 }
 
 void firstRun()
@@ -70,6 +76,7 @@ void firstRun()
     ASettings::write(ASettings::Main::SetupComplete, true);
     LOG << "Initial Setup Completed successfully";
 }
+} // namespace Main
 
 int main(int argc, char *argv[])
 {
@@ -86,11 +93,11 @@ int main(int argc, char *argv[])
     }
 
     // Set Up the Application
-    init();
+    Main::init();
 
     // Check for First Run and launch Setup Wizard
     if (!ASettings::read(ASettings::Main::SetupComplete).toBool())
-        firstRun();
+        Main::firstRun();
 
     // Create Main Window and set Window Icon acc. to Platform
     MainWindow w;

+ 69 - 5
mainwindow.cpp

@@ -20,7 +20,15 @@
 #include "src/functions/alog.h"
 #include "src/database/adatabase.h"
 #include "src/classes/astyle.h"
+#include "src/gui/dialogues/firstrundialog.h"
+#include "src/classes/aentry.h"
 
+// Quick and dirty Debug area
+#include "src/testing/importCrewlounge/importcrewlounge.h"
+void MainWindow::doDebugStuff()
+{
+    ImportCrewlounge::exec(QString());
+}
 MainWindow::MainWindow(QWidget *parent)
     : QMainWindow(parent)
     , ui(new Ui::MainWindow)
@@ -29,13 +37,27 @@ MainWindow::MainWindow(QWidget *parent)
     // connect to the Database
     QFileInfo database_file(AStandardPaths::directory(AStandardPaths::Database).
                                          absoluteFilePath(QStringLiteral("logbook.db")));
-            if (!database_file.exists()) {
-                WARN(tr("Error: Database file not found."));
-            }
+    bool db_invalid = false;
+    if (!database_file.exists()) {
+        WARN(tr("Error: Database file not found."));
+        db_invalid = true;
+    }
+    if (database_file.size() == 0) { // To Do: Check for database errors instead of just checking for empty
+        WARN(tr("Database file invalid."));
+        db_invalid = true;
+    }
+
+    if (db_invalid)
+        onDatabaseInvalid();
+
+
     if(!aDB->connect()){
         WARN(tr("Error establishing database connection."));
     }
 
+    // retreive completion lists and maps
+    completionData.init();
+
     // Create a spacer for the toolbar to separate left and right parts
     auto *spacer = new QWidget();
     spacer->setMinimumWidth(1);
@@ -61,7 +83,7 @@ MainWindow::MainWindow(QWidget *parent)
     // Construct Widgets
     homeWidget = new HomeWidget(this);
     ui->stackedWidget->addWidget(homeWidget);
-    logbookWidget = new LogbookWidget(this);
+    logbookWidget = new LogbookWidget(completionData, this);
     ui->stackedWidget->addWidget(logbookWidget);
     aircraftWidget = new AircraftWidget(this);
     ui->stackedWidget->addWidget(aircraftWidget);
@@ -138,6 +160,47 @@ void MainWindow::connectWidgets()
                      aircraftWidget,  &AircraftWidget::repopulateModel);
 }
 
+void MainWindow::onDatabaseInvalid()
+{
+    QMessageBox db_error(this);
+    //db_error.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
+    db_error.addButton(tr("Restore Backup"), QMessageBox::ButtonRole::AcceptRole);
+    db_error.addButton(tr("Create New Database"), QMessageBox::ButtonRole::RejectRole);
+    db_error.addButton(tr("Abort"), QMessageBox::ButtonRole::DestructiveRole);
+    db_error.setIcon(QMessageBox::Warning);
+    db_error.setWindowTitle(tr("No valid database found"));
+    db_error.setText(tr("No valid database has been found.<br>"
+                       "Would you like to create a new database or import a backup?"));
+
+    int ret = db_error.exec();
+    if (ret == QMessageBox::DestructiveRole) {
+        DEB << "No valid database found. Exiting.";
+        on_actionQuit_triggered();
+    } else if (ret == QMessageBox::ButtonRole::AcceptRole) {
+        DEB << "Yes(Import Backup)";
+        QString db_path = QFileDialog::getOpenFileName(this,
+                                                       tr("Select Database"),
+                                                       AStandardPaths::directory(AStandardPaths::Backup).canonicalPath(),
+                                                       tr("Database file (*.db)"));
+        if (!db_path.isEmpty()) {
+            if(!aDB->restoreBackup(db_path)) {
+               WARN(tr("Unable to restore Backup file: %1").arg(db_path));
+               on_actionQuit_triggered();
+            } else {
+                INFO(tr("Backup successfully restored."));
+            }
+        }
+    } else if (ret == QMessageBox::ButtonRole::RejectRole){
+        DEB << "No(Create New)";
+        if(FirstRunDialog().exec() == QDialog::Rejected){
+            LOG << "Initial setup incomplete or unsuccessfull.";
+            on_actionQuit_triggered();
+        }
+        ASettings::write(ASettings::Main::SetupComplete, true);
+        LOG << "Initial Setup Completed successfully";
+    }
+}
+
 /*
  * Slots
  */
@@ -149,7 +212,8 @@ void MainWindow::on_actionHome_triggered()
 
 void MainWindow::on_actionNewFlight_triggered()
 {
-    NewFlightDialog nf(this);
+    completionData.update();
+    NewFlightDialog nf(completionData, this);
     nf.exec();
 }
 

+ 14 - 0
mainwindow.h

@@ -39,8 +39,10 @@
 #include "src/gui/dialogues/newpilotdialog.h"
 #include "src/gui/dialogues/newflightdialog.h"
 #include "src/classes/arunguard.h"
+#include "src/classes/acompletiondata.h"
 #include "src/testing/atimer.h"
 
+
 QT_BEGIN_NAMESPACE
 namespace Ui {
 class MainWindow;
@@ -96,19 +98,31 @@ private:
 
     DebugWidget* debugWidget;
 
+    // Completion Data for QCompleters and Mapping
+    ACompletionData completionData;
+
     void nope();
 
     void connectWidgets();
 
+    // Prompts the user to fix a broken database or import a backup
+    void onDatabaseInvalid();
+
+    //
+    void doDebugStuff();
+
 protected:
     /*!
      * \brief Shows the debug widget by pressing <ctrl + t>
+     * <Shift+Enter for a quick and dirty debug>
      */
     void keyPressEvent(QKeyEvent* keyEvent) override
     {
         if(keyEvent->type() == QKeyEvent::KeyPress) {
             if(keyEvent->matches(QKeySequence::AddTab)) {
                 on_actionDebug_triggered();
+            } else if (keyEvent->matches(QKeySequence::InsertLineSeparator)) {
+                doDebugStuff();
             }
         }
     }

+ 54 - 0
src/classes/acompletiondata.cpp

@@ -0,0 +1,54 @@
+#include "acompletiondata.h"
+
+void ACompletionData::init()
+{
+    // retreive user modifiable data
+    pilotsIdMap = aDB->getIdMap(ADatabaseTarget::pilots);
+    tailsIdMap  = aDB->getIdMap(ADatabaseTarget::tails);
+    pilotList   = aDB->getCompletionList(ADatabaseTarget::pilots);
+    tailsList   = aDB->getCompletionList(ADatabaseTarget::registrations);
+
+    // For tails, also provide completion for registration stripped of the '-' character
+    QStringList tails_list = aDB->getCompletionList(ADatabaseTarget::registrations);
+    for (auto &reg : tails_list) {
+        if(reg.contains(QLatin1Char('-'))) { // check to avoid duplication if reg has no '-'
+            QString copy = reg;
+            reg.remove(QLatin1Char('-'));
+            reg = copy + " (" + reg + QLatin1Char(')');
+        }
+    }
+    tailsList   = tails_list;
+
+    // retreive default data
+    airportIcaoIdMap = aDB->getIdMap(ADatabaseTarget::airport_identifier_icao);
+    airportIataIdMap = aDB->getIdMap(ADatabaseTarget::airport_identifier_iata);
+    airportNameIdMap = aDB->getIdMap(ADatabaseTarget::airport_names);
+    airportList      = aDB->getCompletionList(ADatabaseTarget::airport_identifier_all);
+
+    current_state = aDB->getUserDataState();
+}
+
+void ACompletionData::update()
+{
+    if (current_state != aDB->getUserDataState()) {
+        // retreive user modifiable data
+        pilotList   = aDB->getCompletionList(ADatabaseTarget::pilots);
+        tailsList   = aDB->getCompletionList(ADatabaseTarget::registrations);
+        pilotsIdMap = aDB->getIdMap(ADatabaseTarget::pilots);
+        tailsIdMap  = aDB->getIdMap(ADatabaseTarget::tails);
+
+        current_state = aDB->getUserDataState();
+    }
+}
+
+void ACompletionData::updateTails()
+{
+    tailsIdMap  = aDB->getIdMap(ADatabaseTarget::tails);
+    tailsList   = aDB->getCompletionList(ADatabaseTarget::registrations);
+}
+
+void ACompletionData::updatePilots()
+{
+    pilotsIdMap  = aDB->getIdMap(ADatabaseTarget::pilots);
+    pilotList    = aDB->getCompletionList(ADatabaseTarget::pilots);
+}

+ 41 - 0
src/classes/acompletiondata.h

@@ -0,0 +1,41 @@
+#ifndef ACOMPLETIONDATA_H
+#define ACOMPLETIONDATA_H
+#include "src/database/adatabase.h"
+
+/*!
+ * \brief The ACompletionData class provides data to QCompleters and QMaps used
+ * for mapping user input to database keys. It retreives the completer targets
+ * and mapping data from the database and caches it for fast access without the
+ * need to query the database.
+ */
+class ACompletionData
+{
+public:  
+    /*!
+     * \brief init Retrieves Data and populates Lists and Maps
+     */
+    void init();
+
+    /*!
+     * \brief updates data from the user modifiable tables
+     */
+    void update();
+    void updateTails();
+    void updatePilots();
+
+
+    // Maps for input mapping DB key - user input
+    QMap<RowId_T, QString> pilotsIdMap;
+    QMap<RowId_T, QString> tailsIdMap;
+    QMap<RowId_T, QString> airportIcaoIdMap;
+    QMap<RowId_T, QString> airportIataIdMap;
+    QMap<RowId_T, QString> airportNameIdMap;
+    // Lists for QCompleter
+    QStringList pilotList;
+    QStringList tailsList;
+    QStringList airportList;
+    // User Data State to trigger update if needed
+    UserDataState current_state;
+};
+
+#endif // ACOMPLETIONDATA_H

+ 7 - 2
src/classes/aentry.cpp

@@ -34,12 +34,17 @@ void AEntry::setData(RowData_T table_data)
     tableData = table_data;
 }
 
-const DataPosition& AEntry::getPosition()
+void AEntry::setPosition(DataPosition position_)
+{
+    position = position_;
+}
+
+const DataPosition& AEntry::getPosition() const
 {
     return position;
 }
 
-const RowData_T& AEntry::getData()
+const RowData_T& AEntry::getData() const
 {
     return tableData;
 }

+ 2 - 2
src/classes/aentry.h

@@ -53,8 +53,8 @@ public:
     void setData(RowData_T table_data);
     void setPosition(DataPosition position_);
 
-    const DataPosition& getPosition();
-    const RowData_T& getData();
+    const DataPosition& getPosition() const;
+    const RowData_T& getData() const;
 
 };
 

+ 3 - 2
src/classes/apilotentry.cpp

@@ -35,6 +35,7 @@ const QString APilotEntry::name()
     if (tableData.isEmpty())
         return QString();
 
-    return tableData.value(Opl::Db::PILOTS_LASTNAME).toString() + ','
-           +tableData.value(Opl::Db::PILOTS_FIRSTNAME).toString().left(1) + '.';
+    return tableData.value(Opl::Db::PILOTS_LASTNAME).toString() + ", "
+           //+tableData.value(Opl::Db::PILOTS_FIRSTNAME).toString().left(1) + '.';
+           +tableData.value(Opl::Db::PILOTS_FIRSTNAME).toString();
 }

+ 0 - 2
src/classes/asettings.cpp

@@ -35,7 +35,6 @@ QMap<ASettings::UserData, QString> ASettings::userDataMap = {
     {UserData::PilotSortColumn,         QStringLiteral("pilotSortColumn")},
     {UserData::AcftAllowIncomplete,     QStringLiteral("acftAllowIncomplete")},
     {UserData::FtlWarningThreshold,     QStringLiteral("ftlWarningThreshold")},
-    {UserData::CurrWarningEnabled,      QStringLiteral("currWarningEnabled")},
     {UserData::CurrWarningThreshold,    QStringLiteral("currWarningThreshold")},
     {UserData::ShowToLgdCurrency,       QStringLiteral("showToLdgCurrency")},
     {UserData::ShowLicCurrency,         QStringLiteral("showLicCurrency")},
@@ -81,7 +80,6 @@ void ASettings::resetToDefaults()
 
     write(UserData::DisplaySelfAs, 0);
     write(UserData::FtlWarningThreshold, 0.8); // To Do: UI Option
-    write(UserData::CurrWarningEnabled, true);
     write(UserData::CurrWarningThreshold, 30);
     write(UserData::ShowToLgdCurrency, true);
     write(UserData::ShowLicCurrency, false);

+ 0 - 1
src/classes/asettings.h

@@ -42,7 +42,6 @@ public:
         PilotSortColumn,
         AcftAllowIncomplete,
         FtlWarningThreshold,
-        CurrWarningEnabled,
         CurrWarningThreshold,
         ShowToLgdCurrency,
         ShowLicCurrency,

+ 1 - 1
src/classes/astyle.cpp

@@ -51,7 +51,7 @@ void AStyle::setup()
         QFont font(ASettings::read(ASettings::Main::Font).toString());
         font.setPointSize(ASettings::read(ASettings::Main::FontSize).toUInt());
         qApp->setFont(font);
-        LOG << "Application Font set: " << font.toString().splitRef(',').first();
+        LOG << "Application Font set: " << font.toString().split(',').first();
     }
     // Set style, stylesheet or palette
     QString style_setting = ASettings::read(ASettings::Main::Style).toString();

+ 32 - 14
src/database/adatabase.cpp

@@ -80,6 +80,22 @@ QStringList ADatabase::getTemplateTableNames()
     return templateTableNames;
 }
 
+UserDataState ADatabase::getUserDataState()
+{
+    QSqlQuery q;
+    q.prepare(QStringLiteral("SELECT COUNT (*) FROM tails"));
+    q.exec();
+    q.first();
+    int tails = q.value(0).toInt();
+
+    q.prepare(QStringLiteral("SELECT COUNT (*) FROM pilots"));
+    q.exec();
+    q.first();
+    int pilots = q.value(0).toInt();
+
+    return UserDataState(tails, pilots);
+}
+
 QStringList ADatabase::getUserTableNames()
 {
     return userTableNames;
@@ -173,7 +189,7 @@ QSqlDatabase ADatabase::database()
     return QSqlDatabase::database(QStringLiteral("qt_sql_default_connection"));
 }
 
-bool ADatabase::commit(AEntry entry)
+bool ADatabase::commit(const AEntry &entry)
 {
     if (exists(entry)) {
         return update(entry);
@@ -182,7 +198,7 @@ bool ADatabase::commit(AEntry entry)
     }
 }
 
-bool ADatabase::remove(AEntry entry)
+bool ADatabase::remove(const AEntry &entry)
 {
     if (!exists(entry)) {
         DEB << "Error: Database entry not found.";
@@ -197,7 +213,7 @@ bool ADatabase::remove(AEntry entry)
     query.prepare(statement);
     query.addBindValue(entry.getPosition().rowId);
 
-    if (!query.exec())
+    if (query.exec())
     {
         DEB << "Entry " << entry.getPosition() << " removed.";
         emit dataBaseUpdated();
@@ -253,7 +269,7 @@ bool ADatabase::removeMany(QList<DataPosition> data_position_list)
     }
 }
 
-bool ADatabase::exists(AEntry entry)
+bool ADatabase::exists(const AEntry &entry)
 {
     if(entry.getPosition().rowId == 0)
         return false;
@@ -328,7 +344,7 @@ bool ADatabase::clear()
 }
 
 
-bool ADatabase::update(AEntry updated_entry)
+bool ADatabase::update(const AEntry &updated_entry)
 {
     auto data = updated_entry.getData();
     QString statement = QLatin1String("UPDATE ") + updated_entry.getPosition().tableName + QLatin1String(" SET ");
@@ -362,7 +378,7 @@ bool ADatabase::update(AEntry updated_entry)
     }
 }
 
-bool ADatabase::insert(AEntry new_entry)
+bool ADatabase::insert(const AEntry &new_entry)
 {
     auto data = new_entry.getData();
     QString statement = QLatin1String("INSERT INTO ") + new_entry.getPosition().tableName + QLatin1String(" (");
@@ -555,7 +571,7 @@ const QStringList ADatabase::getCompletionList(ADatabaseTarget target)
 }
 
 const
-QMap<QString, RowId_T> ADatabase::getIdMap(ADatabaseTarget target)
+QMap<RowId_T, QString> ADatabase::getIdMap(ADatabaseTarget target)
 {
     QString statement;
 
@@ -585,7 +601,11 @@ QMap<QString, RowId_T> ADatabase::getIdMap(ADatabaseTarget target)
         return {};
     }
 
-    auto query = QSqlQuery(statement);
+    QSqlQuery query;
+    query.setForwardOnly(true);
+    query.prepare(statement);
+    query.exec();
+    //auto query = QSqlQuery(statement);
     if (!query.isActive()) {
         DEB << "No result found. Check Query and Error.";
         DEB << "Query: " << statement;
@@ -593,15 +613,13 @@ QMap<QString, RowId_T> ADatabase::getIdMap(ADatabaseTarget target)
         lastError = query.lastError();
         return {};
     }
-
-    auto id_map = QMap<QString, RowId_T>();
-    while (query.next()) {
-        id_map.insert(query.value(1).toString(), query.value(0).toInt());
-    }
+    auto id_map = QMap<RowId_T, QString>();
+    while (query.next())
+        id_map.insert(query.value(0).toInt(), query.value(1).toString());
     return id_map;
 }
 
-int ADatabase::getLastEntry(ADatabaseTable table)
+RowId_T ADatabase::getLastEntry(ADatabaseTable table)
 {
     QString statement = QLatin1String("SELECT MAX(ROWID) FROM ");
 

+ 40 - 8
src/database/adatabase.h

@@ -88,6 +88,31 @@ enum class ADatabaseSummaryKey {
     total_time,
 };
 
+/*!
+ * \brief The UserDateState struct caches the current number of entries in relevant database tables
+ * for fast access
+ * \param numTails - Number of tails in the database
+ * \param numPilots - Number of pilots in the database
+ */
+struct UserDataState {
+
+    UserDataState(){numTails = 0; numPilots = 0;}
+    UserDataState(int numTails_, int numPilots_)
+        : numTails(numTails_), numPilots(numPilots_){}
+
+    bool operator==(const UserDataState& other)
+    {
+        return numTails == other.numTails && numPilots == other.numPilots;
+    }
+    bool operator!=(const UserDataState& other)
+    {
+        return numTails != other.numTails || numPilots != other.numPilots;
+    }
+
+    int numTails;
+    int numPilots;
+};
+
 /*!
  * \brief Custom Database Error derived from QSqlError.
  * Extends text() adding "Database Error: " before the text.
@@ -195,7 +220,7 @@ public:
     /*!
      * \brief Checks if an entry exists in the database, based on position data
      */
-    bool exists(AEntry entry);
+    bool exists(const AEntry &entry);
     bool exists(DataPosition data_position);
 
     /*!
@@ -208,22 +233,22 @@ public:
      * \brief commits an entry to the database, calls either insert or update,
      * based on position data
      */
-    bool commit(AEntry entry);
+    bool commit(const AEntry &entry);
 
     /*!
      * \brief Create new entry in the databse based on UserInput
      */
-    bool insert(AEntry new_entry);
+    bool insert(const AEntry &new_entry);
 
     /*!
      * \brief Updates entry in database from existing entry tweaked by the user.
      */
-    bool update(AEntry updated_entry);
+    bool update(const AEntry &updated_entry);
 
     /*!
      * \brief deletes an entry from the database.
      */
-    bool remove(AEntry entry);
+    bool remove(const AEntry &entry);
 
     /*!
      * \brief deletes a list of entries from the database. Optimised for speed when
@@ -293,16 +318,16 @@ public:
     const QStringList getCompletionList(ADatabaseTarget target);
 
     /*!
-     * \brief returns a QMap<QString, RowId_t> of a human-readable database value and
+     * \brief returns a QMap of a human-readable database value and
      * its row id. Used in the Dialogs to map user input to unique database entries.
      * \todo What is this QString semantically? As i understand its a "QueryResult" QVariant cast to QString
      */
-    const QMap<QString, RowId_T> getIdMap(ADatabaseTarget target);
+    const QMap<RowId_T, QString> getIdMap(ADatabaseTarget target);
 
     /*!
      * \brief returns the ROWID for the newest entry in the respective database.
      */
-    int getLastEntry(ADatabaseTable table);
+    RowId_T getLastEntry(ADatabaseTable table);
 
     /*!
      * \brief returns a list of ROWID's in the flights table for which foreign key constraints
@@ -358,6 +383,13 @@ public:
      */
     QStringList getTemplateTableNames();
 
+    /*!
+     * \brief getUserDataState returns a struct containing the current amount of entries in the tails and
+     * pilots tables.
+     * \return
+     */
+    UserDataState getUserDataState();
+
     /*!
      * \brief getMinimumDatabaseRevision returns the minimum required database revision number required by the application.
      */

+ 106 - 10
src/functions/adate.cpp

@@ -1,29 +1,125 @@
 #include "adate.h"
 
-QDate ADate::formatInput(QString user_input, Opl::Date::ADateFormat format)
+QDate ADate::parseInput(QString &io_user_input, Opl::Date::ADateFormat format)
 {
+    // Try input string
     const QString &format_string = ADATEFORMATSMAP.value(format);
-    QDate return_date = QDate::fromString(user_input, format_string);
+    QDate return_date = QDate::fromString(io_user_input, format_string);
     if (return_date.isValid())
         return return_date;
 
-     // If not successfull, try to fix the user input, it is allowable to ommit date seperators
+    // try to fix the user input
+    tryToFix(io_user_input, format);
+
+    return  QDate::fromString(io_user_input, format_string);
+}
+
+void ADate::tryToFix(QString &io_user_input, Opl::Date::ADateFormat format)
+{
+
+    if (io_user_input.length() < 10) {
+        if (containsSeperator(io_user_input)) {
+            padZeroes(io_user_input);
+        }
+        else {
+            addSeperators(io_user_input, format);
+            padZeroes(io_user_input);
+        }
+    }
+
+    if (io_user_input.length() == 8)
+        padCentury(io_user_input, format);
+
+}
+
+// Input contains seperators and is of length 8
+void ADate::padCentury(QString &io_user_input, Opl::Date::ADateFormat format)
+{
+    switch (format) {
+    case Opl::Date::ADateFormat::ISODate: {
+        int year = io_user_input.leftRef(2).toInt();
+        if (year > 50)
+            io_user_input.prepend(QStringLiteral("19"));
+        else
+            io_user_input.prepend(QStringLiteral("20"));
+        break;
+    }
+    case Opl::Date::ADateFormat::DE: {
+        int year = io_user_input.rightRef(2).toInt();
+        if (year > 50)
+            io_user_input.insert(6, QStringLiteral("19"));
+        else
+            io_user_input.insert(6, QStringLiteral("20"));
+        break;
+    }
+    case Opl::Date::ADateFormat::EN: {
+        int year = io_user_input.rightRef(2).toInt();
+        if (year > 50)
+            io_user_input.insert(6, QStringLiteral("19"));
+        else
+            io_user_input.insert(6, QStringLiteral("20"));
+        break;
+    }
+    }
+    DEB << "Padded century: " << io_user_input;
+}
+
+void ADate::padZeroes(QString &io_user_input)
+{
+    const auto unpadded_start = QRegularExpression(QStringLiteral("^\\d{1}\\W"));
+    const auto unpadded_middle = QRegularExpression(QStringLiteral("\\W\\d\\W"));
+    const auto unpadded_end = QRegularExpression(QStringLiteral("\\W\\d$"));
+
+    auto match = unpadded_start.match(io_user_input);
+    if (match.hasMatch())
+        io_user_input.insert(match.capturedStart(), QLatin1Char('0'));
+
+    match = unpadded_middle.match(io_user_input);
+    if (match.hasMatch())
+        io_user_input.insert(match.capturedStart() + 1, QLatin1Char('0'));
+
+    match = unpadded_end.match(io_user_input);
+    if (match.hasMatch())
+        io_user_input.insert(match.capturedStart() + 1, QLatin1Char('0'));
+    DEB << "Padded zeroes: " << io_user_input;
+}
+// 10.10.2020
+void ADate::addSeperators(QString &io_user_input, const Opl::Date::ADateFormat &format)
+{
     switch (format) {
     case Opl::Date::ADateFormat::ISODate:
-        user_input.insert(4, QLatin1Char('-'));
-        user_input.insert(7, QLatin1Char('-'));
+        if (io_user_input.length() > 7) {
+            io_user_input.insert(4, QLatin1Char('-'));
+            io_user_input.insert(7, QLatin1Char('-'));
+        } else {
+            io_user_input.insert(2, QLatin1Char('-'));
+            io_user_input.insert(5, QLatin1Char('-'));
+        }
         break;
     case Opl::Date::ADateFormat::DE:
-        user_input.insert(2, QLatin1Char('.'));
-        user_input.insert(5, QLatin1Char('.'));
+            io_user_input.insert(2, QLatin1Char('.'));
+            io_user_input.insert(5, QLatin1Char('.'));
         break;
     case Opl::Date::ADateFormat::EN:
-        user_input.insert(2, QLatin1Char('/'));
-        user_input.insert(5, QLatin1Char('/'));
+            io_user_input.insert(2, QLatin1Char('/'));
+            io_user_input.insert(5, QLatin1Char('/'));
         break;
     }
+    DEB << "Added Seperators: " << io_user_input;
+}
+
+bool ADate::containsSeperator(const QString &user_input)
+{
+    if (user_input.contains(QLatin1Char('.')))
+        return true;
+    if (user_input.contains(QLatin1Char('-')))
+        return true;
+    if (user_input.contains(QLatin1Char('/')))
+        return true;
+
+    DEB << "No Seperators found.";
 
-    return  QDate::fromString(user_input, format_string);
+    return false;
 }
 
 const QStringList& ADate::getDisplayNames()

+ 18 - 2
src/functions/adate.h

@@ -27,10 +27,26 @@ class ADate
 {
 public:
     /*!
-     * \brief formatInput takes a user-provided input and tries to convert it to a QDate.
+     * \brief takes a user-provided input and tries to convert it to a (valid) QDate.
      * \return QDate (invalid if input not recognized)
      */
-    static QDate formatInput(QString user_input, Opl::Date::ADateFormat format);
+    static QDate parseInput(QString &io_user_input, Opl::Date::ADateFormat format);
+
+    static void tryToFix(QString &io_user_input, Opl::Date::ADateFormat format);
+
+    /*!
+     * \brief padCentury adds the century to a date where it was omitted
+     */
+    static void padCentury(QString &io_user_input, Opl::Date::ADateFormat format);
+
+    /*!
+     * \brief pads a user-provided date string with 0s to facilitate conversion to QDate
+     */
+    static void padZeroes(QString &io_user_input);
+
+    static void addSeperators(QString &io_user_input, const Opl::Date::ADateFormat &format);
+
+    static bool containsSeperator(const QString &user_input);
 
     /*!
      * \brief Reimplements QDate::toString to accept Opl::Date::ADateFormat enums

+ 18 - 10
src/functions/alog.cpp

@@ -73,6 +73,14 @@ bool init(bool log_debug)
     }
 }
 
+// Maintain compatibility with older Qt versions using QTextStream::flush() but avoiding deprecation
+// warning with newer versions using Qt::endl
+#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
+#define end_line Qt::endl
+#else
+#define end_line '\n' << flush
+#endif
+
 /*!
  * \brief aMessageHandler Intercepts Messages and prints to console and log file
  *
@@ -101,25 +109,25 @@ void aMessageHandler(QtMsgType type, const QMessageLogContext &context,
 
     switch (type) {
         case QtDebugMsg:
-            QTextStream(stdout) << DEB_HEADER_CONSOLE << msg << endl << D_SPACER << function << "\033[m" << endl;
+            QTextStream(stdout) << DEB_HEADER_CONSOLE << msg << end_line << D_SPACER << function << "\033[m" << end_line;
             if(logDebug)
-                log_stream << timeNow() << DEB_HEADER << msg << D_SPACER << function << endl;
+                log_stream << timeNow() << DEB_HEADER << msg << D_SPACER << function << end_line;
             break;
         case QtInfoMsg:
-            log_stream << timeNow() << INFO_HEADER << msg << SPACER << function << endl;
-            QTextStream(stdout) << INFO_HEADER_CONSOLE << msg << endl;
+            log_stream << timeNow() << INFO_HEADER << msg << SPACER << function << end_line;
+            QTextStream(stdout) << INFO_HEADER_CONSOLE << msg << end_line;
             break;
         case QtWarningMsg:
-            log_stream << timeNow() << WARN_HEADER << msg << SPACER << endl;
-            QTextStream(stdout) << WARN_HEADER_CONSOLE << msg << endl;
+            log_stream << timeNow() << WARN_HEADER << msg << SPACER << end_line;
+            QTextStream(stdout) << WARN_HEADER_CONSOLE << msg << end_line;
             break;
         case QtCriticalMsg:
-            log_stream << timeNow() << CRIT_HEADER << msg << SPACER << endl;
-            QTextStream(stdout) << CRIT_HEADER_CONSOLE << msg << endl;
+            log_stream << timeNow() << CRIT_HEADER << msg << SPACER << end_line;
+            QTextStream(stdout) << CRIT_HEADER_CONSOLE << msg << end_line;
             break;
     default:
-            log_stream << timeNow() << INFO_HEADER << msg << function << endl;
-            QTextStream(stdout) << INFO_HEADER_CONSOLE << msg << endl;
+            log_stream << timeNow() << INFO_HEADER << msg << function << end_line;
+            QTextStream(stdout) << INFO_HEADER_CONSOLE << msg << end_line;
             break;
     }
 }

+ 21 - 0
src/functions/areadcsv.cpp

@@ -48,3 +48,24 @@ QVector<QStringList> aReadCsv(QString filename)
     }
     return values;
 }
+
+/*!
+ * \brief aReadCsvAsRows reads from CSV
+ * \param file_name input file path
+ * \return QVector<QStringList> of the CSV data, where each QStringList is one row of the input file
+ */
+QVector<QStringList> aReadCsvAsRows(const QString &file_name)
+{
+    QFile csvfile(file_name);
+    csvfile.open(QIODevice::ReadOnly);
+    QTextStream stream(&csvfile);
+
+    QVector<QStringList> csv_rows;
+
+    // Read each line
+    while (!stream.atEnd()) {
+        const QString line = stream.readLine();
+        csv_rows.append(line.split(','));
+    }
+    return csv_rows;
+}

+ 2 - 0
src/functions/areadcsv.h

@@ -21,5 +21,7 @@
 #include<QtCore>
 
 QVector<QStringList> aReadCsv(QString filename);
+QVector<QStringList> aReadCsvAsRows(const QString &filename);
+
 
 #endif // AREADCSV_H

+ 2 - 31
src/gui/dialogues/firstrundialog.cpp

@@ -66,6 +66,8 @@ FirstRunDialog::FirstRunDialog(QWidget *parent) :
                     ADate::getFormatString(Opl::Date::ADateFormat::ISODate));
         date_edit->setDate(QDate::currentDate());
     }
+    // De-activate non-default date formats for now, implement in future version
+    ui->dateFormatComboBox->setVisible(false);
     // Debug - use ctrl + t to enable branchLineEdit to select from which git branch the templates are pulled
     ui->branchLineEdit->setVisible(false);
 }
@@ -298,17 +300,6 @@ void FirstRunDialog::writeSettings()
     ASettings::write(ASettings::UserData::DisplaySelfAs, ui->aliasComboBox->currentIndex());
     ASettings::write(ASettings::Main::LogbookView, ui->logbookViewComboBox->currentIndex());
 
-    switch (ui->currWarningCheckBox->checkState()) {
-    case Qt::CheckState::Checked:
-        ASettings::write(ASettings::UserData::CurrWarningEnabled, true);
-        break;
-    case Qt::CheckState::Unchecked:
-        ASettings::write(ASettings::UserData::CurrWarningEnabled, false);
-        break;
-    default:
-        break;
-    }
-    ASettings::write(ASettings::UserData::CurrWarningThreshold, ui->currWarningThresholdSpinBox->value());
     ASettings::write(ASettings::Main::Style, ui->styleComboBox->currentText());
     QSettings settings;
     settings.sync();
@@ -440,26 +431,6 @@ void FirstRunDialog::on_styleComboBox_currentTextChanged(const QString &new_styl
     }
 }
 
-void FirstRunDialog::on_currWarningCheckBox_stateChanged(int arg1)
-{
-    switch (arg1) {
-    case Qt::CheckState::Checked:
-        ASettings::write(ASettings::UserData::CurrWarningEnabled, true);
-        break;
-    case Qt::CheckState::Unchecked:
-        ASettings::write(ASettings::UserData::CurrWarningEnabled, false);
-        break;
-    default:
-        break;
-    }
-    ASettings::write(ASettings::UserData::CurrWarningThreshold, arg1);
-}
-
-void FirstRunDialog::on_currWarningThresholdSpinBox_valueChanged(int arg1)
-{
-    ASettings::write(ASettings::UserData::CurrWarningThreshold, arg1);
-}
-
 void FirstRunDialog::on_currCustom1LineEdit_editingFinished()
 {
     ASettings::write(ASettings::UserData::Custom1CurrencyName, ui->currCustom1LineEdit->text());

+ 0 - 4
src/gui/dialogues/firstrundialog.h

@@ -44,10 +44,6 @@ private slots:
 
     void on_styleComboBox_currentTextChanged(const QString &new_style_setting);
 
-    void on_currWarningCheckBox_stateChanged(int arg1);
-
-    void on_currWarningThresholdSpinBox_valueChanged(int arg1);
-
     void on_currCustom1LineEdit_editingFinished();
 
     void on_currCustom2LineEdit_editingFinished();

+ 15 - 46
src/gui/dialogues/firstrundialog.ui

@@ -31,7 +31,7 @@
    <item row="1" column="0" colspan="2">
     <widget class="QStackedWidget" name="stackedWidget">
      <property name="currentIndex">
-      <number>4</number>
+      <number>1</number>
      </property>
      <widget class="QWidget" name="personalDataPage">
       <layout class="QGridLayout" name="gridLayout_9">
@@ -432,6 +432,20 @@
          </item>
         </layout>
        </item>
+       <item row="2" column="0" colspan="2">
+        <widget class="Line" name="line">
+         <property name="orientation">
+          <enum>Qt::Horizontal</enum>
+         </property>
+        </widget>
+       </item>
+       <item row="1" column="0">
+        <widget class="QLabel" name="dateFormatLabel">
+         <property name="text">
+          <string>Date Format</string>
+         </property>
+        </widget>
+       </item>
        <item row="1" column="1">
         <widget class="QComboBox" name="dateFormatComboBox"/>
        </item>
@@ -448,51 +462,6 @@
          </property>
         </widget>
        </item>
-       <item row="1" column="0">
-        <widget class="QLabel" name="dateFormatLabel">
-         <property name="text">
-          <string>Date Format</string>
-         </property>
-        </widget>
-       </item>
-       <item row="4" column="0" colspan="2">
-        <layout class="QHBoxLayout" name="horizontalLayout">
-         <item>
-          <widget class="QCheckBox" name="currWarningCheckBox">
-           <property name="text">
-            <string>Warn me about expiring currencies</string>
-           </property>
-           <property name="checked">
-            <bool>true</bool>
-           </property>
-          </widget>
-         </item>
-         <item>
-          <widget class="QSpinBox" name="currWarningThresholdSpinBox">
-           <property name="maximum">
-            <number>365</number>
-           </property>
-           <property name="value">
-            <number>30</number>
-           </property>
-          </widget>
-         </item>
-         <item>
-          <widget class="QLabel" name="label_3">
-           <property name="text">
-            <string>days before expiry</string>
-           </property>
-          </widget>
-         </item>
-        </layout>
-       </item>
-       <item row="2" column="0" colspan="2">
-        <widget class="Line" name="line">
-         <property name="orientation">
-          <enum>Qt::Horizontal</enum>
-         </property>
-        </widget>
-       </item>
       </layout>
      </widget>
      <widget class="QWidget" name="flightLoggingPage">

+ 274 - 329
src/gui/dialogues/newflightdialog.cpp

@@ -26,130 +26,37 @@
 #include "src/functions/adate.h"
 #include "src/functions/alog.h"
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-///                                         constants                                           ///
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-static const auto NAME_RX     = QLatin1String("((\\p{L}+)?('|\\-|,)?(\\p{L}+)?)");
-static const auto ADD_NAME_RX = QLatin1String("(\\s?(\\p{L}+('|\\-|,)?\\p{L}+?))?");
-
-static const auto TIME_VALID_RGX       = QRegularExpression(
-            "([01]?[0-9]|2[0-3]):?[0-5][0-9]?");
-static const auto LOC_VALID_RGX        = QRegularExpression(
-            "[a-zA-Z0-9]{1,4}");
-static const auto AIRCRAFT_VALID_RGX   = QRegularExpression(
-            "\\w+\\-?(\\w+)?");
-static const auto NAME_VALID_RGX       = QRegularExpression(
-            NAME_RX + ADD_NAME_RX + ADD_NAME_RX + ADD_NAME_RX + ",?\\s?" // up to 4 first names
-            + NAME_RX + ADD_NAME_RX + ADD_NAME_RX + ADD_NAME_RX );         // up to 4 last names
-static const auto DATE_VALID_RGX       = QRegularExpression(
-            "^([1-9][0-9]{3}).?(1[0-2]|0[1-9]).?(3[01]|0[1-9]|[12][0-9])?$");
-static const auto SELF_RX              = QRegularExpression(
-            "self", QRegularExpression::CaseInsensitiveOption);
-
-static const auto MANDATORY_LINE_EDITS_DISPLAY_NAMES = QMap<int, QString> {
-    {0, QObject::tr("Date of Flight")},      {1, QObject::tr("Departure Airport")},
-    {2, QObject::tr("Destination Airport")}, {3, QObject::tr("Time Off Blocks")},
-    {4, QObject::tr("Time on Blocks")},      {5, QObject::tr("PIC Name")},
-    {6, QObject::tr("Aircraft Registration")}
-};
-
-//
-// MandatoryLineEdits definition
-// Ugly but works
-NewFlightDialog::MandatoryLineEdits::MandatoryLineEdits(std::initializer_list<QLineEdit*> init_list)
-    : lineEdits(init_list), lineEditsValid(QBitArray(init_list.size()))
-{}
-void NewFlightDialog::MandatoryLineEdits::operator= (std::initializer_list<QLineEdit*> init_list)
-{
-    lineEdits = init_list;
-    lineEditsValid.resize(init_list.size());
-}
-bool NewFlightDialog::MandatoryLineEdits::contains(QLineEdit* line_edit)
-{
-    return lineEdits.contains(line_edit);
-}
-void NewFlightDialog::MandatoryLineEdits::validate(QLineEdit* line_edit)
-{
-    lineEditsValid.setBit(lineEdits.indexOf(line_edit), true);
-}
-void NewFlightDialog::MandatoryLineEdits::unvalidate(QLineEdit* line_edit)
-{
-    lineEditsValid.setBit(lineEdits.indexOf(line_edit), false);
-}
-int NewFlightDialog::MandatoryLineEdits::countValid()
-{
-    return lineEditsValid.count(true);
-}
-int NewFlightDialog::MandatoryLineEdits::size()
-{
-    return lineEditsValid.size();
-}
-bool NewFlightDialog::MandatoryLineEdits::validAt(int idx)
-{
-    return lineEditsValid[idx];
-}
-bool NewFlightDialog::MandatoryLineEdits::allValid()
-{
-    return lineEditsValid.count(true) == lineEdits.size();
-}
-QLineEdit* NewFlightDialog::MandatoryLineEdits::operator[] (int idx)
-{
-    return lineEdits[idx];
-}
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-///                                      Construction                                           ///
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-/// [F] The general idea for this dialog is this:
-/// - Most line edits have validators and completers.
-/// - Validators are based on regular expressions, serving as raw input validation
-/// - The Completers are based off the database and provide auto-completion
-/// - mandatory line edits only emit editing finished if their content has passed
-///   raw input validation or focus is lost.
-/// - Editing finished triggers validating inputs by mapping them to Database values
-///   where required and results in either pass or fail.
-/// - A QBitArray is mainained containing the state of validity of the mandatory line edits
-/// - The deducted entries are automatically filled if all mandatory entries
-/// are valid.
-/// - Comitting an entry to the database is only allowed if all mandatory inputs are valid.
-///
-/// if the user presses "OK", check if all mandatory inputs are valid,
-/// check if optional user inputs are valid and commit.
-///
-/// For the completion and mapping, I have settled on a more low-level approach using
-/// Completers based on QStringLists and mapping with QMaps.
-///
-/// I implemented the Completers and mapping based on a QSqlTableModel which would
-/// have been quite nice, since it would keep all data in one place, providing both completion
-/// and mapping in one model.
-/// But as we have seen before with the more high-level qt classes, they are quite slow on execution
-/// when used for tasks they were probably not designed to do.
-/// Mapping a registration to an ID for example took around 300ms, which is very
-/// noticeable in the UI and not an acceptable user experience. Using QStringLists and QMaps
-/// this goes down to around 5ms.
-
-NewFlightDialog::NewFlightDialog(QWidget *parent) :
-    QDialog(parent),
-    ui(new Ui::NewFlight)
+const auto LOC_LINE_EDIT_NAME   = QLatin1String("Loc");
+const auto ACFT_LINE_EDIT_NAME  = QLatin1String("acft");
+const auto TIME_LINE_EDIT_NAME  = QLatin1String("Time");
+const auto PILOT_LINE_EDIT_NAME = QLatin1String("Name");
+const auto SELF                 = QLatin1String("self");
+
+NewFlightDialog::NewFlightDialog(ACompletionData &completion_data,
+                                 QWidget *parent)
+    : QDialog(parent),
+      ui(new Ui::NewFlight),
+      completionData(completion_data)
 {
     ui->setupUi(this);
     flightEntry = AFlightEntry();
     setup();
     if (ASettings::read(ASettings::FlightLogging::FunctionComboBox).toString() == QLatin1String("SIC")) {
         ui->picNameLineEdit->setText(QString());
-        ui->secondPilotNameLineEdit->setText(QStringLiteral("self"));
+        ui->secondPilotNameLineEdit->setText(SELF);
     }
     if(ASettings::read(ASettings::FlightLogging::FunctionComboBox).toString() == QLatin1String("PIC")){
-        ui->picNameLineEdit->setText(QStringLiteral("self"));
+        ui->picNameLineEdit->setText(SELF);
         emit ui->picNameLineEdit->editingFinished();
     }
 }
 
-NewFlightDialog::NewFlightDialog(int row_id, QWidget *parent) :
-    QDialog(parent),
-    ui(new Ui::NewFlight)
+NewFlightDialog::NewFlightDialog(ACompletionData &completion_data,
+                                 int row_id,
+                                 QWidget *parent)
+    : QDialog(parent),
+      ui(new Ui::NewFlight),
+      completionData(completion_data)
 {
     ui->setupUi(this);
     flightEntry = aDB->getFlightEntry(row_id);
@@ -159,11 +66,12 @@ NewFlightDialog::NewFlightDialog(int row_id, QWidget *parent) :
 
 NewFlightDialog::~NewFlightDialog()
 {
+    LOG << "Closing NF Dialog";
     delete ui;
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-///                        Methods - setup and maintenance of dialog                            ///
+///                        setup and maintenance of dialog                            ///
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 void NewFlightDialog::setup()
@@ -176,57 +84,10 @@ void NewFlightDialog::setup()
     setupRawInputValidation();
     setupSignalsAndSlots();
     readSettings();
+    setupUi();
 
-    ui->flightDataTabWidget->setCurrentIndex(0);
-    ui->flightDataTabWidget->removeTab(2); // hide calendar widget
-    ui->doftLineEdit->setText(QDate::currentDate().toString(Qt::ISODate));
-    emit ui->doftLineEdit->editingFinished();
     ui->deptLocLineEdit->setFocus();
 }
-void NewFlightDialog::readSettings()
-{
-    DEB << "Reading Settings...";
-    QSettings settings;
-    ui->FunctionComboBox->setCurrentText(ASettings::read(ASettings::FlightLogging::Function).toString());
-    ui->ApproachComboBox->setCurrentIndex(ASettings::read(ASettings::FlightLogging::Approach).toInt());
-
-    ASettings::read(ASettings::FlightLogging::PilotFlying).toBool() ? ui->PilotFlyingCheckBox->setChecked(true)
-                                                          : ui->PilotMonitoringCheckBox->setChecked(true);
-
-    ui->TakeoffSpinBox->setValue(ASettings::read(ASettings::FlightLogging::NumberTakeoffs).toInt());
-    ui->TakeoffSpinBox->value() > 0 ? ui->TakeoffCheckBox->setChecked(true)
-                                    : ui->TakeoffCheckBox->setChecked(false);
-    ui->LandingSpinBox->setValue(ASettings::read(ASettings::FlightLogging::NumberLandings).toInt());
-    ui->LandingSpinBox->value() > 0 ? ui->LandingCheckBox->setChecked(true)
-                                    : ui->LandingCheckBox->setChecked(false);
-    if (ASettings::read(ASettings::FlightLogging::LogIFR).toBool()) {
-        ui->IfrCheckBox->setChecked(true);
-    } else {
-        ui->VfrCheckBox->setChecked(true);
-    }
-
-    ui->FlightNumberLineEdit->setText(ASettings::read(ASettings::FlightLogging::FlightNumberPrefix).toString());
-
-    // Debug
-    ASettings::write(ASettings::FlightLogging::FlightTimeFormat, Opl::Time::Default);
-    TODO << "Support for Decimal Logging is not implemented yet.";
-    flightTimeFormat = static_cast<Opl::Time::FlightTimeFormat>(
-                ASettings::read(ASettings::FlightLogging::FlightTimeFormat).toInt());
-
-
-}
-
-void NewFlightDialog::writeSettings()
-{
-    DEB << "Writing Settings...";
-
-    ASettings::write(ASettings::FlightLogging::Function, ui->FunctionComboBox->currentText());
-    ASettings::write(ASettings::FlightLogging::Approach, ui->ApproachComboBox->currentIndex());
-    ASettings::write(ASettings::FlightLogging::PilotFlying, ui->PilotFlyingCheckBox->isChecked());
-    ASettings::write(ASettings::FlightLogging::NumberTakeoffs, ui->TakeoffSpinBox->value());
-    ASettings::write(ASettings::FlightLogging::NumberLandings, ui->LandingSpinBox->value());
-    ASettings::write(ASettings::FlightLogging::LogIFR, ui->IfrCheckBox->isChecked());
-}
 
 void NewFlightDialog::setupButtonGroups()
 {
@@ -241,53 +102,57 @@ void NewFlightDialog::setupButtonGroups()
 
 void NewFlightDialog::setupRawInputValidation()
 {
-    // get Maps
-    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(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>
-            location_line_edit_settings {QStringLiteral("Loc"), &airportList, LOC_VALID_RGX};
-    const std::tuple<QString, QStringList*, QRegularExpression>
-            name_line_edit_settings {QStringLiteral("Name"), &pilotList, NAME_VALID_RGX};
-    const std::tuple<QString, QStringList*, QRegularExpression>
-            acft_line_edit_settings {QStringLiteral("acft"), &tailsList, AIRCRAFT_VALID_RGX};
-    const std::tuple<QString, QStringList*, QRegularExpression>
-            time_line_edit_settings {QStringLiteral("Time"), &tempList, TIME_VALID_RGX};
-    const QList<std::tuple<QString, QStringList*, QRegularExpression>> line_edit_settings = {
-        location_line_edit_settings,
-        name_line_edit_settings,
-        acft_line_edit_settings,
-        time_line_edit_settings
+    // Prepare Regular Expressions for QValidators
+    const auto name_rx       = QLatin1String("((\\p{L}+)?('|\\-|,)?(\\p{L}+)?)");
+    const auto add_name_rx   = QLatin1String("(\\s?(\\p{L}+('|\\-|,)?\\p{L}+?))?");
+    const auto time_valid_rx = QRegularExpression("([01]?[0-9]|2[0-3]):?[0-5][0-9]?");
+    const auto loc_valid_rx  = QRegularExpression("[a-zA-Z0-9]{1,4}");
+    const auto acft_valid_rx = QRegularExpression("\\w+\\-?(\\w+)?");
+    const auto name_valid_rx = QRegularExpression(
+                name_rx + add_name_rx + add_name_rx + add_name_rx + ",?\\s?" // up to 4 first names
+              + name_rx + add_name_rx + add_name_rx + add_name_rx );         // up to 4 last names
+
+    // create the structs holding the inititalisation data (completion lists and RXs) and store them in a map
+    const auto location_data = ValidationSetupData(&completionData.airportList, &loc_valid_rx);
+    const auto pilot_data    = ValidationSetupData(&completionData.pilotList, &name_valid_rx);
+    const auto aircraft_data = ValidationSetupData(&completionData.tailsList, &acft_valid_rx);
+    const auto time_data     = ValidationSetupData(&time_valid_rx);
+
+    const QHash<const QLatin1String*, const ValidationSetupData*> init_data_map = {
+        {&LOC_LINE_EDIT_NAME,  &location_data},
+        {&PILOT_LINE_EDIT_NAME, &pilot_data},
+        {&ACFT_LINE_EDIT_NAME, &aircraft_data},
+        {&TIME_LINE_EDIT_NAME, &time_data}
     };
-    //get line edits, set up completers and validators
-    const auto line_edits = ui->flightDataTab->findChildren<QLineEdit*>();
 
-    for (const auto &item : line_edit_settings) {
-        for (const auto &line_edit : line_edits) {
-            if(line_edit->objectName().contains(std::get<0>(item))) {
+    //get and set up line edits
+    const QList<QLineEdit*> line_edits = ui->flightDataTab->findChildren<QLineEdit*>();
+    const QList<const QLatin1String*> keys_list = init_data_map.keys();
+
+    for (const auto &line_edit : line_edits) {
+        for (const auto &key : keys_list ) {
+            if (line_edit->objectName().contains(*key)) {
+                // Fetch Data from the map and set up the line edit
+                const ValidationSetupData* init_data = init_data_map.value(key);
                 DEB << "Setting up: " << line_edit->objectName();
+
                 // Set Validator
-                auto validator = new QRegularExpressionValidator(std::get<2>(item), line_edit);
+                auto validator = new QRegularExpressionValidator(*init_data->validationRegEx, line_edit);
                 line_edit->setValidator(validator);
                 // Set Completer
-                auto completer = new QCompleter(*std::get<1>(item), line_edit);
-                completer->setCaseSensitivity(Qt::CaseInsensitive);
-                completer->setCompletionMode(QCompleter::PopupCompletion);
-                completer->setFilterMode(Qt::MatchContains);
-                line_edit->setCompleter(completer);
+                if (init_data->completionData) {
+                    auto completer = new QCompleter(*init_data->completionData, line_edit);
+                    completer->setCaseSensitivity(Qt::CaseInsensitive);
+                    completer->setCompletionMode(QCompleter::PopupCompletion);
+                    completer->setFilterMode(Qt::MatchContains);
+                    line_edit->setCompleter(completer);
+                }
             }
         }
     }
 
     // populate Mandatory Line Edits list and prepare QBitArray
+    mandatoryLineEditsValid.resize(7);
     mandatoryLineEdits = {
         ui->doftLineEdit,
         ui->deptLocLineEdit,
@@ -297,7 +162,6 @@ void NewFlightDialog::setupRawInputValidation()
         ui->picNameLineEdit,
         ui->acftLineEdit,
     };
-
     primaryTimeLineEdits = {
         ui->tofbTimeLineEdit,
         ui->tonbTimeLineEdit
@@ -316,25 +180,25 @@ void NewFlightDialog::setupSignalsAndSlots()
 
     for (const auto &line_edit : line_edits){
         line_edit->installEventFilter(this);
-        if(line_edit->objectName().contains(QStringLiteral("Loc"))){
+        if(line_edit->objectName().contains(LOC_LINE_EDIT_NAME)){
             QObject::connect(line_edit, &QLineEdit::textChanged,
                              this, &NewFlightDialog::onToUpperTriggered_textChanged);
         }
-        if(line_edit->objectName().contains(QStringLiteral("acft"))){
+        if(line_edit->objectName().contains(ACFT_LINE_EDIT_NAME)){
             QObject::connect(line_edit, &QLineEdit::textChanged,
                              this, &NewFlightDialog::onToUpperTriggered_textChanged);
         }
-        if(line_edit->objectName().contains(QStringLiteral("Name"))){
+        if(line_edit->objectName().contains(PILOT_LINE_EDIT_NAME)){
             QObject::connect(line_edit, &QLineEdit::editingFinished,
                              this, &NewFlightDialog::onPilotNameLineEdit_editingFinished);
         }
-        if(line_edit->objectName().contains(QStringLiteral("Time"))){
+        if(line_edit->objectName().contains(TIME_LINE_EDIT_NAME)){
             QObject::connect(line_edit, &QLineEdit::editingFinished,
                              this, &NewFlightDialog::onTimeLineEdit_editingFinished);
         }
     }
 #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)
-    for (const auto &line_edit : qAsConst(mandatoryLineEdits.lineEdits)) {
+    for (const auto &line_edit : qAsConst(mandatoryLineEdits)) {
         if(line_edit->objectName().contains(QStringLiteral("doft")))
             break;
         QObject::connect(line_edit->completer(), QOverload<const QString &>::of(&QCompleter::highlighted),
@@ -347,13 +211,67 @@ void NewFlightDialog::setupSignalsAndSlots()
                      this, &NewFlightDialog::onCalendarWidget_clicked);
 }
 
+void NewFlightDialog::setupUi()
+{
+    ui->flightDataTabWidget->setCurrentIndex(0);
+    ui->flightDataTabWidget->removeTab(2); // hide calendar widget
+    ui->doftLineEdit->setText(QDate::currentDate().toString(Qt::ISODate));
+    emit ui->doftLineEdit->editingFinished();
+    ui->doftLineEdit->setToolTip(tr("Date Format: %1").arg(ADate::getFormatString(Opl::Date::ADateFormat::ISODate)));
+}
+
+void NewFlightDialog::readSettings()
+{
+    DEB << "Reading Settings...";
+    QSettings settings;
+    ui->FunctionComboBox->setCurrentText(ASettings::read(ASettings::FlightLogging::Function).toString());
+    ui->ApproachComboBox->setCurrentIndex(ASettings::read(ASettings::FlightLogging::Approach).toInt());
+
+    ASettings::read(ASettings::FlightLogging::PilotFlying).toBool() ? ui->PilotFlyingCheckBox->setChecked(true)
+                                                          : ui->PilotMonitoringCheckBox->setChecked(true);
+
+    ui->TakeoffSpinBox->setValue(ASettings::read(ASettings::FlightLogging::NumberTakeoffs).toInt());
+    ui->TakeoffSpinBox->value() > 0 ? ui->TakeoffCheckBox->setChecked(true)
+                                    : ui->TakeoffCheckBox->setChecked(false);
+    ui->LandingSpinBox->setValue(ASettings::read(ASettings::FlightLogging::NumberLandings).toInt());
+    ui->LandingSpinBox->value() > 0 ? ui->LandingCheckBox->setChecked(true)
+                                    : ui->LandingCheckBox->setChecked(false);
+    if (ASettings::read(ASettings::FlightLogging::LogIFR).toBool()) {
+        ui->IfrCheckBox->setChecked(true);
+    } else {
+        ui->VfrCheckBox->setChecked(true);
+    }
+
+    ui->FlightNumberLineEdit->setText(ASettings::read(ASettings::FlightLogging::FlightNumberPrefix).toString());
+
+    // Debug
+    ASettings::write(ASettings::FlightLogging::FlightTimeFormat, Opl::Time::Default);
+    TODO << "Support for Decimal Logging is not implemented yet.";
+    flightTimeFormat = static_cast<Opl::Time::FlightTimeFormat>(
+                ASettings::read(ASettings::FlightLogging::FlightTimeFormat).toInt());
+
+
+}
+
+void NewFlightDialog::writeSettings()
+{
+    DEB << "Writing Settings...";
+
+    ASettings::write(ASettings::FlightLogging::Function, ui->FunctionComboBox->currentText());
+    ASettings::write(ASettings::FlightLogging::Approach, ui->ApproachComboBox->currentIndex());
+    ASettings::write(ASettings::FlightLogging::PilotFlying, ui->PilotFlyingCheckBox->isChecked());
+    ASettings::write(ASettings::FlightLogging::NumberTakeoffs, ui->TakeoffSpinBox->value());
+    ASettings::write(ASettings::FlightLogging::NumberLandings, ui->LandingSpinBox->value());
+    ASettings::write(ASettings::FlightLogging::LogIFR, ui->IfrCheckBox->isChecked());
+}
+
 bool NewFlightDialog::eventFilter(QObject* object, QEvent* event)
 {
     auto line_edit = qobject_cast<QLineEdit*>(object);
     if (line_edit != nullptr) {
         if (mandatoryLineEdits.contains(line_edit) && event->type() == QEvent::FocusIn) {
-            mandatoryLineEdits.unvalidate(line_edit);
-            DEB << "Editing " << line_edit->objectName();
+            //invalidateMandatoryLineEdit(mandatoryLineEdit(mandatoryLineEdits.indexOf(line_edit)));
+            //DEB << "Editing " << line_edit->objectName();
             // set verification bit to false when entering a mandatory line edit
             return false;
         }
@@ -361,7 +279,7 @@ bool NewFlightDialog::eventFilter(QObject* object, QEvent* event)
             // show completion menu when pressing down arrow
             QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
             if (keyEvent->key() == Qt::Key_Down) {
-                DEB << "Key down event.";
+                //DEB << "Key down event.";
                 line_edit->completer()->complete();
             }
             return false;
@@ -370,7 +288,7 @@ bool NewFlightDialog::eventFilter(QObject* object, QEvent* event)
             // show completion menu when pressing down arrow
             QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
             if (keyEvent->key() == Qt::Key_Down) {
-                DEB << "Key down event.";
+                //DEB << "Key down event.";
                 line_edit->completer()->complete();
             }
             return false;
@@ -385,12 +303,7 @@ bool NewFlightDialog::eventFilter(QObject* object, QEvent* event)
  * \brief Fills the deductable items in the Dialog based on mandatory data ui selections.
  */
 void NewFlightDialog::fillDeductibleData()
-{
-    // check if mandatory line edits are valid
-    if (!mandatoryLineEdits.allValid()) {
-        return;
-    }
-
+{    
     //zero out labels and line edits to delete previous calculations
     QList<QLineEdit*>   LE = {ui->tSPSETimeLineEdit, ui->tSPMETimeLineEdit, ui->tMPTimeLineEdit,    ui->tIFRTimeLineEdit,
                               ui->tNIGHTTimeLineEdit,ui->tPICTimeLineEdit,  ui->tPICUSTimeLineEdit, ui->tSICTimeLineEdit,
@@ -404,53 +317,18 @@ void NewFlightDialog::fillDeductibleData()
     const auto tonb = ATime::fromString(ui->tonbTimeLineEdit->text());
     const auto tblk = ATime::blocktime(tofb, tonb);
     const auto block_time_string = ATime::toString(tblk, flightTimeFormat);
-    const auto block_minutes = ATime::toMinutes(tblk);
 
     ui->tblkTimeLineEdit->setText(block_time_string);
     // get acft data and fill deductible entries
-    auto acft = aDB->getTailEntry(tailsIdMap.value(ui->acftLineEdit->text()));
-    if (acft.getData().isEmpty())
-        DEB << "Error: No valid aircraft object available, unable to deterime auto times.";
-
-    // SP SE
-    if(acft.getData().value(Opl::Db::TAILS_MULTIPILOT).toInt() == 0
-            && acft.getData().value(Opl::Db::TAILS_MULTIENGINE).toInt() == 0){
-        ui->tSPSETimeLineEdit->setText(block_time_string);
-        ui->tSPSELabel->setText(block_time_string);
-    }
-    // SP ME
-    if(acft.getData().value(Opl::Db::TAILS_MULTIPILOT).toInt() == 0
-            && acft.getData().value(Opl::Db::TAILS_MULTIENGINE).toInt() == 1){
-        ui->tSPMETimeLineEdit->setText(block_time_string);
-        ui->tSPMELabel->setText(block_time_string);
-    }
-    // MP
-    if(acft.getData().value(Opl::Db::TAILS_MULTIPILOT).toInt() == 1){
-        ui->tMPTimeLineEdit->setText(block_time_string);
-        ui->tMPLabel->setText(block_time_string);
-    }
+    auto acft = aDB->getTailEntry(completionData.tailsIdMap.key(ui->acftLineEdit->text()));
+
     // TOTAL
-    ui->tblkLabel->setText(QLatin1String("<b>") + block_time_string
-                           + QLatin1String("</b>"));
+    ui->tblkLabel->setText(QLatin1String("<b>") + block_time_string + QLatin1String("</b>"));
     // IFR
     if(ui->IfrCheckBox->isChecked()){
         ui->tIFRTimeLineEdit->setText(block_time_string);
         ui->tIFRLabel->setText(block_time_string);
     }
-    // Night
-    QString dept_date = ui->doftLineEdit->text() + 'T'
-            + ATime::toString(tofb);
-    auto dept_date_time = QDateTime::fromString(dept_date, QStringLiteral("yyyy-MM-ddThh:mm"));
-    const int night_angle = ASettings::read(ASettings::FlightLogging::NightAngle).toInt();
-    auto night_time = ATime::fromMinutes(ACalc::calculateNightTime(
-                                             ui->deptLocLineEdit->text(),
-                                             ui->destLocLineEdit->text(),
-                                             dept_date_time,
-                                             block_minutes,
-                                             night_angle));
-
-    ui->tNIGHTTimeLineEdit->setText(ATime::toString(night_time, flightTimeFormat));
-    ui->tNIGHTLabel->setText(ATime::toString(night_time, flightTimeFormat));
     // Function times
     switch (ui->FunctionComboBox->currentIndex()) {
     case 0://PIC
@@ -475,6 +353,49 @@ void NewFlightDialog::fillDeductibleData()
         ui->tPICTimeLineEdit->setText(block_time_string);
         ui->tPICLabel->setText(block_time_string);
     }
+
+    // if location data is available, fill night time
+    if (locLineEditsValid()) {
+
+        const auto block_minutes = ATime::toMinutes(tblk);
+        QString dept_date = ui->doftLineEdit->text() + 'T' + ATime::toString(tofb);
+        auto dept_date_time = QDateTime::fromString(dept_date, QStringLiteral("yyyy-MM-ddThh:mm"));
+        const int night_angle = ASettings::read(ASettings::FlightLogging::NightAngle).toInt();
+        auto night_time = ATime::fromMinutes(ACalc::calculateNightTime(
+                                                 ui->deptLocLineEdit->text(),
+                                                 ui->destLocLineEdit->text(),
+                                                 dept_date_time,
+                                                 block_minutes,
+                                                 night_angle));
+
+        ui->tNIGHTTimeLineEdit->setText(ATime::toString(night_time, flightTimeFormat));
+        ui->tNIGHTLabel->setText(ATime::toString(night_time, flightTimeFormat));
+    }
+
+    // if acft data is available, fill additional times
+    if (acftLineEditValid()) {
+
+        if (acft.getData().isEmpty())
+            WARN("Error: No valid aircraft object available, unable to deterime auto times.");
+
+        // SP SE
+        if(acft.getData().value(Opl::Db::TAILS_MULTIPILOT).toInt() == 0
+                && acft.getData().value(Opl::Db::TAILS_MULTIENGINE).toInt() == 0){
+            ui->tSPSETimeLineEdit->setText(block_time_string);
+            ui->tSPSELabel->setText(block_time_string);
+        }
+        // SP ME
+        if(acft.getData().value(Opl::Db::TAILS_MULTIPILOT).toInt() == 0
+                && acft.getData().value(Opl::Db::TAILS_MULTIENGINE).toInt() == 1){
+            ui->tSPMETimeLineEdit->setText(block_time_string);
+            ui->tSPMELabel->setText(block_time_string);
+        }
+        // MP
+        if(acft.getData().value(Opl::Db::TAILS_MULTIPILOT).toInt() == 1){
+            ui->tMPTimeLineEdit->setText(block_time_string);
+            ui->tMPLabel->setText(block_time_string);
+        }
+    }
 }
 
 /*!
@@ -487,7 +408,7 @@ void NewFlightDialog::fillDeductibleData()
 RowData_T NewFlightDialog::collectInput()
 {
     RowData_T newData;
-    DEB << "Collecting Input...";
+    //DEB << "Collecting Input...";
     //Block Time
     const auto tofb = ATime::fromString(ui->tofbTimeLineEdit->text());
     const auto tonb = ATime::fromString(ui->tonbTimeLineEdit->text());
@@ -501,11 +422,11 @@ RowData_T NewFlightDialog::collectInput()
     newData.insert(Opl::Db::FLIGHTS_TONB, ATime::toMinutes(tonb));
     newData.insert(Opl::Db::FLIGHTS_TBLK, block_minutes);
     // Aircraft
-    newData.insert(Opl::Db::FLIGHTS_ACFT, tailsIdMap.value(ui->acftLineEdit->text()));
+    newData.insert(Opl::Db::FLIGHTS_ACFT, completionData.tailsIdMap.key(ui->acftLineEdit->text()));
     // Pilots
-    newData.insert(Opl::Db::FLIGHTS_PIC, pilotsIdMap.value(ui->picNameLineEdit->text()));
-    newData.insert(Opl::Db::FLIGHTS_SECONDPILOT, pilotsIdMap.value(ui->secondPilotNameLineEdit->text()));
-    newData.insert(Opl::Db::FLIGHTS_THIRDPILOT, pilotsIdMap.value(ui->thirdPilotNameLineEdit->text()));
+    newData.insert(Opl::Db::FLIGHTS_PIC, completionData.pilotsIdMap.key(ui->picNameLineEdit->text()));
+    newData.insert(Opl::Db::FLIGHTS_SECONDPILOT, completionData.pilotsIdMap.key(ui->secondPilotNameLineEdit->text()));
+    newData.insert(Opl::Db::FLIGHTS_THIRDPILOT, completionData.pilotsIdMap.key(ui->thirdPilotNameLineEdit->text()));
 
     // Extra Times
     ui->tSPSETimeLineEdit->text().isEmpty() ?
@@ -693,8 +614,8 @@ void NewFlightDialog::formFiller()
             if(rx.match(leName).hasMatch())  {
                 auto line_edits = this->findChild<QLineEdit *>(leName);
                 if(line_edits != nullptr){
-                    DEB << pilotsIdMap.key(1);
-                    line_edits->setText(pilotsIdMap.key(flightEntry.getData().value(data_key).toInt()));
+                    DEB << completionData.pilotsIdMap.value(1);
+                    line_edits->setText(completionData.pilotsIdMap.value(flightEntry.getData().value(data_key).toInt()));
                     line_edits_names.removeOne(leName);
                 }
                 break;
@@ -757,14 +678,14 @@ void NewFlightDialog::formFiller()
         ui->AutolandSpinBox->setValue(AL);
     }
 
-    for(const auto& le : qAsConst(mandatoryLineEdits.lineEdits)){
+    for(const auto& le : qAsConst(mandatoryLineEdits)){
         emit le->editingFinished();
     }
 }
 
 bool NewFlightDialog::isLessOrEqualThanBlockTime(const QString time_string)
 {
-    if (!mandatoryLineEdits.allValid()){
+    if (!allMandatoryLineEditsValid()){
         INFO(tr("Unable to determine total block time.<br>"
                                "Please fill out all Mandatory Fields<br>"
                                "before manually editing these times."));
@@ -800,21 +721,28 @@ void NewFlightDialog::addNewTail(QLineEdit *parent_line_edit)
                                      "<br><br>Would you like to add a new aircraft to the database?"),
                                   QMessageBox::Yes|QMessageBox::No);
     if (reply == QMessageBox::Yes) {
-        DEB << "Add new aircraft selected";
         // create and open new aircraft dialog
         NewTailDialog na(ui->acftLineEdit->text(), this);
-        na.exec();
+        int ret = na.exec();
         // update map and list, set line edit
-        tailsIdMap  = aDB->getIdMap(ADatabaseTarget::tails);
-        tailsList   = aDB->getCompletionList(ADatabaseTarget::registrations);
+        if (ret == QDialog::Accepted) {
+            DEB << "New Tail Entry added. Id:" << aDB->getLastEntry(ADatabaseTable::tails);
 
-        DEB << "New Entry added. Id:" << aDB->getLastEntry(ADatabaseTable::tails);
-        DEB << "AC Map: " << tailsIdMap;
+            // update completion Data and Completer
+            completionData.updateTails();
+            auto new_model = new QStringListModel(completionData.tailsList, parent_line_edit->completer());
+            parent_line_edit->completer()->setModel(new_model); //setModel deletes old model if it has the completer as parent
 
-        parent_line_edit->setText(tailsIdMap.key(aDB->getLastEntry(ADatabaseTable::tails)));
-        emit parent_line_edit->editingFinished();
+            // update Line Edit
+            parent_line_edit->setText(completionData.tailsIdMap.value(aDB->getLastEntry(ADatabaseTable::tails)));
+            emit parent_line_edit->editingFinished();
+        } else {
+            parent_line_edit->setText(QString());
+            parent_line_edit->setFocus();
+        }
     } else {
         parent_line_edit->setText(QString());
+        parent_line_edit->setFocus();
     }
 }
 
@@ -833,16 +761,24 @@ void NewFlightDialog::addNewPilot(QLineEdit *parent_line_edit)
                                      "<br><br>Would you like to add a new pilot to the database?"),
                                   QMessageBox::Yes|QMessageBox::No);
     if (reply == QMessageBox::Yes) {
-        DEB << "Add new pilot selected";
         // create and open new pilot dialog
         NewPilotDialog np(this);
-        np.exec();
+        int ret = np.exec();
         // update map and list, set line edit
-        pilotsIdMap  = aDB->getIdMap(ADatabaseTarget::pilots);
-        pilotList    = aDB->getCompletionList(ADatabaseTarget::pilots);
-        DEB << "Setting new entry: " << pilotsIdMap.key(aDB->getLastEntry(ADatabaseTable::pilots));
-        parent_line_edit->setText(pilotsIdMap.key(aDB->getLastEntry(ADatabaseTable::pilots)));
-        emit parent_line_edit->editingFinished();
+        if (ret == QDialog::Accepted) {
+            DEB << "New Pilot Entry added. Id:" << aDB->getLastEntry(ADatabaseTable::pilots);
+            // update completion Data and Completer
+            completionData.updatePilots();
+            auto new_model = new QStringListModel(completionData.pilotList, parent_line_edit->completer());
+            parent_line_edit->completer()->setModel(new_model); //setModel deletes old model if it has the completer as parent
+
+            // update Line Edit
+            parent_line_edit->setText(completionData.pilotsIdMap.value(aDB->getLastEntry(ADatabaseTable::pilots)));
+            emit parent_line_edit->editingFinished();
+        } else {
+            parent_line_edit->setText(QString());
+            parent_line_edit->setFocus();
+        }
     } else {
         parent_line_edit->setText(QString());
     }
@@ -860,15 +796,23 @@ void NewFlightDialog::on_cancelButton_clicked()
 
 void NewFlightDialog::on_submitButton_clicked()
 {
-    for (const auto &line_edit : qAsConst(mandatoryLineEdits.lineEdits)) {
+    // emit editing finished for all mandatory line edits to trigger input verification
+    for (const auto &line_edit : qAsConst(mandatoryLineEdits)) {
         emit line_edit->editingFinished();
     }
-    DEB << "editing finished emitted. good count: " << mandatoryLineEdits.countValid();
-    if (!mandatoryLineEdits.allValid()) {
+
+    // If input verification is passed, continue, otherwise prompt user to correct
+    if (!allMandatoryLineEditsValid()) {
+        const auto display_names = QMap<int, QString> {
+            {0, QObject::tr("Date of Flight")},      {1, QObject::tr("Departure Airport")},
+            {2, QObject::tr("Destination Airport")}, {3, QObject::tr("Time Off Blocks")},
+            {4, QObject::tr("Time on Blocks")},      {5, QObject::tr("PIC Name")},
+            {6, QObject::tr("Aircraft Registration")}
+        };
         QString missing_items;
         for (int i=0; i < mandatoryLineEdits.size(); i++) {
-            if (!mandatoryLineEdits.validAt(i)){
-                missing_items.append(MANDATORY_LINE_EDITS_DISPLAY_NAMES.value(i) + "<br>");
+            if (!mandatoryLineEditsValid[i]){
+                missing_items.append(display_names.value(i) + "<br>");
                 mandatoryLineEdits[i]->setStyleSheet(QStringLiteral("border: 1px solid red"));
             }
         }
@@ -881,7 +825,7 @@ void NewFlightDialog::on_submitButton_clicked()
         return;
     }
 
-    DEB << "Submit Button clicked. Mandatory good " << mandatoryLineEdits.size() << "out of: " << mandatoryLineEdits.countValid();
+    // If input verification passed, collect input and submit to database
     auto newData = collectInput();
     DEB << "Setting Data for flightEntry...";
     flightEntry.setData(newData);
@@ -906,17 +850,15 @@ void NewFlightDialog::on_submitButton_clicked()
 void NewFlightDialog::onGoodInputReceived(QLineEdit *line_edit)
 {
     DEB << line_edit->objectName() << " - Good input received - " << line_edit->text();
+
     line_edit->setStyleSheet(QString());
 
     if (mandatoryLineEdits.contains(line_edit))
-        mandatoryLineEdits.validate(line_edit);
+        validateMandatoryLineEdit(mandatoryLineEdit(mandatoryLineEdits.indexOf(line_edit)));
+    validationStatus();
 
-    if (mandatoryLineEdits.allValid())
+    if (timeLineEditsValid())
         onMandatoryLineEditsFilled();
-
-    DEB << "Mandatory good: " << mandatoryLineEdits.countValid()
-        << " out of " << mandatoryLineEdits.size()
-        << " : " << mandatoryLineEdits.lineEdits;
 }
 
 void NewFlightDialog::onBadInputReceived(QLineEdit *line_edit)
@@ -924,8 +866,10 @@ void NewFlightDialog::onBadInputReceived(QLineEdit *line_edit)
     DEB << line_edit->objectName() << " - Bad input received - " << line_edit->text();
     line_edit->setStyleSheet(QStringLiteral("border: 1px solid red"));
 
-    DEB << "Mandatory Good: " << mandatoryLineEdits.countValid() << " out of "
-        << mandatoryLineEdits.size() << ". Array: " << mandatoryLineEdits.lineEditsValid;
+    if (mandatoryLineEdits.contains(line_edit))
+        invalidateMandatoryLineEdit(mandatoryLineEdit(mandatoryLineEdits.indexOf(line_edit)));
+    validationStatus();
+
 }
 
 // capitalize input for dept, dest and registration input
@@ -933,7 +877,7 @@ void NewFlightDialog::onToUpperTriggered_textChanged(const QString &text)
 {
     auto sender_object = sender();
     auto line_edit = this->findChild<QLineEdit*>(sender_object->objectName());
-    DEB << "Text changed - " << line_edit->objectName() << line_edit->text();
+    //DEB << "Text changed - " << line_edit->objectName() << line_edit->text();
     {
         const QSignalBlocker blocker(line_edit);
         line_edit->setText(text.toUpper());
@@ -943,13 +887,8 @@ void NewFlightDialog::onToUpperTriggered_textChanged(const QString &text)
 // update is disabled if the user chose to manually edit extra times
 void NewFlightDialog::onMandatoryLineEditsFilled()
 {
-    if (!mandatoryLineEdits.allValid()) {
-        return;
-    };
-
     if (updateEnabled)
         fillDeductibleData();
-    DEB << mandatoryLineEdits.lineEditsValid;
 }
 
 // make sure that when using keyboard to scroll through completer sugggestions, line edit is up to date
@@ -978,11 +917,11 @@ void NewFlightDialog::on_doftLineEdit_editingFinished()
     auto line_edit = ui->doftLineEdit;
     auto text = ui->doftLineEdit->text();
     auto label = ui->doftDisplayLabel;
-    DEB << line_edit->objectName() << "Editing finished - " << text;
+    //DEB << line_edit->objectName() << "Editing finished - " << text;
 
-    TODO << "Implement other Date Formats";
+    TODO << "Non-default Date formats not implemented yet.";
     Opl::Date::ADateFormat date_format = Opl::Date::ADateFormat::ISODate;
-    auto date = ADate::formatInput(text, date_format);
+    auto date = ADate::parseInput(text, date_format);
     if (date.isValid()) {
         label->setText(date.toString(Qt::TextDate));
         line_edit->setText(ADate::toString(date, date_format));
@@ -1035,6 +974,20 @@ void NewFlightDialog::on_calendarPushButton_clicked()
     }
 }
 
+void NewFlightDialog::validationStatus()
+{
+
+    QString deb_string("Validation State:\tdoft\tdept\tdest\ttofb\ttonb\tpic\tacft\n");
+    deb_string += "\t\t\t\t\t" + QString::number(mandatoryLineEditsValid[0]);
+    deb_string += "\t" + QString::number(mandatoryLineEditsValid[1]);
+    deb_string += "\t" + QString::number(mandatoryLineEditsValid[2]);
+    deb_string += "\t" + QString::number(mandatoryLineEditsValid[3]);
+    deb_string += "\t" + QString::number(mandatoryLineEditsValid[4]);
+    deb_string += "\t" + QString::number(mandatoryLineEditsValid[5]);
+    deb_string += "\t" + QString::number(mandatoryLineEditsValid[6]);
+    qDebug().noquote() << deb_string;
+}
+
 /*
  * Location Line Edits
  */
@@ -1057,9 +1010,9 @@ void NewFlightDialog::onLocationEditingFinished(QLineEdit *line_edit, QLabel *na
 
     // try to map iata or icao code to airport id;
     if (text.length() == 3) {
-        airport_id = airportIataIdMap.value(text);
+        airport_id = completionData.airportIataIdMap.key(text);
     } else {
-        airport_id = airportIcaoIdMap.value(text);
+        airport_id = completionData.airportIcaoIdMap.key(text);
     }
     // check result
     if (airport_id == 0) {
@@ -1068,8 +1021,8 @@ void NewFlightDialog::onLocationEditingFinished(QLineEdit *line_edit, QLabel *na
         onBadInputReceived(line_edit);
         return;
     }
-    line_edit->setText(airportIcaoIdMap.key(airport_id));
-    name_label->setText(airportNameIdMap.key(airport_id));
+    line_edit->setText(completionData.airportIcaoIdMap.value(airport_id));
+    name_label->setText(completionData.airportNameIdMap.value(airport_id));
     onGoodInputReceived(line_edit);
 }
 
@@ -1107,30 +1060,31 @@ void NewFlightDialog::onTimeLineEdit_editingFinished()
 
 void NewFlightDialog::on_acftLineEdit_editingFinished()
 {
+    TODO << "Looking up and matching tails is currently broken...";
     auto line_edit = ui->acftLineEdit;
-    //DEB << line_edit->objectName() << "Editing Finished!" << line_edit->text());
+    int acft_id = completionData.tailsIdMap.key(line_edit->text());
+    DEB << "acft_id: " << acft_id;
 
-    if (tailsIdMap.value(line_edit->text()) != 0) {
-        DEB << "Mapped: " << line_edit->text() << tailsIdMap.value(line_edit->text());
-        auto acft = aDB->getTailEntry(tailsIdMap.value(line_edit->text()));
+    if (acft_id != 0) { // Success
+        //DEB << "Mapped: " << line_edit->text() << completionData.tailsIdMap.value(line_edit->text());
+        auto acft = aDB->getTailEntry(acft_id);
         ui->acftTypeLabel->setText(acft.type());
+        ui->picCompanyLabel->setText(acft.getData().value(Opl::Db::TAILS_COMPANY).toString());
         onGoodInputReceived(line_edit);
         return;
     }
 
-    // try to fix input
-    if (!line_edit->completer()->currentCompletion().isEmpty()
-            && !line_edit->text().isEmpty()) {
-        DEB << "Trying to fix input...";
-        line_edit->setText(line_edit->completer()->currentCompletion());
+    if (!line_edit->completer()->currentCompletion().isEmpty()) {
+        line_edit->setText(line_edit->completer()->currentCompletion().split(QLatin1Char(' ')).first());
         emit line_edit->editingFinished();
         return;
     }
 
-    // to do: promp user to add new
+    // If no success mark as bad input
     onBadInputReceived(line_edit);
     ui->acftTypeLabel->setText(tr("Unknown Registration."));
-    addNewTail(line_edit);
+    if (line_edit->text() != QString())
+        addNewTail(line_edit);
 }
 
 /*
@@ -1143,19 +1097,15 @@ void NewFlightDialog::onPilotNameLineEdit_editingFinished()
     auto line_edit = this->findChild<QLineEdit*>(sender_object->objectName());
     //DEB << line_edit->objectName() << "Editing Finished -" << line_edit->text());
 
-    if(line_edit->text().contains(SELF_RX)) {
+    if(line_edit->text().contains(SELF, Qt::CaseInsensitive)) {
         DEB << "self recognized.";
-        line_edit->setText(pilotsIdMap.key(1));
-        auto pilot = aDB->getPilotEntry(1);
-        ui->picCompanyLabel->setText(pilot.getData().value(Opl::Db::TAILS_COMPANY).toString());
+        line_edit->setText(completionData.pilotsIdMap.value(1));
         onGoodInputReceived(line_edit);
         return;
     }
 
-    if(pilotsIdMap.value(line_edit->text()) != 0) {
-        DEB << "Mapped: " << line_edit->text() << pilotsIdMap.value(line_edit->text());
-        auto pilot = aDB->getPilotEntry(pilotsIdMap.value(line_edit->text()));
-        ui->picCompanyLabel->setText(pilot.getData().value(Opl::Db::TAILS_COMPANY).toString());
+    if(completionData.pilotsIdMap.key(line_edit->text()) != 0) {
+        DEB << "Mapped: " << line_edit->text() << completionData.pilotsIdMap.key(line_edit->text());
         onGoodInputReceived(line_edit);
         return;
     }
@@ -1171,7 +1121,6 @@ void NewFlightDialog::onPilotNameLineEdit_editingFinished()
         return;
     }
 
-    // to do: prompt user to add new
     onBadInputReceived(line_edit);
     addNewPilot(line_edit);
 }
@@ -1209,13 +1158,13 @@ void NewFlightDialog::on_PilotFlyingCheckBox_stateChanged(int)
 
 void NewFlightDialog::on_IfrCheckBox_stateChanged(int)
 {
-    if (mandatoryLineEdits.allValid() && updateEnabled)
+    if (timeLineEditsValid() && updateEnabled)
         onMandatoryLineEditsFilled();
 }
 
 void NewFlightDialog::on_manualEditingCheckBox_stateChanged(int arg1)
 {
-    if (!(mandatoryLineEdits.allValid()) && ui->manualEditingCheckBox->isChecked()) {
+    if (!(timeLineEditsValid()) && ui->manualEditingCheckBox->isChecked()) {
         INFO(tr("Before editing times manually, please fill out the required fields "
                 "in the flight data tab, so that total time can be calculated."));
         ui->manualEditingCheckBox->setChecked(false);
@@ -1231,7 +1180,7 @@ void NewFlightDialog::on_manualEditingCheckBox_stateChanged(int arg1)
             le->setStyleSheet(QString());
         }
         updateEnabled = true;
-        if (mandatoryLineEdits.allValid() && updateEnabled)
+        if (allMandatoryLineEditsValid() && updateEnabled)
             onMandatoryLineEditsFilled();
         break;
     case 2:
@@ -1257,10 +1206,6 @@ void NewFlightDialog::on_ApproachComboBox_currentTextChanged(const QString &arg1
 
     if (arg1 != QLatin1String("VISUAL"))
         ui->IfrCheckBox->setChecked(true);
-
-    if (arg1 == QLatin1String("OTHER")) {
-        INFO(tr("You can specify the approach type in the Remarks field."));
-    }
 }
 
 void NewFlightDialog::on_FunctionComboBox_currentIndexChanged(int)

+ 67 - 38
src/gui/dialogues/newflightdialog.h

@@ -37,11 +37,28 @@
 #include "src/classes/apilotentry.h"
 #include "src/classes/atailentry.h"
 #include "src/database/adatabase.h"
+#include "src/classes/acompletiondata.h"
 
 namespace Ui {
+
 class NewFlight;
 }
 
+/*!
+ * \brief The NewFlightDialog enables the user to add a new flight or edit an existing one.
+ * \details
+ * - Most line edits have validators and completers.
+ * - Validators are based on regular expressions, serving as raw input validation
+ * - The Completers are based off the database and provide auto-completion
+ * - mandatory line edits only emit editing finished if their content has passed
+ *   raw input validation or focus is lost.
+ * - Editing finished triggers validating inputs by mapping them to Database values
+ *   where required and results in either pass or fail.
+ * - A QBitArray is mainained containing the state of validity of the mandatory line edits
+ * - The deducted entries are automatically filled if the necessary mandatory entries
+ * are valid.
+ * - Comitting an entry to the database is only allowed if all mandatory inputs are valid.
+ */
 class NewFlightDialog : public QDialog
 {
     Q_OBJECT
@@ -49,13 +66,29 @@ public:
     /*!
      * \brief NewFlightDialog create a new flight and add it to the logbook.
      */
-    explicit NewFlightDialog(QWidget *parent = nullptr);
+    explicit NewFlightDialog(ACompletionData &completion_data, QWidget *parent = nullptr);
     /*!
      * \brief NewFlightDialog Edit an existing logbook entry.
      */
-    explicit NewFlightDialog(int row_id, QWidget *parent = nullptr);
+    explicit NewFlightDialog(ACompletionData &completion_data, int row_id, QWidget *parent = nullptr);
     ~NewFlightDialog();
 
+    /*!
+     * \brief The ValidationSetupData struct encapsulates the items required to initialise
+     * the line edits with QValidators and QCompleters
+     */
+    struct ValidationSetupData
+    {
+        ValidationSetupData(QStringList* completion_data, const QRegularExpression* validation_RegEx)
+            : completionData(completion_data), validationRegEx(validation_RegEx){};
+
+        ValidationSetupData(const QRegularExpression* validation_RegEx)
+            : completionData(nullptr), validationRegEx(validation_RegEx){};
+
+        const QStringList* completionData;
+        const QRegularExpression* validationRegEx;
+    };
+
 private slots:
 
     void onToUpperTriggered_textChanged(const QString&);
@@ -93,48 +126,40 @@ private:
      */
     AFlightEntry flightEntry;
 
-    // [G]: Initial refactoring based on previous use.
-    /*!
-     * \brief Wrapper around Vector of mandatory line edits and their corresponding
-     * "ok" QBitArray.
-     */
-    struct MandatoryLineEdits {
-        QVector<QLineEdit*> lineEdits;
-        QBitArray lineEditsValid;
-
-        MandatoryLineEdits() = default;
-        MandatoryLineEdits(std::initializer_list<QLineEdit*> init_list);
-        void operator= (std::initializer_list<QLineEdit*> init_list);
-
-        bool contains(QLineEdit* line_edit);
-        void validate(QLineEdit* line_edit);
-        void unvalidate(QLineEdit* line_edit);
-        int countValid();
-        int size();
-        bool validAt(int idx);
-        bool allValid();
-        QLineEdit* operator[] (int idx);
-
-    } mandatoryLineEdits;
-
+    QVector<QLineEdit*> mandatoryLineEdits;
     QVector<QLineEdit*> primaryTimeLineEdits;
     QVector<QLineEdit*> pilotsLineEdits;
 
     /*!
-     * To be used by the QCompleters
+     * \brief mandatoryLineEditsValid holds the minimum required information to create a
+     * valid database entries.
      */
-    QStringList pilotList;
-    QStringList tailsList;
-    QStringList airportList;
-
+    QBitArray mandatoryLineEditsValid;
+    enum mandatoryLineEdit {
+        doft = 0,
+        dept = 1,
+        dest = 2,
+        tofb = 3,
+        tonb = 4,
+        pic  = 5,
+        acft = 6
+    };
+    void validateMandatoryLineEdit(mandatoryLineEdit line_edit){mandatoryLineEditsValid.setBit(line_edit, true);}
+    void invalidateMandatoryLineEdit(mandatoryLineEdit line_edit){mandatoryLineEditsValid.setBit(line_edit, false);}
+    bool timeLineEditsValid(){return mandatoryLineEditsValid[mandatoryLineEdit::tofb]
+                                  && mandatoryLineEditsValid[mandatoryLineEdit::tonb];}
+    bool acftLineEditValid(){return mandatoryLineEditsValid[mandatoryLineEdit::acft];}
+    bool locLineEditsValid(){return mandatoryLineEditsValid[mandatoryLineEdit::dept]
+                                 && mandatoryLineEditsValid[mandatoryLineEdit::dest];}
+    bool allMandatoryLineEditsValid(){return mandatoryLineEditsValid.count(true) == 7;}
+
+    //debug
+    void validationStatus();
     /*!
-     * \brief Used to map user input to database keys
+     * Contains completion data for QCompleters and mapping user input
      */
-    QMap<PilotName_T, PilotRowId_T> pilotsIdMap;
-    QMap<TailRegistration_T, TailId_T> tailsIdMap;
-    QMap<AirportICAO_T, AirportId_T> airportIcaoIdMap;
-    QMap<AirportIATA_T, AirportId_T> airportIataIdMap;
-    QMap<AirportName_T, AirportId_T> airportNameIdMap;
+    ACompletionData completionData;
+
 
     Opl::Time::FlightTimeFormat flightTimeFormat;
 
@@ -146,6 +171,7 @@ private:
 
     void setup();
     void readSettings();
+    void setupUi();
     void writeSettings();
     void setupButtonGroups();
     void setupRawInputValidation();
@@ -162,6 +188,10 @@ private:
     void addNewTail(QLineEdit*);
     void addNewPilot(QLineEdit *);
 
+    /*!
+     * \brief Collects user input from the line edits and processes it to be ready
+     * for database submission.
+     */
     RowData_T collectInput();
 
     /*!
@@ -183,5 +213,4 @@ private:
     }
 };
 
-
 #endif // NEWFLIGHT_H

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

@@ -52,7 +52,7 @@
    <item row="2" column="1">
     <widget class="QLineEdit" name="companyLineEdit">
      <property name="maxLength">
-      <number>40</number>
+      <number>69</number>
      </property>
     </widget>
    </item>

+ 1 - 1
src/gui/dialogues/newtail.ui

@@ -183,7 +183,7 @@
    <item row="3" column="1">
     <widget class="QLineEdit" name="companyLineEdit">
      <property name="maxLength">
-      <number>20</number>
+      <number>69</number>
      </property>
      <property name="placeholderText">
       <string>optional</string>

+ 6 - 6
src/gui/dialogues/newtaildialog.cpp

@@ -127,7 +127,7 @@ void NewTailDialog::fillForm(AEntry entry, bool is_template)
 
     auto data = entry.getData();
 
-    for (const auto &le : line_edits) {
+    for (const auto &le : qAsConst(line_edits)) {
         auto key = le->objectName().remove(QStringLiteral("LineEdit"));
         le->setText(data.value(key).toString());
     }
@@ -152,7 +152,7 @@ bool NewTailDialog::verify()
     recommended_combo_boxes.append(this->findChild<QComboBox *>(QStringLiteral("ppNumberComboBox")));
     recommended_combo_boxes.append(this->findChild<QComboBox *>(QStringLiteral("ppTypeComboBox")));
 
-    for (const auto &le : recommended_line_edits) {
+    for (const auto &le : qAsConst(recommended_line_edits)) {
         if (le->text() != "") {
             DEB << "Good: " << le;
             recommended_line_edits.removeOne(le);
@@ -162,7 +162,7 @@ bool NewTailDialog::verify()
             DEB << "Not Good: " << le;
         }
     }
-    for (const auto &cb : recommended_combo_boxes) {
+    for (const auto &cb : qAsConst(recommended_combo_boxes)) {
         if (cb->currentIndex() != 0) {
 
             recommended_combo_boxes.removeOne(cb);
@@ -192,7 +192,7 @@ void NewTailDialog::submitForm()
     auto line_edits = this->findChildren<QLineEdit *>();
     line_edits.removeOne(this->findChild<QLineEdit *>(QStringLiteral("searchLineEdit")));
 
-    for (const auto &le : line_edits) {
+    for (const auto &le : qAsConst(line_edits)) {
         auto key = le->objectName().remove(QStringLiteral("LineEdit"));
         new_data.insert(key, le->text());
     }
@@ -284,9 +284,9 @@ void NewTailDialog::onSearchCompleterActivated()
     const auto &text = ui->searchLineEdit->text();
     if (aircraftList.contains(text)) {
 
-            DEB << "Template Selected. aircraft_id is: " << idMap.value(text);
+            DEB << "Template Selected. aircraft_id is: " << idMap.key(text);
             //call autofiller for dialog
-            fillForm(aDB->getAircraftEntry(idMap.value(text)), true);
+            fillForm(aDB->getAircraftEntry(idMap.key(text)), true);
             ui->searchLineEdit->setStyleSheet(QStringLiteral("border: 1px solid green"));
             ui->searchLabel->setText(text);
         } else {

+ 1 - 1
src/gui/dialogues/newtaildialog.h

@@ -59,7 +59,7 @@ private:
 
     QStringList aircraftList;
 
-    QMap<QString, int> idMap;
+    QMap<RowId_T, QString> idMap;
 
     void setupCompleter();
     void setupValidators();

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

@@ -18,7 +18,6 @@
 #include "aircraftwidget.h"
 #include "ui_aircraftwidget.h"
 #include "src/opl.h"
-#include "src/gui/dialogues/newtaildialog.h"
 #include "src/classes/asettings.h"
 #include "src/database/adatabase.h"
 #include "src/classes/atailentry.h"
@@ -199,6 +198,8 @@ void AircraftWidget::on_deleteAircraftButton_clicked()
         }
     }
     refreshView();
+    ui->stackedWidget->setCurrentIndex(0);
+    ui->aircraftSearchLineEdit->setText(QString());
 }
 
 /*!

+ 1 - 0
src/gui/widgets/aircraftwidget.h

@@ -23,6 +23,7 @@
 #include <QSqlTableModel>
 #include <QTableView>
 #include "src/gui/widgets/settingswidget.h"
+#include "src/gui/dialogues/newtaildialog.h"
 
 
 namespace Ui {

+ 21 - 27
src/gui/widgets/debugwidget.cpp

@@ -30,36 +30,30 @@
 #include "src/classes/atranslator.h"
 #include "src/database/adatabasesetup.h"
 #include "src/classes/ahash.h"
-
 #include "src/classes/ajson.h"
-void DebugWidget::on_debugPushButton_clicked()
-{
-    // Debug
-    QFileInfo check_file("/home/felix/.local/share/opl/openPilotLog/templates/changelog.json");
-    AHash hash(check_file);
-
-    QFileInfo md5_file("/home/felix/.local/share/opl/openPilotLog/templates/changelog.md5");
-    DEB << "Sums are equal?" << hash.compare(md5_file);
-    //test_file2.open(QFile::ReadOnly);
-    //QTextStream in(&test_file2);
-    //auto read = in.read(32);
-    //auto array = read.toUtf8();
-    //test_file2.close();
-    //DEB << read;
-    //DEB << (read == hash.hashToHex());
-    //DEB << array;
+#include "src/functions/adate.h"
 
 
-    //for (const auto &table_name : aDB->getTemplateTableNames()) {
-    //    //json_files.append(QFile(AStandardPaths::asChildOfDir(AStandardPaths::Templates, table_name)));
-    //    QString json_path = AStandardPaths::asChildOfDir(AStandardPaths::Templates, table_name) + QLatin1String(".json");
-    //    QString md5_path = AStandardPaths::asChildOfDir(AStandardPaths::Templates, table_name) + QLatin1String(".md5");
-    //    DEB << json_path << md5_path;
-    //    QFileInfo json_fi(json_path);
-    //    QFileInfo md5_fi(md5_path);
-    //    DEB << "Exists? " << json_fi.exists() << md5_fi.exists();
-    //}
-
+#include "src/testing/importCrewlounge/processflights.h"
+#include "src/testing/importCrewlounge/processpilots.h"
+#include "src/testing/importCrewlounge/processaircraft.h"
+void DebugWidget::on_debugPushButton_clicked()
+{
+    auto rawCsvData = aReadCsvAsRows("/home/felix/git/importMCC/assets/data/felix.csv");
+    // Process Pilots
+    auto proc_pilots = ProcessPilots(rawCsvData);
+    proc_pilots.init();
+    const auto p_maps = proc_pilots.getProcessedPilotMaps();
+    // Process Tails
+    auto proc_tails = ProcessAircraft(rawCsvData);
+    proc_tails.init();
+    const auto t_maps = proc_tails.getProcessedTailMaps();
+    // Process Flights
+    auto proc_flights = ProcessFlights(rawCsvData,proc_pilots.getProcessedPilotsIds(), proc_tails.getProcessedTailIds());
+    proc_flights.init();
+
+    auto flights = proc_flights.getProcessedFlights();
+    DEB << "Flight:" << flights[1000];
 }
 
 DebugWidget::DebugWidget(QWidget *parent) :

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

@@ -37,8 +37,8 @@ HomeWidget::HomeWidget(QWidget *parent) :
 {
     ui->setupUi(this);
     today = QDate::currentDate();
-    currWarningThreshold = ASettings::read(ASettings::UserData::CurrWarningThreshold).toInt();
     ftlWarningThreshold = ASettings::read(ASettings::UserData::FtlWarningThreshold).toDouble();
+    currWarningThreshold = ASettings::read(ASettings::UserData::CurrWarningThreshold).toInt();
     auto logo = QPixmap(Opl::Assets::LOGO);
     ui->logoLabel->setPixmap(logo);
     ui->welcomeLabel->setText(tr("Welcome to openPilotLog, %1!").arg(userName()));
@@ -54,7 +54,6 @@ HomeWidget::HomeWidget(QWidget *parent) :
     fillTotals();
     fillSelectedCurrencies();
     fillLimitations();
-    checkAllCurrencies();
 }
 
 HomeWidget::~HomeWidget()
@@ -68,7 +67,6 @@ void HomeWidget::refresh()
     const auto label_list = this->findChildren<QLabel *>();
     for (const auto label : label_list)
         label->setVisible(true);
-    currWarningThreshold = ASettings::read(ASettings::UserData::CurrWarningThreshold).toInt();
     for (const auto &label : qAsConst(limitationDisplayLabels))
         label->setStyleSheet(QString());
 
@@ -212,38 +210,6 @@ void HomeWidget::fillLimitations()
     }
 }
 
-/*!
- * \brief HomeWidget::checkAllCurrencies loops through all the currencies and warns the user about
- * impending expiries.
- */
-void HomeWidget::checkAllCurrencies()
-{
-    for (int i = 1 ; i <= 6; i++) {
-        // Get the Currency entry
-        auto entry = aDB->getCurrencyEntry(ACurrencyEntry::CurrencyName(i));
-        const auto currency_date = entry.getData().value(Opl::Db::CURRENCIES_EXPIRYDATE).toDate();
-        if (!currency_date.isValid())
-            continue;
-        const auto currency_name = entry.getData().value(Opl::Db::CURRENCIES_DESCRIPTION).toString();
-
-        // check expiration dates
-        if (today >= currency_date) {
-            // is expired
-            WARN(tr("Your currency <b>%1</b> is expired since %2.<br><br>")
-                 .arg(currency_name,
-                      currency_date.toString(Qt::TextDate)));
-            continue;
-        } else if (today.addDays(currWarningThreshold) >=currency_date) {
-            // expires less than <currWarningThreshold> days from current Date
-            QString days_to_expiry = QString::number(today.daysTo(currency_date));
-            WARN(tr("Your currency <b>%1</b> expires in <b>%2</b> days (%3).<br><br>")
-                 .arg(currency_name,
-                      days_to_expiry,
-                      currency_date.toString(Qt::TextDate)));
-        }
-    }
-}
-
 const QString HomeWidget::userName()
 {
     const auto statement = QStringLiteral("SELECT firstname FROM pilots WHERE ROWID=1");

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

@@ -57,12 +57,12 @@ private:
      * \brief currWarningThreshold - Retreived from ASettings::UserData::CurrWarningThreshold, the number
      * of days before expiry that the user gets notified about impending expiries.
      */
-    int            currWarningThreshold;
+    int currWarningThreshold;
     /*!
      * \brief ftlWarningThreshold - Retreived from ASettings::UserData::FtlWarningThreshold, the percentage
      * of how close the user has to be to reaching a Flight Time Limitation before getting notified.
      */
-    double         ftlWarningThreshold;
+    double ftlWarningThreshold;
 
     void fillTotals();
     void fillSelectedCurrencies();
@@ -98,7 +98,6 @@ private:
      * \brief Retreives the users first name from the database.
      */
     const QString userName();
-    void checkAllCurrencies();
 
 public slots:
     void refresh();

+ 19 - 7
src/gui/widgets/logbookwidget.cpp

@@ -34,9 +34,10 @@ const QMap<int, QString> FILTER_MAP = {
 };
 const auto NON_WORD_CHAR = QRegularExpression("\\W");
 
-LogbookWidget::LogbookWidget(QWidget *parent) :
+LogbookWidget::LogbookWidget(ACompletionData& completion_data, QWidget *parent) :
     QWidget(parent),
-    ui(new Ui::LogbookWidget)
+    ui(new Ui::LogbookWidget),
+    completionData(completion_data)
 {
     ui->setupUi(this);
     ui->newFlightButton->setFocus();
@@ -136,9 +137,15 @@ void LogbookWidget::flightsTableView_selectionChanged()
  */
 void LogbookWidget::on_newFlightButton_clicked()
 {
-    auto nf = new NewFlightDialog(this);
-    nf->setAttribute(Qt::WA_DeleteOnClose);
-    nf->exec();
+    auto old_state = aDB->getUserDataState();
+
+    NewFlightDialog nf(completionData, this);
+    nf.exec();
+
+    auto new_state = aDB->getUserDataState();
+    if (old_state != new_state)
+        completionData.update();
+
     displayModel->select();
 }
 
@@ -149,9 +156,14 @@ void LogbookWidget::on_newFlightButton_clicked()
 void LogbookWidget::on_editFlightButton_clicked()
 {
     if(selectedFlights.length() == 1){
-        auto ef = new NewFlightDialog(selectedFlights.first(), this);
-        ef->setAttribute(Qt::WA_DeleteOnClose);
+        auto old_state = aDB->getUserDataState();
+
+        auto ef = new NewFlightDialog(completionData, selectedFlights.first(), this);
         ef->exec();
+
+        auto new_state = aDB->getUserDataState();
+        if (old_state != new_state)
+            completionData.update();
         displayModel->select();
     } else if (selectedFlights.isEmpty()) {
         WARN(tr("<br>No flight selected.<br>"));

+ 4 - 1
src/gui/widgets/logbookwidget.h

@@ -26,6 +26,7 @@
 #include <QMenu>
 #include <QTableView>
 #include "src/gui/widgets/settingswidget.h"
+#include "src/classes/acompletiondata.h"
 
 namespace Ui {
 class LogbookWidget;
@@ -47,7 +48,7 @@ class LogbookWidget : public QWidget
     Q_OBJECT
 
 public:
-    explicit LogbookWidget(QWidget *parent = nullptr);
+    explicit LogbookWidget(ACompletionData &completion_data, QWidget *parent = nullptr);
     ~LogbookWidget();
 
 private slots:
@@ -84,6 +85,8 @@ private:
     void setupModelAndView(int view_id);
     void connectSignalsAndSlots();
 
+    ACompletionData completionData;
+
 protected:
     /*!
      * \brief Handles change events, like updating the UI to new localisation

+ 2 - 0
src/gui/widgets/pilotswidget.cpp

@@ -180,6 +180,8 @@ void PilotsWidget::on_deletePilotButton_clicked()
         }
     }
     refreshView();
+    ui->stackedWidget->setCurrentIndex(0);
+    ui->pilotSearchLineEdit->setText(QString());
 }
 
 /*!

+ 3 - 24
src/gui/widgets/settingswidget.cpp

@@ -112,6 +112,9 @@ void SettingsWidget::setupDateEdits()
     for (const auto &date_edit : date_edits) {
         date_edit->setDisplayFormat(date_format_string);
     }
+    // De-activate non-default date settings for now, implement in future release
+    ui->dateFormatComboBox->setVisible(false);
+    ui->dateFormatLabel->setVisible(false);
     // Fill currencies
     const QList<QPair<ACurrencyEntry::CurrencyName, QDateEdit* >> currencies = {
         {ACurrencyEntry::CurrencyName::Licence,     ui->currLicDateEdit},
@@ -175,12 +178,9 @@ void SettingsWidget::readSettings()
     ui->currMedCheckBox->setChecked(ASettings::read(ASettings::UserData::ShowMedCurrency).toBool());
     ui->currCustom1CheckBox->setChecked(ASettings::read(ASettings::UserData::ShowCustom1Currency).toBool());
     ui->currCustom2CheckBox->setChecked(ASettings::read(ASettings::UserData::ShowCustom2Currency).toBool());
-    ui->currWarningThresholdSpinBox->setValue(ASettings::read(ASettings::UserData::CurrWarningThreshold).toInt());
-    ui->currWarningCheckBox->setChecked(ASettings::read(ASettings::UserData::CurrWarningEnabled).toBool());
     ui->currCustom1LineEdit->setText(ASettings::read(ASettings::UserData::Custom1CurrencyName).toString());
     ui->currCustom2LineEdit->setText(ASettings::read(ASettings::UserData::Custom2CurrencyName).toString());
 
-
     /*
      * Misc Tab
      */
@@ -675,27 +675,6 @@ void SettingsWidget::on_currCustom2CheckBox_stateChanged(int arg1)
     emit settingChanged(HomeWidget);
 }
 
-void SettingsWidget::on_currWarningCheckBox_stateChanged(int arg1)
-{
-    switch (arg1) {
-    case Qt::CheckState::Checked:
-        ASettings::write(ASettings::UserData::CurrWarningEnabled, true);
-        break;
-    case Qt::CheckState::Unchecked:
-        ASettings::write(ASettings::UserData::CurrWarningEnabled, false);
-        break;
-    default:
-        break;
-    }
-    emit settingChanged(HomeWidget);
-}
-
-void SettingsWidget::on_currWarningThresholdSpinBox_valueChanged(int arg1)
-{
-    ASettings::write(ASettings::UserData::CurrWarningThreshold, arg1);
-    emit settingChanged(SettingSignal::HomeWidget);
-}
-
 void SettingsWidget::on_currCustom1LineEdit_editingFinished()
 {
     ASettings::write(ASettings::UserData::Custom1CurrencyName, ui->currCustom1LineEdit->text());

+ 0 - 4
src/gui/widgets/settingswidget.h

@@ -105,10 +105,6 @@ private slots:
 
     void on_currCustom2CheckBox_stateChanged(int arg1);
 
-    void on_currWarningCheckBox_stateChanged(int arg1);
-
-    void on_currWarningThresholdSpinBox_valueChanged(int arg1);
-
     void on_currCustom1LineEdit_editingFinished();
 
     void on_currCustom2LineEdit_editingFinished();

+ 119 - 205
src/gui/widgets/settingswidget.ui

@@ -17,7 +17,7 @@
    <item row="0" column="0">
     <widget class="QTabWidget" name="tabWidget">
      <property name="currentIndex">
-      <number>3</number>
+      <number>2</number>
      </property>
      <widget class="QWidget" name="personalTab">
       <attribute name="title">
@@ -371,76 +371,56 @@
       <attribute name="title">
        <string>Currencies</string>
       </attribute>
-      <layout class="QGridLayout" name="gridLayout_7">
-       <item row="0" column="2">
-        <spacer name="verticalSpacer_2">
-         <property name="orientation">
-          <enum>Qt::Vertical</enum>
-         </property>
-         <property name="sizeHint" stdset="0">
-          <size>
-           <width>20</width>
-           <height>35</height>
-          </size>
-         </property>
-        </spacer>
-       </item>
-       <item row="1" column="0">
-        <widget class="QLabel" name="headerCurrencyLabel">
+      <layout class="QGridLayout" name="gridLayout_6">
+       <item row="3" column="3">
+        <widget class="QCheckBox" name="currToLdgCheckBox">
          <property name="minimumSize">
           <size>
-           <width>280</width>
+           <width>140</width>
            <height>0</height>
           </size>
          </property>
-         <property name="text">
-          <string>Currency</string>
+         <property name="layoutDirection">
+          <enum>Qt::LeftToRight</enum>
          </property>
-         <property name="alignment">
-          <set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
+         <property name="text">
+          <string/>
          </property>
         </widget>
        </item>
-       <item row="1" column="3">
-        <widget class="QLabel" name="headerExpirationLabel">
+       <item row="8" column="0">
+        <widget class="QLineEdit" name="currCustom1LineEdit">
          <property name="minimumSize">
           <size>
-           <width>140</width>
+           <width>280</width>
            <height>0</height>
           </size>
          </property>
          <property name="text">
-          <string>Expiration Date</string>
+          <string/>
          </property>
-         <property name="alignment">
-          <set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
+         <property name="placeholderText">
+          <string>custom currency</string>
          </property>
         </widget>
        </item>
-       <item row="1" column="4">
-        <widget class="QLabel" name="headerShowLabel">
+       <item row="5" column="3">
+        <widget class="QCheckBox" name="currTrCheckBox">
          <property name="minimumSize">
           <size>
            <width>140</width>
            <height>0</height>
           </size>
          </property>
-         <property name="text">
-          <string>Show on Home Page</string>
-         </property>
-         <property name="alignment">
-          <set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
+         <property name="layoutDirection">
+          <enum>Qt::LeftToRight</enum>
          </property>
-        </widget>
-       </item>
-       <item row="2" column="0" colspan="5">
-        <widget class="Line" name="line">
-         <property name="orientation">
-          <enum>Qt::Horizontal</enum>
+         <property name="text">
+          <string/>
          </property>
         </widget>
        </item>
-       <item row="3" column="0" rowspan="2">
+       <item row="3" column="0">
         <widget class="QLabel" name="currToLdgLabel">
          <property name="minimumSize">
           <size>
@@ -449,15 +429,25 @@
           </size>
          </property>
          <property name="text">
-          <string>Take-off / Landing (automatic)</string>
+          <string>Take-off / Landing (days)</string>
          </property>
         </widget>
        </item>
-       <item row="4" column="3" rowspan="2">
-        <widget class="QDateEdit" name="currToLdgDateEdit">
-         <property name="enabled">
-          <bool>false</bool>
+       <item row="1" column="1">
+        <spacer name="verticalSpacer">
+         <property name="orientation">
+          <enum>Qt::Vertical</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>20</width>
+           <height>168</height>
+          </size>
          </property>
+        </spacer>
+       </item>
+       <item row="4" column="2">
+        <widget class="QDateEdit" name="currLicDateEdit">
          <property name="minimumSize">
           <size>
            <width>140</width>
@@ -473,6 +463,9 @@
          <property name="calendarPopup">
           <bool>true</bool>
          </property>
+         <property name="timeSpec">
+          <enum>Qt::UTC</enum>
+         </property>
          <property name="date">
           <date>
            <year>2020</year>
@@ -482,37 +475,8 @@
          </property>
         </widget>
        </item>
-       <item row="5" column="4">
-        <widget class="QCheckBox" name="currToLdgCheckBox">
-         <property name="minimumSize">
-          <size>
-           <width>140</width>
-           <height>0</height>
-          </size>
-         </property>
-         <property name="layoutDirection">
-          <enum>Qt::LeftToRight</enum>
-         </property>
-         <property name="text">
-          <string/>
-         </property>
-        </widget>
-       </item>
-       <item row="6" column="0">
-        <widget class="QLabel" name="currLicLabel">
-         <property name="minimumSize">
-          <size>
-           <width>280</width>
-           <height>0</height>
-          </size>
-         </property>
-         <property name="text">
-          <string>Licence</string>
-         </property>
-        </widget>
-       </item>
-       <item row="6" column="3">
-        <widget class="QDateEdit" name="currLicDateEdit">
+       <item row="5" column="2">
+        <widget class="QDateEdit" name="currTrDateEdit">
          <property name="minimumSize">
           <size>
            <width>140</width>
@@ -540,8 +504,8 @@
          </property>
         </widget>
        </item>
-       <item row="6" column="4">
-        <widget class="QCheckBox" name="currLicCheckBox">
+       <item row="9" column="3">
+        <widget class="QCheckBox" name="currCustom2CheckBox">
          <property name="minimumSize">
           <size>
            <width>140</width>
@@ -556,21 +520,8 @@
          </property>
         </widget>
        </item>
-       <item row="7" column="0">
-        <widget class="QLabel" name="currTrLabel">
-         <property name="minimumSize">
-          <size>
-           <width>280</width>
-           <height>0</height>
-          </size>
-         </property>
-         <property name="text">
-          <string>Type Rating</string>
-         </property>
-        </widget>
-       </item>
-       <item row="7" column="3">
-        <widget class="QDateEdit" name="currTrDateEdit">
+       <item row="7" column="2">
+        <widget class="QDateEdit" name="currMedDateEdit">
          <property name="minimumSize">
           <size>
            <width>140</width>
@@ -598,8 +549,31 @@
          </property>
         </widget>
        </item>
-       <item row="7" column="4">
-        <widget class="QCheckBox" name="currTrCheckBox">
+       <item row="3" column="2">
+        <widget class="QSpinBox" name="currToLdgSpinBox">
+         <property name="toolTip">
+          <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Number of days for TO/LDG currency. Default 90&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+         </property>
+         <property name="value">
+          <number>90</number>
+         </property>
+        </widget>
+       </item>
+       <item row="7" column="0">
+        <widget class="QLabel" name="currMedLabel">
+         <property name="minimumSize">
+          <size>
+           <width>280</width>
+           <height>0</height>
+          </size>
+         </property>
+         <property name="text">
+          <string>Medical</string>
+         </property>
+        </widget>
+       </item>
+       <item row="8" column="3">
+        <widget class="QCheckBox" name="currCustom1CheckBox">
          <property name="minimumSize">
           <size>
            <width>140</width>
@@ -614,20 +588,20 @@
          </property>
         </widget>
        </item>
-       <item row="8" column="0">
-        <widget class="QLabel" name="currLckLabel">
+       <item row="9" column="0">
+        <widget class="QLineEdit" name="currCustom2LineEdit">
          <property name="minimumSize">
           <size>
            <width>280</width>
            <height>0</height>
           </size>
          </property>
-         <property name="text">
-          <string>Line Check</string>
+         <property name="placeholderText">
+          <string>custom currency</string>
          </property>
         </widget>
        </item>
-       <item row="8" column="3">
+       <item row="6" column="2">
         <widget class="QDateEdit" name="currLckDateEdit">
          <property name="minimumSize">
           <size>
@@ -656,24 +630,21 @@
          </property>
         </widget>
        </item>
-       <item row="8" column="4">
-        <widget class="QCheckBox" name="currLckCheckBox">
+       <item row="6" column="0">
+        <widget class="QLabel" name="currLckLabel">
          <property name="minimumSize">
           <size>
-           <width>140</width>
+           <width>280</width>
            <height>0</height>
           </size>
          </property>
-         <property name="layoutDirection">
-          <enum>Qt::LeftToRight</enum>
-         </property>
          <property name="text">
-          <string/>
+          <string>Line Check</string>
          </property>
         </widget>
        </item>
-       <item row="9" column="0">
-        <widget class="QLabel" name="currMedLabel">
+       <item row="5" column="0">
+        <widget class="QLabel" name="currTrLabel">
          <property name="minimumSize">
           <size>
            <width>280</width>
@@ -681,41 +652,25 @@
           </size>
          </property>
          <property name="text">
-          <string>Medical</string>
+          <string>Type Rating</string>
          </property>
         </widget>
        </item>
-       <item row="9" column="3">
-        <widget class="QDateEdit" name="currMedDateEdit">
+       <item row="4" column="0">
+        <widget class="QLabel" name="currLicLabel">
          <property name="minimumSize">
           <size>
-           <width>140</width>
+           <width>280</width>
            <height>0</height>
           </size>
          </property>
-         <property name="currentSection">
-          <enum>QDateTimeEdit::MonthSection</enum>
-         </property>
-         <property name="displayFormat">
-          <string>MM/dd/yyyy</string>
-         </property>
-         <property name="calendarPopup">
-          <bool>true</bool>
-         </property>
-         <property name="timeSpec">
-          <enum>Qt::UTC</enum>
-         </property>
-         <property name="date">
-          <date>
-           <year>2020</year>
-           <month>1</month>
-           <day>1</day>
-          </date>
+         <property name="text">
+          <string>Licence</string>
          </property>
         </widget>
        </item>
-       <item row="9" column="4">
-        <widget class="QCheckBox" name="currMedCheckBox">
+       <item row="6" column="3">
+        <widget class="QCheckBox" name="currLckCheckBox">
          <property name="minimumSize">
           <size>
            <width>140</width>
@@ -730,24 +685,21 @@
          </property>
         </widget>
        </item>
-       <item row="10" column="0">
-        <widget class="QLineEdit" name="currCustom1LineEdit">
-         <property name="minimumSize">
+       <item row="10" column="1">
+        <spacer name="verticalSpacer_2">
+         <property name="orientation">
+          <enum>Qt::Vertical</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
           <size>
-           <width>280</width>
-           <height>0</height>
+           <width>20</width>
+           <height>167</height>
           </size>
          </property>
-         <property name="text">
-          <string/>
-         </property>
-         <property name="placeholderText">
-          <string>custom currency</string>
-         </property>
-        </widget>
+        </spacer>
        </item>
-       <item row="10" column="3">
-        <widget class="QDateEdit" name="currCustom1DateEdit">
+       <item row="9" column="2">
+        <widget class="QDateEdit" name="currCustom2DateEdit">
          <property name="minimumSize">
           <size>
            <width>140</width>
@@ -775,8 +727,8 @@
          </property>
         </widget>
        </item>
-       <item row="10" column="4">
-        <widget class="QCheckBox" name="currCustom1CheckBox">
+       <item row="4" column="3">
+        <widget class="QCheckBox" name="currLicCheckBox">
          <property name="minimumSize">
           <size>
            <width>140</width>
@@ -791,21 +743,8 @@
          </property>
         </widget>
        </item>
-       <item row="11" column="0">
-        <widget class="QLineEdit" name="currCustom2LineEdit">
-         <property name="minimumSize">
-          <size>
-           <width>280</width>
-           <height>0</height>
-          </size>
-         </property>
-         <property name="placeholderText">
-          <string>custom currency</string>
-         </property>
-        </widget>
-       </item>
-       <item row="11" column="3">
-        <widget class="QDateEdit" name="currCustom2DateEdit">
+       <item row="8" column="2">
+        <widget class="QDateEdit" name="currCustom1DateEdit">
          <property name="minimumSize">
           <size>
            <width>140</width>
@@ -833,8 +772,8 @@
          </property>
         </widget>
        </item>
-       <item row="11" column="4">
-        <widget class="QCheckBox" name="currCustom2CheckBox">
+       <item row="7" column="3">
+        <widget class="QCheckBox" name="currMedCheckBox">
          <property name="minimumSize">
           <size>
            <width>140</width>
@@ -849,46 +788,21 @@
          </property>
         </widget>
        </item>
-       <item row="12" column="0" colspan="5">
-        <widget class="Line" name="line_2">
-         <property name="orientation">
-          <enum>Qt::Horizontal</enum>
-         </property>
-        </widget>
-       </item>
-       <item row="13" column="1">
-        <layout class="QGridLayout" name="gridLayout_6">
-         <item row="0" column="0">
-          <widget class="QCheckBox" name="currWarningCheckBox">
-           <property name="text">
-            <string>Warn me about expiring currencies</string>
-           </property>
-          </widget>
-         </item>
-         <item row="0" column="1">
-          <widget class="QSpinBox" name="currWarningThresholdSpinBox"/>
-         </item>
-         <item row="0" column="2">
-          <widget class="QLabel" name="label">
-           <property name="text">
-            <string>days before expiry</string>
-           </property>
-          </widget>
-         </item>
-        </layout>
-       </item>
-       <item row="14" column="1">
-        <spacer name="verticalSpacer">
-         <property name="orientation">
-          <enum>Qt::Vertical</enum>
-         </property>
-         <property name="sizeHint" stdset="0">
+       <item row="2" column="3">
+        <widget class="QLabel" name="headerShowLabel">
+         <property name="minimumSize">
           <size>
-           <width>20</width>
-           <height>35</height>
+           <width>140</width>
+           <height>0</height>
           </size>
          </property>
-        </spacer>
+         <property name="text">
+          <string>Show on Home Page</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
+         </property>
+        </widget>
        </item>
       </layout>
      </widget>

+ 3 - 0
src/opl.h

@@ -170,6 +170,7 @@ static const auto TABLE_AIRPORTS         = QLatin1String("airports");
 static const auto TABLE_CURRENCIES       = QLatin1String("currencies");
 
 // Flights table columns
+static const auto FLIGHTS_ROWID          = QLatin1String("flight_id");
 static const auto FLIGHTS_DOFT           = QLatin1String("doft");
 static const auto FLIGHTS_DEPT           = QLatin1String("dept");
 static const auto FLIGHTS_DEST           = QLatin1String("dest");
@@ -203,6 +204,7 @@ static const auto FLIGHTS_REMARKS        = QLatin1String("remarks");
 
 // tails table
 
+static const auto TAILS_ROWID            = QLatin1String("tail_id");
 static const auto TAILS_REGISTRATION     = QLatin1String("registration");
 static const auto TAILS_COMPANY          = QLatin1String("company");
 static const auto TAILS_MAKE             = QLatin1String("make");
@@ -215,6 +217,7 @@ static const auto TAILS_WEIGHTCLASS      = QLatin1String("weightClass");
 
 // pilots table
 
+static const auto PILOTS_ROWID           = QLatin1String("pilot_id");
 static const auto PILOTS_LASTNAME        = QLatin1String("lastname");
 static const auto PILOTS_FIRSTNAME       = QLatin1String("firstname");
 static const auto PILOTS_ALIAS           = QLatin1String("alias");

+ 69 - 0
src/testing/importCrewlounge/importcrewlounge.cpp

@@ -0,0 +1,69 @@
+#include "importcrewlounge.h"
+#include "src/database/adatabase.h"
+#include "src/opl.h"
+#include "src/testing/importCrewlounge/processpilots.h"
+#include "src/testing/importCrewlounge/processaircraft.h"
+#include "src/testing/importCrewlounge/processflights.h"
+#include "src/functions/areadcsv.h"
+
+namespace ImportCrewlounge
+{
+
+void exec(const QString &csv_file_path)
+{
+    // Inhibit HomeWindow Updating
+    QSignalBlocker blocker(aDB);
+
+    // Prepare database and set up exclusive transaction for mass commit
+    QSqlQuery q;
+    q.prepare(QStringLiteral("BEGIN EXCLUSIVE TRANSACTION"));
+    q.exec();
+
+    // Read from CSV and remove first line (headers)
+    auto raw_csv_data = aReadCsvAsRows(csv_file_path);
+    raw_csv_data.removeFirst();
+
+    // Process Pilots
+    auto proc_pilots = ProcessPilots(raw_csv_data);
+    proc_pilots.init();
+    const auto p_maps = proc_pilots.getProcessedPilotMaps();
+
+    for (const auto & pilot : p_maps) {
+        APilotEntry pe(pilot);
+        pe.setPosition(DataPosition(Opl::Db::TABLE_PILOTS, pilot.value(Opl::Db::PILOTS_ROWID).toInt()));
+        aDB->commit(pe);
+    }
+
+    // Process Tails
+    auto proc_tails = ProcessAircraft(raw_csv_data);
+    proc_tails.init();
+    const auto t_maps = proc_tails.getProcessedTailMaps();
+
+    for (const auto& tail : t_maps) {
+        ATailEntry te(tail);
+        te.setPosition(DataPosition(Opl::Db::TABLE_TAILS, tail.value(Opl::Db::PILOTS_ROWID).toInt()));
+        aDB->commit(te);
+    }
+
+    auto proc_flights = ProcessFlights(raw_csv_data,
+                                       proc_pilots.getProcessedPilotsIds(),
+                                       proc_tails.getProcessedTailIds());
+    proc_flights.init();
+    const auto flights = proc_flights.getProcessedFlights();
+
+
+
+    for (const auto &flight : flights) {
+        AFlightEntry fe(flight);
+        aDB->commit(fe);
+    }
+
+    // Commit the exclusive transaction
+    q.prepare(QStringLiteral("COMMIT"));
+    q.exec();
+
+    // destroy blocker
+    blocker.unblock();
+    emit aDB->dataBaseUpdated();
+}
+}// namespace ImportCrewLongue

+ 10 - 0
src/testing/importCrewlounge/importcrewlounge.h

@@ -0,0 +1,10 @@
+#ifndef IMPORTCREWLOUNGE_H
+#define IMPORTCREWLOUNGE_H
+#include <QtCore>
+
+namespace ImportCrewlounge
+{
+void exec(const QString &csv_file_path);
+};
+
+#endif // IMPORTCREWLOUNGE_H

+ 69 - 0
src/testing/importCrewlounge/processaircraft.cpp

@@ -0,0 +1,69 @@
+#include "processaircraft.h"
+
+void ProcessAircraft::parseRawData()
+{
+    QStringList tail_details;
+    // relevant colums: {reg,company,make,model,variant,multipilot,multiengine,engineType,weightClass}
+    int relevant_cols[9] = {79, 36, 76, 77, 78, 83, 84, 92, 96};
+
+    for (const auto &row : qAsConst(rawData)) {
+        for (const auto &col : relevant_cols) {
+            tail_details.append(row[col]);
+        }
+        if (!(unique_tails.contains(tail_details)))
+            unique_tails.append(tail_details);
+        tail_details.clear();
+    }
+}
+
+void ProcessAircraft::processParsedData()
+{
+    // init counter
+    int unique_tail_id = 1;
+
+    QMap<QString, QVariant> new_tail_data;
+    for (const auto &list : qAsConst(unique_tails)) {
+        new_tail_data.insert(Opl::Db::TAILS_REGISTRATION, list[0]);
+        new_tail_data.insert(Opl::Db::TAILS_COMPANY, list[1]);
+        new_tail_data.insert(Opl::Db::TAILS_MAKE, list[2]);
+        new_tail_data.insert(Opl::Db::TAILS_MODEL, list[3]);
+        new_tail_data.insert(Opl::Db::TAILS_VARIANT, list[4]);
+
+        if (list[5] == "TRUE")
+            new_tail_data.insert(Opl::Db::TAILS_MULTIPILOT, 1);
+        else
+            new_tail_data.insert(Opl::Db::TAILS_MULTIPILOT, 0);
+
+        if (list[6] == "TRUE")
+            new_tail_data.insert(Opl::Db::TAILS_MULTIENGINE, 1);
+        else
+            new_tail_data.insert(Opl::Db::TAILS_MULTIENGINE, 0);
+
+        if (list[7] == "Piston") // other values need to be added as needed, do later
+            new_tail_data.insert(Opl::Db::TAILS_ENGINETYPE, 1);
+        else if (list[7] == "Turbine (jet-fan)")
+            new_tail_data.insert(Opl::Db::TAILS_ENGINETYPE, 3);
+
+        if (list[8] == "TRUE") // this is a above 7.5t switch in MCC, so default to medium for now
+            new_tail_data.insert(Opl::Db::TAILS_WEIGHTCLASS, 1);
+        else
+            new_tail_data.insert(Opl::Db::TAILS_WEIGHTCLASS, 0);
+
+        new_tail_data.insert(QStringLiteral("tail_id"), unique_tail_id);
+
+        processedTailIds.insert(list[0], unique_tail_id);
+        processedTailMaps.insert(list[0], new_tail_data);
+        unique_tail_id ++;
+        new_tail_data.clear();
+    }
+}
+
+QMap<QString, int> ProcessAircraft::getProcessedTailIds() const
+{
+    return processedTailIds;
+}
+
+QMap<QString, QMap<QString, QVariant> > ProcessAircraft::getProcessedTailMaps() const
+{
+    return processedTailMaps;
+}

+ 49 - 0
src/testing/importCrewlounge/processaircraft.h

@@ -0,0 +1,49 @@
+#ifndef PROCESSAIRCRAFT_H
+#define PROCESSAIRCRAFT_H
+#include <QtCore>
+#include "src/opl.h"
+
+class ProcessAircraft
+{
+public:
+    ProcessAircraft(const QVector<QStringList> &raw_csv_data)
+        : rawData(raw_csv_data){};
+
+    void init(){
+        parseRawData();
+        processParsedData();
+    };
+
+    QMap<QString, QMap<QString, QVariant> > getProcessedTailMaps() const;
+    QMap<QString, int> getProcessedTailIds() const;
+
+private:
+    void parseRawData();
+    void processParsedData();
+    QVector<QStringList> rawData;
+
+    /*!
+     * \brief unique_tails contains a list of all unique tails with relevant details
+     */
+    QVector<QStringList> unique_tails;
+
+    /*!
+     * \brief processedPilotMaps Holds a map of the processed pilots
+     *
+     * \details Here, the imported tails are stored after having been read from the CSV file.
+     * The key is the original string as it was originally in the AC_REG field and allows
+     * mapping the data to ID's later on. The value is a QMap<QString, QVariant> and contains
+     * the data processed as it will be represented in the OPL database later on.
+     */
+    QMap<QString, QMap<QString, QVariant>> processedTailMaps;
+
+    /*!
+     * \brief processedTailIds Holds a map of the ids that have been given to the processed pilots
+     *
+     * \details The tail data, once processed is held in processedTailMaps. With processedTailIds it
+     * is possible to map the original String to the tail_id that has been assigned during the parsing process.
+     */
+    QMap<QString, int> processedTailIds;
+};
+
+#endif // PROCESSAIRCRAFT_H

+ 89 - 0
src/testing/importCrewlounge/processflights.cpp

@@ -0,0 +1,89 @@
+#include "processflights.h"
+#include <src/functions/atime.h>
+
+void ProcessFlights::parseRawData()
+{
+    // doft, flightNumber, dept, dest, tofb, tonb, tblk, tPIC, tSIC, tDUAL, tPICUS, tFI, tNight, pic, secondPilot, thirdPilot   toDN,ldDN   pilotFlying, appType, remarks,  acftReg
+    int relevant_cols[24] = {0,3,5,7,9,11,17,19,20,21,22,23,25,38,42,46,53,54,55,56,58,60,64,79};
+    QStringList row_data;
+    for (const auto &row : qAsConst(rawData)) {
+        for (const auto &col : relevant_cols) {
+            row_data.append(row[col]);
+        }
+        rawFlightData.append(row_data);
+        row_data.clear();
+    }
+    DEB << "Flight Info #1742:" << rawFlightData[1742];
+}
+
+void ProcessFlights::processParsedData()
+{
+    QMap<QString, QVariant> new_flight_data;
+    int flight_id = 1;
+
+    for (const auto &row : qAsConst(rawFlightData)) {
+        // insert values that don't require editing
+        new_flight_data.insert(Opl::Db::FLIGHTS_FLIGHTNUMBER, row[1]);
+        new_flight_data.insert(Opl::Db::FLIGHTS_DEPT, row[2]);
+        new_flight_data.insert(Opl::Db::FLIGHTS_DEST, row[3]);
+        new_flight_data.insert(Opl::Db::FLIGHTS_TBLK, row[6]);
+        new_flight_data.insert(Opl::Db::FLIGHTS_TPIC, row[7]);
+        new_flight_data.insert(Opl::Db::FLIGHTS_TSIC, row[8]);
+        new_flight_data.insert(Opl::Db::FLIGHTS_TDUAL, row[9]);
+        new_flight_data.insert(Opl::Db::FLIGHTS_TPICUS, row[10]);
+        new_flight_data.insert(Opl::Db::FLIGHTS_TFI, row[11]);
+        new_flight_data.insert(Opl::Db::FLIGHTS_TNIGHT, row[12]);
+        new_flight_data.insert(Opl::Db::FLIGHTS_TODAY, row[16]);
+        new_flight_data.insert(Opl::Db::FLIGHTS_TONIGHT, row[17]);
+        new_flight_data.insert(Opl::Db::FLIGHTS_LDGDAY, row[18]);
+        new_flight_data.insert(Opl::Db::FLIGHTS_LDGNIGHT, row[19]);
+        new_flight_data.insert(Opl::Db::FLIGHTS_APPROACHTYPE, row[21]);
+        new_flight_data.insert(Opl::Db::FLIGHTS_REMARKS, row[22]);
+
+        // PF
+        if (row[20] == QLatin1String("TRUE"))
+            new_flight_data.insert(Opl::Db::FLIGHTS_PILOTFLYING, 1);
+        else
+            new_flight_data.insert(Opl::Db::FLIGHTS_PILOTFLYING, 0);
+
+        // Convert Date and Time
+        const QDate doft = QDate::fromString(row[0],QStringLiteral("dd/MM/yyyy"));
+        new_flight_data.insert(Opl::Db::FLIGHTS_DOFT, doft.toString(Qt::ISODate));
+
+        auto time_off = QTime::fromString(row[4], QStringLiteral("hh:mm"));
+        if (!time_off.isValid())
+            time_off = QTime::fromString(row[4], QStringLiteral("h:mm"));
+        int tofb = ATime::toMinutes(time_off);
+        new_flight_data.insert(Opl::Db::FLIGHTS_TOFB, tofb);
+
+        auto time_on = QTime::fromString(row[5], QStringLiteral("hh:mm"));
+        if (!time_on.isValid())
+            time_on = QTime::fromString(row[5], QStringLiteral("h:mm"));
+
+        int tonb = ATime::toMinutes(time_on);
+        new_flight_data.insert(Opl::Db::FLIGHTS_TONB, tonb);
+
+        // map pilots
+        int pic = processedPilotsIds.value(row[13]);
+        new_flight_data.insert(Opl::Db::FLIGHTS_PIC, pic);
+        int second_pilot = processedPilotsIds.value(row[14]);
+        new_flight_data.insert(Opl::Db::FLIGHTS_SECONDPILOT, second_pilot);
+        int third_pilot = processedPilotsIds.value(row[15]);
+        new_flight_data.insert(Opl::Db::FLIGHTS_THIRDPILOT, third_pilot);
+
+        // map tail
+        int acft = processedTailsIds.value(row[23]);
+        new_flight_data.insert(Opl::Db::FLIGHTS_ACFT, acft);
+
+        // set id, fix opl to include alias
+        new_flight_data.insert(QStringLiteral("flight_id"), flight_id);
+        processedFlights.append(new_flight_data);
+        new_flight_data.clear();
+        flight_id ++;
+    }
+}
+
+QVector<QMap<QString, QVariant> > ProcessFlights::getProcessedFlights() const
+{
+    return processedFlights;
+}

+ 34 - 0
src/testing/importCrewlounge/processflights.h

@@ -0,0 +1,34 @@
+#ifndef PROCESSFLIGHTS_H
+#define PROCESSFLIGHTS_H
+#include <QtCore>
+#include <src/opl.h>
+
+class ProcessFlights
+{
+public:
+    ProcessFlights(const QVector<QStringList> &raw_csv_data,
+                   const QMap<QString, int> &processed_pilots_ids,
+                   const QMap<QString, int> &processed_tails_ids)
+        : rawData(raw_csv_data),
+          processedPilotsIds(processed_pilots_ids),
+          processedTailsIds(processed_tails_ids){};
+
+    void init(){
+        parseRawData();
+        processParsedData();
+    };
+    QVector<QMap<QString, QVariant> > getProcessedFlights() const;
+
+private:
+    void parseRawData();
+    void processParsedData();
+    QVector<QStringList> rawData;
+    QVector<QStringList> rawFlightData;
+
+    QVector<QMap<QString, QVariant>> processedFlights;
+
+    QMap<QString, int> processedPilotsIds;
+    QMap<QString, int> processedTailsIds;
+};
+
+#endif // PROCESSFLIGHTS_H

+ 84 - 0
src/testing/importCrewlounge/processpilots.cpp

@@ -0,0 +1,84 @@
+#include "processpilots.h"
+
+void ProcessPilots::parseRawData()
+{
+    const QVector<int> cols_pilot1 = {37, 38, 39, 40}; //empId, name, phone, email
+    const QVector<int> cols_pilot2 = {41, 42, 43, 44};
+    const QVector<int> cols_pilot3 = {45, 46, 47, 48};
+    const QVector<QVector<int>> pilot_cols = {
+        cols_pilot1,
+        cols_pilot2,
+        cols_pilot3
+    };
+
+    QVector<QStringList> unique_pilots;
+    QStringList pilot_data = {
+        QString(),
+        QStringLiteral("SELF"),
+        QString(),
+        QString()
+    };
+    unique_pilots.append(pilot_data);
+    int unique_pilot_id = 1;
+    processedPilotsIds.insert(pilot_data[1], unique_pilot_id);
+    rawPilotsAndIds.append({pilot_data, unique_pilot_id});
+    pilot_data.clear();
+    unique_pilot_id ++;
+
+
+    for (const auto &row : qAsConst(rawData)) {
+        for (const auto &col_array : pilot_cols) {
+            for (const auto &col : col_array) {
+                pilot_data.append(row[col]);
+            }
+            if (!unique_pilots.contains(pilot_data) && !pilot_data.contains(QLatin1String("SELF"))) {
+                unique_pilots.append(pilot_data);
+                processedPilotsIds.insert(pilot_data[1], unique_pilot_id);
+                rawPilotsAndIds.append({pilot_data, unique_pilot_id});
+                unique_pilot_id ++;
+            }
+            pilot_data.clear();
+        }
+    }
+}
+
+void ProcessPilots::processParsedData()
+{
+    for (const auto &pair : qAsConst(rawPilotsAndIds)) {
+        //DEB << "ID:" << pair.second << "Details:" << pair.first;
+        QMap<QString, QVariant> new_pilot_data;
+
+        // process name [1]
+        auto temp_list = pair.first[1].split(QLatin1Char(' '));
+        if (!temp_list.isEmpty()) {
+            new_pilot_data.insert(Opl::Db::PILOTS_LASTNAME, temp_list.first());
+            temp_list.pop_front();
+
+            if (!temp_list.isEmpty())
+                new_pilot_data.insert(Opl::Db::PILOTS_FIRSTNAME, temp_list.join(QLatin1Char(' ')));
+        } else {
+            new_pilot_data.insert(Opl::Db::PILOTS_LASTNAME, QStringLiteral("UNKNOWN"));
+        }
+
+        // add additional data
+        new_pilot_data.insert(Opl::Db::PILOTS_EMPLOYEEID, pair.first[0]);
+        new_pilot_data.insert(Opl::Db::PILOTS_PHONE, pair.first[2]);
+        new_pilot_data.insert(Opl::Db::PILOTS_EMAIL, pair.first[3]);
+
+        // add pilot_id (workaround with literal until Opl::Db is updated)
+        new_pilot_data.insert(QStringLiteral("pilot_id"), pair.second);
+
+        processedPilotMaps.insert(pair.first[1], new_pilot_data);
+        processedPilotsIds.insert(pair.first[1], pair.second);
+    }
+}
+
+QMap<QString, QMap<QString, QVariant>> ProcessPilots::getProcessedPilotMaps() const
+{
+    return processedPilotMaps;
+}
+
+QMap<QString, int> ProcessPilots::getProcessedPilotsIds() const
+{
+    return processedPilotsIds;
+}

+ 49 - 0
src/testing/importCrewlounge/processpilots.h

@@ -0,0 +1,49 @@
+#ifndef PROCESSPILOTS_H
+#define PROCESSPILOTS_H
+#include <QtCore>
+#include <src/opl.h>
+#include <QMap>
+#include <QVector>
+
+class ProcessPilots
+{
+public:
+    ProcessPilots(const QVector<QStringList> &raw_csv_data)
+        : rawData(raw_csv_data){};
+
+    void init(){
+        parseRawData();
+        processParsedData();
+    };
+
+    QMap<QString, QMap<QString, QVariant> > getProcessedPilotMaps() const;
+    QMap<QString, int> getProcessedPilotsIds() const;
+
+private:
+
+    void parseRawData();
+    void processParsedData();
+
+    QVector<QPair<QStringList, int>> rawPilotsAndIds;
+    QVector<QStringList> rawData;
+
+    /*!
+     * \brief processedPilotMaps Holds a map of the processed pilots
+     *
+     * \details Here, the imported pilots are stored after having been read from the CSV file.
+     * The key is the original string as it was originally in the PILOT_NAME field and allows
+     * mapping the data to ID's later on. The value is a QMap<QString, QVariant> and contains
+     * the data processed as it will be represented in the OPL database later on.
+     */
+    QMap<QString, QMap<QString, QVariant>> processedPilotMaps;
+
+    /*!
+     * \brief processedPilotsIds Holds a map of the ids that have been given to the processed pilots
+     *
+     * \details The pilot data, once processed is held in processedPilotMaps. With processePilotsIds it
+     * is possible to map the original String to the PilotID that has been assigned during the parsing process.
+     */
+    QMap<QString, int> processedPilotsIds;
+};
+
+#endif // PROCESSPILOTS_H

Some files were not shown because too many files changed in this diff