Forráskód Böngészése

Merge pull request #88 from fiffty-50/develop-database-setup

Merge Database setup redesign
Felix Turowsky 2 éve
szülő
commit
b727f7906c

+ 1 - 3
CMakeLists.txt

@@ -122,12 +122,10 @@ set(PROJECT_SOURCES
     src/database/adatabasetypes.h
     src/database/adatabase.h
     src/database/adatabase.cpp
-    src/database/adbsetup.h
-    src/database/adbsetup.cpp
 
     # Ressources
     assets/icons.qrc
-    assets/templates.qrc
+    assets/database/templates.qrc
     assets/themes/stylesheets/breeze/breeze.qrc
     assets/themes/stylesheets/qdarkstyle/qdarkstyle.qrc
 

+ 1 - 0
assets/database/database_schema.md5

@@ -0,0 +1 @@
+c71c34d120dbc8fe7186c303d5cfb6bb  database_schema.sql

+ 288 - 0
assets/database/database_schema.sql

@@ -0,0 +1,288 @@
+DROP TABLE IF EXISTS 'pilots';
+CREATE TABLE IF NOT EXISTS 'pilots' (
+	'pilot_id'	INTEGER NOT NULL,
+	'lastname'	TEXT NOT NULL,
+	'firstname'	TEXT,
+	'alias'	TEXT,
+	'company'	TEXT,
+	'employeeid'	TEXT,
+	'phone'	TEXT,
+	'email'	TEXT,
+	PRIMARY KEY('pilot_id' AUTOINCREMENT)
+);
+DROP TABLE IF EXISTS 'tails';
+CREATE TABLE IF NOT EXISTS 'tails' (
+	'tail_id'	INTEGER NOT NULL,
+	'registration'	TEXT NOT NULL,
+	'company'	TEXT,
+	'make'	TEXT,
+	'model'	TEXT,
+	'variant'	TEXT,
+	'multipilot'	INTEGER,
+	'multiengine'	INTEGER,
+	'engineType'	INTEGER,
+	'weightClass'	INTEGER,
+	PRIMARY KEY('tail_id' AUTOINCREMENT)
+);
+DROP TABLE IF EXISTS 'flights';
+CREATE TABLE IF NOT EXISTS 'flights' (
+	'flight_id'	INTEGER NOT NULL,
+	'doft'	NUMERIC NOT NULL,
+	'dept'	TEXT NOT NULL,
+	'dest'	TEXT NOT NULL,
+	'tofb'	INTEGER NOT NULL,
+	'tonb'	INTEGER NOT NULL,
+	'pic'	INTEGER NOT NULL,
+	'acft'	INTEGER NOT NULL,
+	'tblk'	INTEGER NOT NULL,
+	'tSPSE'	INTEGER,
+	'tSPME'	INTEGER,
+	'tMP'	INTEGER,
+	'tNIGHT'	INTEGER,
+	'tIFR'	INTEGER,
+	'tPIC'	INTEGER,
+	'tPICUS'	INTEGER,
+	'tSIC'	INTEGER,
+	'tDUAL'	INTEGER,
+	'tFI'	INTEGER,
+	'tSIM'	INTEGER,
+	'pilotFlying'	INTEGER,
+	'toDay'	INTEGER,
+	'toNight'	INTEGER,
+	'ldgDay'	INTEGER,
+	'ldgNight'	INTEGER,
+	'autoland'	INTEGER,
+	'secondPilot'	INTEGER,
+	'thirdPilot'	INTEGER,
+	'approachType'	TEXT,
+	'flightNumber'	TEXT,
+	'remarks'	TEXT,
+	FOREIGN KEY('pic') REFERENCES 'pilots'('pilot_id') ON DELETE RESTRICT,
+	FOREIGN KEY('acft') REFERENCES 'tails'('tail_id') ON DELETE RESTRICT,
+	PRIMARY KEY('flight_id' AUTOINCREMENT)
+);
+DROP TABLE IF EXISTS 'aircraft';
+CREATE TABLE IF NOT EXISTS 'aircraft' (
+	'aircraft_id'	INTEGER NOT NULL,
+	'make'	TEXT,
+	'model'	TEXT,
+	'variant'	TEXT,
+	'name'	TEXT,
+	'iata'	TEXT,
+	'icao'	TEXT,
+	'multipilot'	INTEGER,
+	'multiengine'	INTEGER,
+	'engineType'	INTEGER,
+	'weightClass'	INTEGER,
+	PRIMARY KEY('aircraft_id' AUTOINCREMENT)
+);
+DROP TABLE IF EXISTS 'airports';
+CREATE TABLE IF NOT EXISTS 'airports' (
+	'airport_id'	INTEGER NOT NULL,
+	'icao'	TEXT NOT NULL,
+	'iata'	TEXT,
+	'name'	TEXT,
+	'lat'	REAL,
+	'long'	REAL,
+	'country'	TEXT,
+	'alt'	INTEGER,
+	'utcoffset'	INTEGER,
+	'tzolson'	TEXT,
+	PRIMARY KEY('airport_id' AUTOINCREMENT)
+);
+DROP TABLE IF EXISTS 'currencies';
+CREATE TABLE IF NOT EXISTS 'currencies' (
+	'currency_id'	INTEGER NOT NULL,
+	'description'	TEXT,
+	'expiryDate'	NUMERIC,
+	PRIMARY KEY('currency_id' AUTOINCREMENT)
+);
+DROP TABLE IF EXISTS 'changelog';
+CREATE TABLE IF NOT EXISTS 'changelog' (
+	'revision'	INTEGER NOT NULL,
+	'comment'	TEXT,
+	'date'	NUMERIC,
+	PRIMARY KEY('revision' AUTOINCREMENT)
+);
+DROP TABLE IF EXISTS 'simulators';
+CREATE TABLE IF NOT EXISTS 'simulators' (
+	'session_id'	INTEGER NOT NULL,
+	'date'	NUMERIC NOT NULL,
+	'totalTime'	INTEGER NOT NULL,
+	'deviceType'	TEXT NOT NULL,
+	'aircraftType'	TEXT,
+	'registration'	TEXT,
+	'remarks'	TEXT,
+	PRIMARY KEY('session_id' AUTOINCREMENT)
+);
+
+DROP VIEW IF EXISTS 'viewDefault';
+CREATE VIEW viewDefault AS  
+SELECT flight_id, 
+doft as 'Date',  
+dept AS 'Dept',  
+printf('%02d',(tofb/60))||':'||printf('%02d',(tofb%60)) AS 'Time',  
+dest AS 'Dest', printf('%02d',(tonb/60))||':'||printf('%02d',(tonb%60)) AS 'Time ',  
+printf('%02d',(tblk/60))||':'||printf('%02d',(tblk%60)) AS 'Total',  
+CASE  WHEN pilot_id = 1 THEN alias  ELSE lastname||', '||substr(firstname, 1, 1)||'.'  END  AS 'Name PIC',  
+CASE  WHEN variant IS NOT NULL THEN make||' '||model||'-'||variant  ELSE make||' '||model  END  AS 'Type',  
+registration AS 'Registration',  
+FlightNumber AS 'Flight #',  
+remarks AS 'Remarks' 
+FROM flights  
+INNER JOIN pilots on flights.pic = pilots.pilot_id  
+INNER JOIN tails on flights.acft = tails.tail_id  
+ORDER BY date DESC;
+
+DROP VIEW IF EXISTS 'viewDefaultSim';
+CREATE VIEW viewDefaultSim AS 
+SELECT flight_id AS 'rowid',   
+doft as 'Date',   
+dept AS 'Dept',   
+printf('%02d',(tofb/60))||':'||printf('%02d',(tofb%60)) AS 'Time',   
+dest AS 'Dest', printf('%02d',(tonb/60))||':'||printf('%02d',(tonb%60)) AS 'Time ',   
+printf('%02d',(tblk/60))||':'||printf('%02d',(tblk%60)) AS 'Total',   
+CASE  WHEN pilot_id = 1 THEN alias  ELSE lastname||', '||substr(firstname, 1, 1)||'.'  END  AS 'Name PIC',   
+CASE  WHEN variant IS NOT NULL THEN make||' '||model||'-'||variant  ELSE make||' '||model  END  AS 'Type',   
+registration AS 'Registration',    
+null AS 'Sim Type', 
+null AS 'Time of Session', 
+remarks AS 'Remarks' 
+FROM flights   
+INNER JOIN pilots on flights.pic = pilots.pilot_id  
+INNER JOIN tails on flights.acft = tails.tail_id   
+UNION 
+SELECT (session_id * -1), 
+date, 
+null, null, null, null, 
+'SIM', 
+null, 
+aircraftType, 
+registration, 
+deviceType, 
+printf('%02d',(totalTime/60))||':'||printf('%02d',(totalTime%60)), 
+remarks 
+FROM simulators 
+ORDER BY date DESC;
+
+DROP VIEW IF EXISTS 'viewEasa';
+CREATE VIEW viewEasa AS  SELECT  flight_id, 
+doft as 'Date',  
+dept AS 'Dept',  
+printf('%02d',(tofb/60))||':'||printf('%02d',(tofb%60)) AS 'Time',  
+dest AS 'Dest', 
+printf('%02d',(tonb/60))||':'||printf('%02d',(tonb%60)) AS 'Time ',  
+CASE  WHEN variant IS NOT NULL THEN make||' '||model||'-'||variant  ELSE make||' '||model  END  AS 'Type',  
+registration AS 'Registration',  
+(SELECT printf('%02d',(tSPSE/60))||':'||printf('%02d',(tSPSE%60)) WHERE tSPSE IS NOT NULL) AS 'SP SE',  
+(SELECT printf('%02d',(tSPME/60))||':'||printf('%02d',(tSPME%60)) WHERE tSPME IS NOT NULL) AS 'SP ME',  
+(SELECT printf('%02d',(tMP/60))||':'||printf('%02d',(tMP%60)) WHERE tMP IS NOT NULL) AS 'MP',  
+printf('%02d',(tblk/60))||':'||printf('%02d',(tblk%60)) AS 'Total',  
+CASE  WHEN pilot_id = 1 THEN alias  ELSE lastname||', '||substr(firstname, 1, 1)||'.'  END  AS 'Name PIC',  
+ldgDay AS 'L/D',  
+ldgNight AS 'L/N',  
+(SELECT printf('%02d',(tNight/60))||':'||printf('%02d',(tNight%60)) WHERE tNight IS NOT NULL)  AS 'Night',  
+(SELECT printf('%02d',(tIFR/60))||':'||printf('%02d',(tIFR%60)) WHERE tIFR IS NOT NULL)  AS 'IFR',  
+(SELECT printf('%02d',(tPIC/60))||':'||printf('%02d',(tPIC%60)) WHERE tPIC IS NOT NULL)  AS 'PIC',  
+(SELECT printf('%02d',(tSIC/60))||':'||printf('%02d',(tSIC%60)) WHERE tSIC IS NOT NULL)  AS 'SIC',  
+(SELECT printf('%02d',(tDual/60))||':'||printf('%02d',(tDual%60)) WHERE tDual IS NOT NULL)  AS 'Dual',  
+(SELECT printf('%02d',(tFI/60))||':'||printf('%02d',(tFI%60)) WHERE tFI IS NOT NULL)  AS 'FI',  
+remarks AS 'Remarks'  
+FROM flights  
+INNER JOIN pilots on flights.pic = pilots.pilot_id  
+INNER JOIN tails on flights.acft = tails.tail_id  ORDER BY date DESC;
+
+DROP VIEW IF EXISTS 'viewEasaSim';
+CREATE VIEW viewEasaSim AS  SELECT  flight_id, 
+doft as 'Date',    
+dept AS 'Dept',  
+printf('%02d',(tofb/60))||':'||printf('%02d',(tofb%60)) AS 'Time',    
+dest AS 'Dest', 
+printf('%02d',(tonb/60))||':'||printf('%02d',(tonb%60)) AS 'Time ',    
+CASE  WHEN variant IS NOT NULL THEN make||' '||model||'-'||variant  ELSE make||' '||model  END  AS 'Type',    
+registration AS 'Registration',    
+(SELECT printf('%02d',(tSPSE/60))||':'||printf('%02d',(tSPSE%60)) WHERE tSPSE IS NOT NULL) AS 'SP SE', 
+(SELECT printf('%02d',(tSPME/60))||':'||printf('%02d',(tSPME%60)) WHERE tSPME IS NOT NULL) AS 'SP ME',    
+(SELECT printf('%02d',(tMP/60))||':'||printf('%02d',(tMP%60)) WHERE tMP IS NOT NULL) AS 'MP',    
+printf('%02d',(tblk/60))||':'||printf('%02d',(tblk%60)) AS 'Total',    
+CASE  WHEN pilot_id = 1 THEN alias  ELSE lastname||', '||substr(firstname, 1, 1)||'.'  END  AS 'Name PIC',    
+ldgDay AS 'L/D',    
+ldgNight AS 'L/N',    
+(SELECT printf('%02d',(tNight/60))||':'||printf('%02d',(tNight%60)) WHERE tNight IS NOT NULL)  AS 'Night',    
+(SELECT printf('%02d',(tIFR/60))||':'||printf('%02d',(tIFR%60)) WHERE tIFR IS NOT NULL)  AS 'IFR',    
+(SELECT printf('%02d',(tPIC/60))||':'||printf('%02d',(tPIC%60)) WHERE tPIC IS NOT NULL)  AS 'PIC',    
+(SELECT printf('%02d',(tSIC/60))||':'||printf('%02d',(tSIC%60)) WHERE tSIC IS NOT NULL)  AS 'SIC',    
+(SELECT printf('%02d',(tDual/60))||':'||printf('%02d',(tDual%60)) WHERE tDual IS NOT NULL)  AS 'Dual',    
+(SELECT printf('%02d',(tFI/60))||':'||printf('%02d',(tFI%60)) WHERE tFI IS NOT NULL)  AS 'FI',  
+null AS 'Sim Type',  
+null AS 'Time of Session',  
+remarks AS 'Remarks'    
+FROM flights    
+INNER JOIN pilots on flights.pic = pilots.pilot_id    
+INNER JOIN tails on flights.acft = tails.tail_id    
+UNION  
+SELECT (session_id * -1),  
+date,  
+null,  null,  null,  null,  
+aircraftType,  
+registration,  
+null,  null,  null,  
+'SIM',  
+null,  null,  null,  null,  null,  null,  null,  null,  null,  
+deviceType,  printf('%02d',(totalTime/60))||':'||printf('%02d',(totalTime%60)),  
+remarks  
+FROM simulators  
+ORDER BY date DESC;
+
+DROP VIEW IF EXISTS 'viewSimulators';
+CREATE VIEW viewSimulators AS SELECT (session_id * -1),  
+date as 'Date',  
+registration AS 'Registration',   
+aircraftType AS 'Aircraft Type',   
+deviceType 'Sim Type',  
+printf('%02d',(totalTime/60))||':'||printf('%02d',(totalTime%60)) AS 'Time of Session',  
+remarks AS 'Remarks'  
+FROM simulators  
+ORDER BY date DESC;
+
+DROP VIEW IF EXISTS 'viewTails';
+CREATE VIEW viewTails AS  
+SELECT  tail_id AS 'ID',  
+registration AS 'Registration',  
+make||' '||model AS 'Type',  
+company AS 'Company'  
+FROM tails WHERE model IS NOT NULL AND variant IS NULL  
+UNION  
+SELECT  tail_id AS 'ID',  
+registration AS 'Registration',  
+make||' '||model||'-'||variant AS 'Type',  
+company AS 'Company'  
+FROM tails WHERE variant IS NOT NULL;
+
+DROP VIEW IF EXISTS 'viewPilots';
+CREATE VIEW viewPilots AS  
+SELECT  pilot_id AS 'ID',  
+lastname AS 'Last Name',  
+firstname AS 'First Name',  
+company AS 'Company'  
+FROM pilots;
+
+DROP VIEW IF EXISTS 'viewTotals';
+CREATE VIEW viewTotals AS  
+SELECT  printf('%02d',CAST(SUM(tblk) AS INT)/60)||':'||printf('%02d',CAST(SUM(tblk) AS INT)%60) AS 'TOTAL',  
+printf('%02d',CAST(SUM(tSPSE) AS INT)/60)||':'||printf('%02d',CAST(SUM(tSPSE) AS INT)%60) AS 'SP SE',  
+printf('%02d',CAST(SUM(tSPME) AS INT)/60)||':'||printf('%02d',CAST(SUM(tSPME) AS INT)%60) AS 'SP ME',  
+printf('%02d',CAST(SUM(tNIGHT) AS INT)/60)||':'||printf('%02d',CAST(SUM(tNIGHT) AS INT)%60) AS 'NIGHT',  
+printf('%02d',CAST(SUM(tIFR) AS INT)/60)||':'||printf('%02d',CAST(SUM(tIFR) AS INT)%60) AS 'IFR',  
+printf('%02d',CAST(SUM(tPIC) AS INT)/60)||':'||printf('%02d',CAST(SUM(tPIC) AS INT)%60) AS 'PIC',  
+printf('%02d',CAST(SUM(tPICUS) AS INT)/60)||':'||printf('%02d',CAST(SUM(tPICUS) AS INT)%60) AS 'PICUS',  
+printf('%02d',CAST(SUM(tSIC) AS INT)/60)||':'||printf('%02d',CAST(SUM(tSIC) AS INT)%60) AS 'SIC',  
+printf('%02d',CAST(SUM(tDual) AS INT)/60)||':'||printf('%02d',CAST(SUM(tDual) AS INT)%60) AS 'DUAL',  
+printf('%02d',CAST(SUM(tFI) AS INT)/60)||':'||printf('%02d',CAST(SUM(tFI) AS INT)%60) AS 'INSTRUCTOR',  
+printf('%02d',CAST(SUM(tSIM) AS INT)/60)||':'||printf('%02d',CAST(SUM(tSIM) AS INT)%60) AS 'SIMULATOR',  
+printf('%02d',CAST(SUM(tMP) AS INT)/60)||':'||printf('%02d',CAST(SUM(tMP) AS INT)%60) AS 'MultPilot',  
+CAST(SUM(toDay) AS INT) AS 'TO Day', 
+CAST(SUM(toNight) AS INT) AS 'TO Night',  
+CAST(SUM(ldgDay) AS INT) AS 'LDG Day', 
+CAST(SUM(ldgNight) AS INT) AS 'LDG Night'  
+FROM flights

+ 9 - 0
assets/database/templates.qrc

@@ -0,0 +1,9 @@
+<RCC>
+    <qresource prefix="/database">
+        <file>templates/aircraft.json</file>
+        <file>templates/airports.json</file>
+        <file>templates/changelog.json</file>
+        <file>templates/currencies.json</file>
+        <file>database_schema.sql</file>
+    </qresource>
+</RCC>

+ 0 - 8
assets/templates.qrc

@@ -1,8 +0,0 @@
-<RCC>
-    <qresource prefix="/templates">
-        <file>database/templates/aircraft.json</file>
-        <file>database/templates/airports.json</file>
-        <file>database/templates/changelog.json</file>
-        <file>database/templates/currencies.json</file>
-    </qresource>
-</RCC>

+ 10 - 12
mainwindow.cpp

@@ -30,8 +30,15 @@
 // Quick and dirty Debug area
 void MainWindow::doDebugStuff()
 {
-    auto nsd = new NewSimDialog(1, this);
-    nsd->exec();
+    //QSqlQuery query;
+    QFile f(OPL::Assets::DATABASE_SCHEMA);
+    f.open(QIODevice::ReadOnly);
+    QByteArray filedata = f.readAll();
+
+    auto list = filedata.split(';');
+    for (const auto &string : list)
+        //query.exec(string);
+        LOG << string;
 }
 
 MainWindow::MainWindow(QWidget *parent)
@@ -45,19 +52,10 @@ MainWindow::MainWindow(QWidget *parent)
     // connect to the Database
     QFileInfo database_file(AStandardPaths::directory(AStandardPaths::Database).
                                          absoluteFilePath(QStringLiteral("logbook.db")));
-    bool db_invalid = false;
-    if (!database_file.exists()) {
-        WARN(tr("Error: Database file not found."));
-        db_invalid = true;
-    } else 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)
+    if (database_file.size() == 0)
         onDatabaseInvalid();
 
-
     if(!aDB->connect()){
         WARN(tr("Error establishing database connection."));
     }

+ 2 - 2
src/classes/ajson.cpp

@@ -16,7 +16,7 @@
  *along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 #include "ajson.h"
-#include "src/database/adbsetup.h"
+#include "src/database/adatabase.h"
 
 const QList<QPair<TableName_T, ADatabaseTable>> AJson::tables {
     qMakePair(OPL::Db::TABLE_TAILS, ADatabaseTable::tails),
@@ -53,7 +53,7 @@ void AJson::importDatabase()
         q.exec();
         const auto doc = readFileToDoc(AStandardPaths::asChildOfDir(AStandardPaths::JSON,
                                                                pair.first + QLatin1String(".json")));
-        aDbSetup::commitData(doc.array(), pair.first);
+        aDB->commit(doc.array(), pair.first);
     }
 }
 

+ 129 - 7
src/database/adatabase.cpp

@@ -20,6 +20,7 @@
 #include "src/classes/astandardpaths.h"
 #include "src/opl.h"
 #include "src/functions/alog.h"
+#include "src/classes/ajson.h"
 
 const int ADatabase::minimumDatabaseRevision = 0;
 
@@ -59,11 +60,16 @@ int ADatabase::getMinimumDatabaseRevision()
     return minimumDatabaseRevision;
 }
 
-QStringList ADatabase::getTemplateTableNames()
+const QStringList &ADatabase::getTemplateTableNames() const
 {
     return templateTableNames;
 }
 
+const QStringList& ADatabase::getUserTableNames() const
+{
+    return userTableNames;
+}
+
 UserDataState ADatabase::getUserDataState()
 {
     QSqlQuery q;
@@ -80,9 +86,17 @@ UserDataState ADatabase::getUserDataState()
     return UserDataState(tails, pilots);
 }
 
-QStringList ADatabase::getUserTableNames()
+bool ADatabase::resetUserData()
 {
-    return userTableNames;
+    QSqlQuery query;
+    for (const auto& table : userTableNames) {
+        query.prepare(QLatin1String("DELETE FROM ") + table);
+        if (!query.exec()) {
+            DEB << "Error: " << query.lastError().text();
+            return false;
+        }
+    }
+    return true;
 }
 
 const ColumnNames_T ADatabase::getTableColumns(TableName_T table_name) const
@@ -120,6 +134,76 @@ ADatabase* ADatabase::instance()
     return self;
 }
 
+bool ADatabase::createSchema()
+{
+    // Read Database layout from sql file
+    QFile f(OPL::Assets::DATABASE_SCHEMA);
+    f.open(QIODevice::ReadOnly);
+    QByteArray filedata = f.readAll();
+    // create individual queries for each table/view
+    auto list = filedata.split(';');
+
+    // Create Tables
+    QSqlQuery q;
+    QVector<QSqlError> errors;
+    for (const auto &query_string : list) {
+        q.prepare(query_string);
+        if (!q.exec()) {
+            errors.append(q.lastError());
+            LOG << "Unable to execute query: ";
+            LOG << q.lastQuery();
+            LOG << q.lastError();
+        }
+    }
+    updateLayout();
+
+    if (errors.isEmpty()) {
+        LOG << "Database succesfully created.";
+        return true;
+    } else {
+        LOG << "Database creation has failed. The following error(s) have ocurred: ";
+        for (const auto &error : qAsConst(errors)) {
+            LOG << error.type() << error.text();
+        }
+        return false;
+    }
+}
+
+bool ADatabase::importTemplateData(bool use_local_ressources)
+{
+    for (const auto& table_name : templateTableNames) {
+
+        //clear table
+        QSqlQuery q;
+        q.prepare(QLatin1String("DELETE FROM ") + table_name);
+        if (!q.exec()) {
+            LOG << "Error clearing tables: " << q.lastError().text();
+            return false;
+        }
+
+        //Prepare data
+        QJsonArray data_to_commit;
+        QString error_message("Error importing data ");
+
+        if (use_local_ressources) {
+            data_to_commit = AJson::readFileToDoc(QLatin1String(":database/templates/")
+                                      + table_name + QLatin1String(".json")).array();
+            error_message.append(QLatin1String(" (ressource) "));
+        } else {
+            data_to_commit = AJson::readFileToDoc(AStandardPaths::directory(
+                                          AStandardPaths::Templates).absoluteFilePath(
+                                          table_name + QLatin1String(".json"))).array();
+            error_message.append(QLatin1String(" (downloaded) "));
+        }
+
+        // commit Data from Array
+        if (!commit(data_to_commit, table_name)) {
+            LOG << error_message;
+            return false;
+        }
+    } // for table_name
+    return true;
+}
 
 const QString ADatabase::sqliteVersion() const
 {
@@ -162,6 +246,7 @@ void ADatabase::disconnect()
 {
     auto db = ADatabase::database();
     db.close();
+    db.removeDatabase(db.connectionName());
     LOG << "Database connection closed.";
 }
 
@@ -179,6 +264,43 @@ bool ADatabase::commit(const AEntry &entry)
     }
 }
 
+bool ADatabase::commit(const QJsonArray &json_arr, const QString &table_name)
+{
+    // create statement
+    QString statement = QLatin1String("INSERT INTO ") + table_name + QLatin1String(" (");
+    QString placeholder = QStringLiteral(") VALUES (");
+    for (const auto &column_name : aDB->getTableColumns(table_name)) {
+        statement += column_name + ',';
+        placeholder.append(QLatin1Char(':') + column_name + QLatin1Char(','));
+    }
+
+    statement.chop(1);
+    placeholder.chop(1);
+    placeholder.append(')');
+    statement.append(placeholder);
+
+    // Create query and commit
+    QSqlQuery q;
+    q.prepare(QStringLiteral("BEGIN EXCLUSIVE TRANSACTION"));
+    q.exec();
+    for (const auto &entry : json_arr) {
+        q.prepare(statement);
+        auto object = entry.toObject();
+        const auto keys = object.keys();
+        for (const auto &key : keys){
+            object.value(key).isNull() ? q.bindValue(key, QVariant(QVariant::String)) : // refactor to use QMetaType with Qt6
+                                         q.bindValue(QLatin1Char(':') + key, object.value(key).toVariant());
+        }
+        q.exec();
+    }
+
+    q.prepare(QStringLiteral("COMMIT"));
+    if (q.exec())
+        return true;
+    else
+        return false;
+}
+
 bool ADatabase::remove(const AEntry &entry)
 {
     if (!exists(entry)) {
@@ -207,7 +329,7 @@ bool ADatabase::remove(const AEntry &entry)
     }
 }
 
-bool ADatabase::removeMany(QList<DataPosition> data_position_list)
+bool ADatabase::removeMany(const QList<DataPosition> &data_position_list)
 {
     int errorCount = 0;
     QSqlQuery query;
@@ -401,7 +523,7 @@ bool ADatabase::insert(const AEntry &new_entry)
 
 }
 
-RowData_T ADatabase::getEntryData(DataPosition data_position)
+RowData_T ADatabase::getEntryData(const DataPosition &data_position)
 {
     // check table exists
     if (!getTableNames().contains(data_position.tableName)) {
@@ -458,7 +580,7 @@ RowData_T ADatabase::getEntryData(DataPosition data_position)
     return entry_data;
 }
 
-AEntry ADatabase::getEntry(DataPosition data_position)
+AEntry ADatabase::getEntry(const DataPosition &data_position)
 {
     AEntry entry(data_position);
     entry.setData(getEntryData(data_position));
@@ -500,7 +622,7 @@ ASimulatorEntry ADatabase::getSimEntry(RowId_T row_id)
     return sim_entry;
 }
 
-ACurrencyEntry ADatabase::getCurrencyEntry(ACurrencyEntry::CurrencyName currency_name)
+ACurrencyEntry ADatabase::getCurrencyEntry(const ACurrencyEntry::CurrencyName &currency_name)
 {
     ACurrencyEntry currency_entry(currency_name);
     currency_entry.setData(getEntryData(currency_entry.getPosition()));

+ 41 - 10
src/database/adatabase.h

@@ -157,7 +157,12 @@ private:
     const static QStringList userTableNames;
     const static QStringList templateTableNames;
     const static int minimumDatabaseRevision;
+
 public:
+    ADatabase(const ADatabase&) = delete;
+    void operator=(const ADatabase&) = delete;
+    static ADatabase* instance();
+
     /*!
      * \brief Holds information about the last error that ocurred during
      * a SQL operation. If the error type is QSqlError::UnknownError, the error is related to data
@@ -170,10 +175,24 @@ public:
 
     const QFileInfo databaseFile;
 
-    // Ensure DB is not copiable or assignable
-    ADatabase(const ADatabase&) = delete;
-    void operator=(const ADatabase&) = delete;
-    static ADatabase* instance();
+
+
+    /*!
+     * \brief Create or restore the database to its ready-to-use but empty state
+     * \details The SQL code for the database creation is stored in a .sql file which is available as a ressource.
+     * This file gets read, and the querys executed. If errors occur, returns false.
+     */
+    bool createSchema();
+
+    /*!
+     * \brief importTemplateData fills an empty database with the template
+     * data (Aircraft, Airports, currencies, changelog) as read from the JSON
+     * templates.
+     * \param use_local_ressources determines whether the included ressource files
+     * or a previously downloaded file should be used.
+     * \return
+     */
+    bool importTemplateData(bool use_local_ressources = true);
 
     /*!
      * \brief dbRevision returns the database Revision Number. The Revision refers to what iteration
@@ -245,6 +264,13 @@ public:
      */
     bool commit(const AEntry &entry);
 
+    /*!
+     * \brief commits data imported from JSON
+     * \details This function is used to import values to the databases which are held in JSON documents.
+     * These entries are pre-filled data used for providing completion data, such as Airport or Aircraft Type Data.
+     */
+    bool commit(const QJsonArray &json_arr, const QString &table_name);
+
     /*!
      * \brief Create new entry in the databse based on UserInput
      */
@@ -264,17 +290,17 @@ public:
      * \brief deletes a list of entries from the database. Optimised for speed when
      * deleting many entries.
      */
-    bool removeMany(QList<DataPosition>);
+    bool removeMany(const QList<DataPosition> &);
 
     /*!
      * \brief retreive entry data from the database to create an entry object
      */
-    RowData_T getEntryData(DataPosition data_position);
+    RowData_T getEntryData(const DataPosition &data_position);
 
     /*!
      * \brief retreive an Entry from the database.
      */
-    AEntry getEntry(DataPosition data_position);
+    AEntry getEntry(const DataPosition &data_position);
 
     /*!
      * \brief retreives a PilotEntry from the database.
@@ -329,7 +355,7 @@ public:
     /*!
      * \brief Retreives a currency entry from the database.
      */
-    ACurrencyEntry getCurrencyEntry(ACurrencyEntry::CurrencyName currency_name);
+    ACurrencyEntry getCurrencyEntry(const ACurrencyEntry::CurrencyName &currency_name);
 
     /*!
      * \brief getCompletionList returns a QStringList of values for a
@@ -394,13 +420,13 @@ public:
      * \brief getUserTableNames returns a list of the table names of tables that contain user-created data
      * (flights, pilots,..)
      */
-    QStringList getUserTableNames();
+    const QStringList &getUserTableNames() const;
 
     /*!
      * \brief getTemplateTableNames returns a list of the table names of tables that contain template data
      * (aiports, aircraft,..)
      */
-    QStringList getTemplateTableNames();
+    const QStringList &getTemplateTableNames() const;
 
     /*!
      * \brief getUserDataState returns a struct containing the current amount of entries in the tails and
@@ -409,6 +435,11 @@ public:
      */
     UserDataState getUserDataState();
 
+    /*!
+     * \brief Delete all rows from the user data tables (flights, pliots, tails)
+     */
+    bool resetUserData();
+
     /*!
      * \brief getMinimumDatabaseRevision returns the minimum required database revision number required by the application.
      */

+ 0 - 517
src/database/adbsetup.cpp

@@ -1,517 +0,0 @@
-/*
- *openPilotLog - A FOSS Pilot Logbook Application
- *Copyright (C) 2020-2021 Felix Turowsky
- *
- *This program is free software: you can redistribute it and/or modify
- *it under the terms of the GNU General Public License as published by
- *the Free Software Foundation, either version 3 of the License, or
- *(at your option) any later version.
- *
- *This program is distributed in the hope that it will be useful,
- *but WITHOUT ANY WARRANTY; without even the implied warranty of
- *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *GNU General Public License for more details.
- *
- *You should have received a copy of the GNU General Public License
- *along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-#include "adbsetup.h"
-#include "src/opl.h"
-#include "src/database/adatabase.h"
-#include "src/functions/alog.h"
-#include "src/classes/ajson.h"
-
-namespace aDbSetup {
-
-// const auto TEMPLATE_URL = QStringLiteral("https://raw.githubusercontent.com/fiffty-50/openpilotlog/tree/main/assets/database/templates/");
-const static auto TEMPLATE_URL = QStringLiteral("https://raw.githubusercontent.com/fiffty-50/openpilotlog/develop/assets/database/templates/");
-
-
-const static auto CREATE_TABLE_PILOTS = QStringLiteral("CREATE TABLE pilots ( "
-            " pilot_id       INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, "
-            " lastname       TEXT    NOT NULL, "
-            " firstname      TEXT, "
-            " alias          TEXT, "
-            " company        TEXT, "
-            " employeeid     TEXT, "
-            " phone          TEXT, "
-            " email          TEXT "
-//            " PRIMARY KEY(pilot_id AUTOINCREMENT)"
-            ")"
-                                                       );
-
-const static auto CREATE_TABLE_TAILS = QStringLiteral("CREATE TABLE tails ("
-            " tail_id        INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,"
-            " registration   TEXT NOT NULL,"
-            " company        TEXT,"
-            " make           TEXT,"
-            " model          TEXT,"
-            " variant        TEXT,"
-            " multipilot     INTEGER,"
-            " multiengine    INTEGER,"
-            " engineType     INTEGER,"
-            " weightClass    INTEGER"
-//            " PRIMARY KEY(tail_id AUTOINCREMENT)"
-            ")"
-                                                      );
-
-const static auto CREATE_TABLE_FLIGHTS = QStringLiteral("CREATE TABLE flights ("
-            " flight_id      INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, "
-            " doft           NUMERIC NOT NULL, "
-            " dept           TEXT NOT NULL, "
-            " dest           TEXT NOT NULL, "
-            " tofb           INTEGER NOT NULL, "
-            " tonb           INTEGER NOT NULL, "
-            " pic            INTEGER NOT NULL, "
-            " acft           INTEGER NOT NULL, "
-            " tblk           INTEGER NOT NULL, "
-            " tSPSE          INTEGER, "
-            " tSPME          INTEGER, "
-            " tMP            INTEGER, "
-            " tNIGHT         INTEGER, "
-            " tIFR           INTEGER, "
-            " tPIC           INTEGER, "
-            " tPICUS         INTEGER, "
-            " tSIC           INTEGER, "
-            " tDUAL          INTEGER, "
-            " tFI            INTEGER, "
-            " tSIM           INTEGER, "
-            " pilotFlying    INTEGER, "
-            " toDay          INTEGER, "
-            " toNight        INTEGER, "
-            " ldgDay         INTEGER, "
-            " ldgNight       INTEGER, "
-            " autoland       INTEGER, "
-            " secondPilot    INTEGER, "
-            " thirdPilot     INTEGER, "
-            " approachType   TEXT, "
-            " flightNumber   TEXT, "
-            " remarks        TEXT, "
-            " FOREIGN KEY(pic)  REFERENCES pilots(pilot_id) ON DELETE RESTRICT, "
-            " FOREIGN KEY(acft) REFERENCES tails(tail_id)   ON DELETE RESTRICT "
-//            " PRIMARY KEY(flight_id    AUTOINCREMENT) "
-        ")"
-                                                        );
-
-const static auto CREATE_TABLE_AIRPORTS = QStringLiteral("CREATE TABLE airports ( "
-            " airport_id     INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, "
-            " icao           TEXT NOT NULL, "
-            " iata           TEXT, "
-            " name           TEXT, "
-            " lat            REAL, "
-            " long           REAL, "
-            " country        TEXT, "
-            " alt            INTEGER, "
-            " utcoffset      INTEGER, "
-            " tzolson        TEXT "
- //           " PRIMARY KEY(airport_id AUTOINCREMENT) "
-            ")"
-                                                         );
-
-const static auto CREATE_TABLE_AIRCRAFT = QStringLiteral("CREATE TABLE aircraft ("
-            " aircraft_id   INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,"
-            " make          TEXT,"
-            " model         TEXT,"
-            " variant       TEXT,"
-            " name          TEXT,"
-            " iata          TEXT,"
-            " icao          TEXT,"
-            " multipilot    INTEGER,"
-            " multiengine   INTEGER,"
-            " engineType    INTEGER,"
-            " weightClass   INTEGER"
-//            " PRIMARY KEY(aircraft_id AUTOINCREMENT)"
-            ")"
-                                                         );
-
-const static auto CREATE_TABLE_CHANGELOG = QStringLiteral("CREATE TABLE changelog ( "
-            " revision   INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, "
-            " comment    TEXT, "
-            " date       NUMERIC "
-//            " PRIMARY KEY(revision) "
-            ")"
-                                                          );
-
-const static auto CREATE_TABLE_CURRENCIES = QStringLiteral("CREATE TABLE currencies ( "
-            " currency_id	INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, "
-            " description	TEXT, "
-            " expiryDate     NUMERIC "
-            ")"
-                                                           );
-
-const static auto CREATE_TABLE_SIMULATORS = QStringLiteral("CREATE TABLE simulators ( "
-                                                       " session_id	INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, "
-                                                       " date	NUMERIC NOT NULL, "
-                                                       " totalTime	INTEGER NOT NULL, "
-                                                       " deviceType	TEXT NOT NULL, "
-                                                       " aircraftType	TEXT, "
-                                                       " registration	TEXT, "
-                                                       " remarks	TEXT "
-                                                       ")"
-                                                   );
-
-// Statements for creation of views in the database
-const static auto CREATE_VIEW_DEFAULT = QStringLiteral("CREATE VIEW viewDefault AS "
-        " SELECT flight_id, doft as 'Date', "
-        " dept AS 'Dept', "
-        " printf('%02d',(tofb/60))||':'||printf('%02d',(tofb%60)) AS 'Time', "
-        " dest AS 'Dest', printf('%02d',(tonb/60))||':'||printf('%02d',(tonb%60)) AS 'Time ', "
-        " printf('%02d',(tblk/60))||':'||printf('%02d',(tblk%60)) AS 'Total', "
-        " CASE "
-        " WHEN pilot_id = 1 THEN alias "
-        " ELSE lastname||', '||substr(firstname, 1, 1)||'.' "
-        " END "
-        " AS 'Name PIC', "
-        " CASE "
-        " WHEN variant IS NOT NULL THEN make||' '||model||'-'||variant "
-        " ELSE make||' '||model "
-        " END "
-        " AS 'Type', "
-        " registration AS 'Registration', "
-        " FlightNumber AS 'Flight #', "
-        " remarks AS 'Remarks'"
-        " FROM flights "
-        " INNER JOIN pilots on flights.pic = pilots.pilot_id "
-        " INNER JOIN tails on flights.acft = tails.tail_id "
-        " ORDER BY date DESC ");
-
-const static auto CREATE_VIEW_DEFAULT_SIM = QStringLiteral( "CREATE VIEW viewDefaultSim AS SELECT flight_id AS 'rowid',  "
-        " doft as 'Date',  "
-        " dept AS 'Dept',  "
-        " printf('%02d',(tofb/60))||':'||printf('%02d',(tofb%60)) AS 'Time',  "
-        " dest AS 'Dest', printf('%02d',(tonb/60))||':'||printf('%02d',(tonb%60)) AS 'Time ',  "
-        " printf('%02d',(tblk/60))||':'||printf('%02d',(tblk%60)) AS 'Total',  "
-        " CASE  WHEN pilot_id = 1 THEN alias  ELSE lastname||', '||substr(firstname, 1, 1)||'.'  END  AS 'Name PIC',  "
-        " CASE  WHEN variant IS NOT NULL THEN make||' '||model||'-'||variant  ELSE make||' '||model  END  AS 'Type',  "
-        " registration AS 'Registration',   "
-        " null AS 'Sim Type',"
-        " null AS 'Time of Session',"
-        " remarks AS 'Remarks'"
-        " FROM flights  "
-        " INNER JOIN pilots on flights.pic = pilots.pilot_id  INNER JOIN tails on flights.acft = tails.tail_id  "
-        " UNION"
-        " SELECT (session_id * -1),"
-        " date,"
-        " null,"
-        " null,"
-        " null,"
-        " null,"
-        " 'SIM',"
-        " null,"
-        " aircraftType,"
-        " registration,"
-        " deviceType,"
-        " printf('%02d',(totalTime/60))||':'||printf('%02d',(totalTime%60)),"
-        " remarks"
-        " FROM simulators"
-        " ORDER BY date DESC"
-            );
-
-const static auto CREATE_VIEW_EASA = QStringLiteral("CREATE VIEW viewEasa AS "
-        " SELECT "
-        " flight_id, doft as 'Date', "
-        " dept AS 'Dept', "
-        " printf('%02d',(tofb/60))||':'||printf('%02d',(tofb%60)) AS 'Time', "
-        " dest AS 'Dest', printf('%02d',(tonb/60))||':'||printf('%02d',(tonb%60)) AS 'Time ', "
-        " CASE "
-        " WHEN variant IS NOT NULL THEN make||' '||model||'-'||variant "
-        " ELSE make||' '||model "
-        " END "
-        " AS 'Type', "
-        " registration AS 'Registration', "
-        " (SELECT printf('%02d',(tSPSE/60))||':'||printf('%02d',(tSPSE%60)) WHERE tSPSE IS NOT NULL) AS 'SP SE', "
-        " (SELECT printf('%02d',(tSPME/60))||':'||printf('%02d',(tSPME%60)) WHERE tSPME IS NOT NULL) AS 'SP ME', "
-        " (SELECT printf('%02d',(tMP/60))||':'||printf('%02d',(tMP%60)) WHERE tMP IS NOT NULL) AS 'MP', "
-        " printf('%02d',(tblk/60))||':'||printf('%02d',(tblk%60)) AS 'Total', "
-        " CASE "
-        " WHEN pilot_id = 1 THEN alias "
-        " ELSE lastname||', '||substr(firstname, 1, 1)||'.' "
-        " END "
-        " AS 'Name PIC', "
-        " ldgDay AS 'L/D', "
-        " ldgNight AS 'L/N', "
-        " (SELECT printf('%02d',(tNight/60))||':'||printf('%02d',(tNight%60)) WHERE tNight IS NOT NULL)  AS 'Night', "
-        " (SELECT printf('%02d',(tIFR/60))||':'||printf('%02d',(tIFR%60)) WHERE tIFR IS NOT NULL)  AS 'IFR', "
-        " (SELECT printf('%02d',(tPIC/60))||':'||printf('%02d',(tPIC%60)) WHERE tPIC IS NOT NULL)  AS 'PIC', "
-        " (SELECT printf('%02d',(tSIC/60))||':'||printf('%02d',(tSIC%60)) WHERE tSIC IS NOT NULL)  AS 'SIC', "
-        " (SELECT printf('%02d',(tDual/60))||':'||printf('%02d',(tDual%60)) WHERE tDual IS NOT NULL)  AS 'Dual', "
-        " (SELECT printf('%02d',(tFI/60))||':'||printf('%02d',(tFI%60)) WHERE tFI IS NOT NULL)  AS 'FI', "
-        " remarks AS 'Remarks' "
-        " FROM flights "
-        " INNER JOIN pilots on flights.pic = pilots.pilot_id "
-        " INNER JOIN tails on flights.acft = tails.tail_id "
-        " ORDER BY date DESC"
-                                                    );
-
-const static auto CREATE_VIEW_EASA_SIM = QStringLiteral(" CREATE VIEW viewEasaSim AS "
-            " SELECT  flight_id, doft as 'Date',   "
-            " dept AS 'Dept',  printf('%02d',(tofb/60))||':'||printf('%02d',(tofb%60)) AS 'Time',   "
-            " dest AS 'Dest', printf('%02d',(tonb/60))||':'||printf('%02d',(tonb%60)) AS 'Time ',   "
-            " CASE  WHEN variant IS NOT NULL THEN make||' '||model||'-'||variant  ELSE make||' '||model  END  AS 'Type',   "
-            " registration AS 'Registration',   "
-            " (SELECT printf('%02d',(tSPSE/60))||':'||printf('%02d',(tSPSE%60)) WHERE tSPSE IS NOT NULL) AS 'SP SE',   "
-            " (SELECT printf('%02d',(tSPME/60))||':'||printf('%02d',(tSPME%60)) WHERE tSPME IS NOT NULL) AS 'SP ME',   "
-            " (SELECT printf('%02d',(tMP/60))||':'||printf('%02d',(tMP%60)) WHERE tMP IS NOT NULL) AS 'MP',   "
-            " printf('%02d',(tblk/60))||':'||printf('%02d',(tblk%60)) AS 'Total',   "
-            " CASE  WHEN pilot_id = 1 THEN alias  ELSE lastname||', '||substr(firstname, 1, 1)||'.'  END  AS 'Name PIC',   "
-            " ldgDay AS 'L/D',   "
-            " ldgNight AS 'L/N',   "
-            " (SELECT printf('%02d',(tNight/60))||':'||printf('%02d',(tNight%60)) WHERE tNight IS NOT NULL)  AS 'Night',   "
-            " (SELECT printf('%02d',(tIFR/60))||':'||printf('%02d',(tIFR%60)) WHERE tIFR IS NOT NULL)  AS 'IFR',   "
-            " (SELECT printf('%02d',(tPIC/60))||':'||printf('%02d',(tPIC%60)) WHERE tPIC IS NOT NULL)  AS 'PIC',   "
-            " (SELECT printf('%02d',(tSIC/60))||':'||printf('%02d',(tSIC%60)) WHERE tSIC IS NOT NULL)  AS 'SIC',   "
-            " (SELECT printf('%02d',(tDual/60))||':'||printf('%02d',(tDual%60)) WHERE tDual IS NOT NULL)  AS 'Dual',   "
-            " (SELECT printf('%02d',(tFI/60))||':'||printf('%02d',(tFI%60)) WHERE tFI IS NOT NULL)  AS 'FI', "
-            " null AS 'Sim Type', "
-            " null AS 'Time of Session', "
-            " remarks AS 'Remarks'   "
-            " FROM flights   "
-            " INNER JOIN pilots on flights.pic = pilots.pilot_id   "
-            " INNER JOIN tails on flights.acft = tails.tail_id   "
-            " UNION "
-            " SELECT (session_id * -1), "
-            " date, "
-            " null, "
-            " null, "
-            " null, "
-            " null, "
-            " aircraftType, "
-            " registration, "
-            " null, "
-            " null, "
-            " null, "
-            " 'SIM', "
-            " null, "
-            " null, "
-            " null, "
-            " null, "
-            " null, "
-            " null, "
-            " null, "
-            " null, "
-            " null, "
-            " deviceType, "
-            " printf('%02d',(totalTime/60))||':'||printf('%02d',(totalTime%60)), "
-            " remarks "
-            " FROM simulators "
-            " ORDER BY date DESC "
-            );
-
-const static auto CREATE_VIEW_SIMULATORS = QStringLiteral (" CREATE VIEW viewSimulators AS SELECT (session_id * -1), "
-                                                           " date as 'Date', "
-                                                           " registration AS 'Registration',  "
-                                                           " aircraftType AS 'Aircraft Type',  "
-                                                           " deviceType 'Sim Type', "
-                                                           " printf('%02d',(totalTime/60))||':'||printf('%02d',(totalTime%60)) AS 'Time of Session', "
-                                                           " remarks AS 'Remarks' "
-                                                           " FROM simulators "
-                                                           " ORDER BY date DESC "
-            );
-
-const static auto CREATE_VIEW_TAILS = QStringLiteral("CREATE VIEW viewTails AS "
-        " SELECT "
-        " tail_id AS 'ID', "
-        " registration AS 'Registration', "
-        " make||' '||model AS 'Type', "
-        " company AS 'Company' "
-        " FROM tails WHERE model IS NOT NULL AND variant IS NULL "
-        " UNION "
-        " SELECT "
-        " tail_id AS 'ID', "
-        " registration AS 'Registration', "
-        " make||' '||model||'-'||variant AS 'Type', "
-        " company AS 'Company' "
-        " FROM tails WHERE variant IS NOT NULL");
-
-const static auto CREATE_VIEW_PILOTS = QStringLiteral("CREATE VIEW viewPilots AS "
-        " SELECT "
-        " pilot_id AS 'ID', "
-        " lastname AS 'Last Name', "
-        " firstname AS 'First Name', "
-        " company AS 'Company' "
-        " FROM pilots");
-
-const static auto CREATE_VIEW_QCOMPLETER = QStringLiteral("CREATE VIEW viewQCompleter AS "
-        " SELECT airport_id, icao, iata, tail_id, registration, pilot_id, "
-        " lastname||', '||firstname AS 'pilot_name', alias "
-        " FROM airports "
-        " LEFT JOIN tails ON airports.airport_id = tails.tail_id "
-        " LEFT JOIN pilots ON airports.airport_id = pilots.pilot_id");
-
-const static auto CREATE_VIEW_TOTALS = QStringLiteral("CREATE VIEW viewTotals AS "
-        " SELECT "
-        " printf(\"%02d\",CAST(SUM(tblk) AS INT)/60)||\":\"||printf(\"%02d\",CAST(SUM(tblk) AS INT)%60) AS \"TOTAL\", "
-        " printf(\"%02d\",CAST(SUM(tSPSE) AS INT)/60)||\":\"||printf(\"%02d\",CAST(SUM(tSPSE) AS INT)%60) AS \"SP SE\", "
-        " printf(\"%02d\",CAST(SUM(tSPME) AS INT)/60)||\":\"||printf(\"%02d\",CAST(SUM(tSPME) AS INT)%60) AS \"SP ME\", "
-        " printf(\"%02d\",CAST(SUM(tNIGHT) AS INT)/60)||\":\"||printf(\"%02d\",CAST(SUM(tNIGHT) AS INT)%60) AS \"NIGHT\", "
-        " printf(\"%02d\",CAST(SUM(tIFR) AS INT)/60)||\":\"||printf(\"%02d\",CAST(SUM(tIFR) AS INT)%60) AS \"IFR\", "
-        " printf(\"%02d\",CAST(SUM(tPIC) AS INT)/60)||\":\"||printf(\"%02d\",CAST(SUM(tPIC) AS INT)%60) AS \"PIC\", "
-        " printf(\"%02d\",CAST(SUM(tPICUS) AS INT)/60)||\":\"||printf(\"%02d\",CAST(SUM(tPICUS) AS INT)%60) AS \"PICUS\", "
-        " printf(\"%02d\",CAST(SUM(tSIC) AS INT)/60)||\":\"||printf(\"%02d\",CAST(SUM(tSIC) AS INT)%60) AS \"SIC\", "
-        " printf(\"%02d\",CAST(SUM(tDual) AS INT)/60)||\":\"||printf(\"%02d\",CAST(SUM(tDual) AS INT)%60) AS \"DUAL\", "
-        " printf(\"%02d\",CAST(SUM(tFI) AS INT)/60)||\":\"||printf(\"%02d\",CAST(SUM(tFI) AS INT)%60) AS \"INSTRUCTOR\", "
-        " printf(\"%02d\",CAST(SUM(tSIM) AS INT)/60)||\":\"||printf(\"%02d\",CAST(SUM(tSIM) AS INT)%60) AS \"SIMULATOR\", "
-        " printf(\"%02d\",CAST(SUM(tMP) AS INT)/60)||\":\"||printf(\"%02d\",CAST(SUM(tMP) AS INT)%60) AS \"MultPilot\", "
-        " CAST(SUM(toDay) AS INT) AS \"TO Day\", CAST(SUM(toNight) AS INT) AS \"TO Night\", "
-        " CAST(SUM(ldgDay) AS INT) AS \"LDG Day\", CAST(SUM(ldgNight) AS INT) AS \"LDG Night\" "
-        " FROM flights");
-
-const static QStringList DATABASE_TABLES = {
-    CREATE_TABLE_PILOTS,
-    CREATE_TABLE_TAILS,
-    CREATE_TABLE_FLIGHTS,
-    CREATE_TABLE_AIRCRAFT,
-    CREATE_TABLE_AIRPORTS,
-    CREATE_TABLE_CURRENCIES,
-    CREATE_TABLE_CHANGELOG,
-    CREATE_TABLE_SIMULATORS,
-};
-const static QStringList DATABASE_VIEWS = {
-    CREATE_VIEW_DEFAULT,
-    CREATE_VIEW_DEFAULT_SIM,
-    CREATE_VIEW_EASA,
-    CREATE_VIEW_EASA_SIM,
-    CREATE_VIEW_SIMULATORS,
-    CREATE_VIEW_TAILS,
-    CREATE_VIEW_PILOTS,
-    CREATE_VIEW_TOTALS,
-    CREATE_VIEW_QCOMPLETER,
-};
-
-const static QStringList USER_TABLES = {
-    QStringLiteral("flights"),
-    QStringLiteral("pilots"),
-    QStringLiteral("tails")
-};
-const static QStringList TEMPLATE_TABLES= {
-    QStringLiteral("aircraft"),
-    QStringLiteral("airports"),
-    QStringLiteral("currencies"),
-    QStringLiteral("changelog")
-};
-
-bool createDatabase()
-{
-    QSqlQuery q;
-    QVector<QSqlError> errors;
-    // Create Tables
-    for (const auto &query_string : DATABASE_TABLES) {
-        q.prepare(query_string);
-        if (!q.exec())
-            errors.append(q.lastError());
-    }
-    // Create Views
-    for (const auto &query_string : DATABASE_VIEWS) {
-        q.prepare(query_string);
-        if (!q.exec())
-            errors.append(q.lastError());
-    }
-
-    aDB->updateLayout();
-
-    if (errors.isEmpty()) {
-        LOG << "Database succesfully created.";
-        return true;
-    } else {
-        LOG << "Database creation has failed. The following error(s) have ocurred: ";
-        for (const auto &error : qAsConst(errors)) {
-            LOG << error.type() << error.text();
-        }
-        return false;
-    }
-}
-
-bool commitData(const QJsonArray &json_arr, const QString &table_name)
-{
-    //aDB->updateLayout(); // needed?
-    QSqlQuery q;
-
-    // create insert statement
-    QString statement = QLatin1String("INSERT INTO ") + table_name + QLatin1String(" (");
-    QString placeholder = QStringLiteral(") VALUES (");
-    for (const auto &column_name : aDB->getTableColumns(table_name)) {
-        statement += column_name + ',';
-        placeholder.append(QLatin1Char(':') + column_name + QLatin1Char(','));
-    }
-
-    statement.chop(1);
-    placeholder.chop(1);
-    placeholder.append(')');
-    statement.append(placeholder);
-
-    q.prepare(QStringLiteral("BEGIN EXCLUSIVE TRANSACTION"));
-    q.exec();
-    //DEB << statement;
-    for (const auto &entry : json_arr) {
-        q.prepare(statement);
-
-        auto object = entry.toObject();
-        const auto keys = object.keys();
-        for (const auto &key : keys){
-            object.value(key).isNull() ? q.bindValue(key, QVariant(QVariant::String)) :
-                                         q.bindValue(QLatin1Char(':') + key, object.value(key).toVariant());
-        }
-
-        q.exec();
-    }
-
-    q.prepare(QStringLiteral("COMMIT"));
-    if (q.exec())
-        return true;
-    else
-        return false;
-}
-
-bool importTemplateData(bool use_local_ressources)
-{
-    //QSqlQuery q;
-    // reset template tables
-    const auto table_names = aDB->getTemplateTableNames();
-    for (const auto& table_name : table_names) {
-
-        //clear table
-        //q.prepare(QLatin1String("DELETE FROM ") + table_name);
-        //if (!q.exec()) {
-        //    DEB << "Error: " << q.lastError().text();
-        //    return false;
-        //}
-
-        //Prepare data
-        QJsonArray data_to_commit;
-        QString error_message("Error importing data ");
-
-        if (use_local_ressources) {
-            data_to_commit = AJson::readFileToDoc(QLatin1String(":templates/database/templates/")
-                                      + table_name + QLatin1String(".json")).array();
-            error_message.append(QLatin1String(" (ressource) "));
-        } else {
-            data_to_commit = AJson::readFileToDoc(AStandardPaths::directory(
-                                          AStandardPaths::Templates).absoluteFilePath(
-                                          table_name + QLatin1String(".json"))).array();
-            error_message.append(QLatin1String(" (downloaded) "));
-        }
-
-        // commit Data from Array
-        if (!commitData(data_to_commit, table_name)) {
-            LOG << error_message;
-            return false;
-        }
-    } // for table_name
-    return true;
-}
-
-bool resetUserData()
-{
-    QSqlQuery query;
-
-    // clear user tables
-    for (const auto& table : USER_TABLES) {
-        query.prepare("DELETE FROM " + table);
-        if (!query.exec()) {
-            DEB << "Error: " << query.lastError().text();
-        }
-    }
-    return true;
-}
-
-} // namespace aDbSetup

+ 0 - 59
src/database/adbsetup.h

@@ -1,59 +0,0 @@
-/*
- *openPilotLog - A FOSS Pilot Logbook Application
- *Copyright (C) 2020-2021 Felix Turowsky
- *
- *This program is free software: you can redistribute it and/or modify
- *it under the terms of the GNU General Public License as published by
- *the Free Software Foundation, either version 3 of the License, or
- *(at your option) any later version.
- *
- *This program is distributed in the hope that it will be useful,
- *but WITHOUT ANY WARRANTY; without even the implied warranty of
- *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *GNU General Public License for more details.
- *
- *You should have received a copy of the GNU General Public License
- *along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-#ifndef ADBSETUP_H
-#define ADBSETUP_H
-
-#include <QCoreApplication>
-
-/*!
- * \brief The aDbSetup namespace is responsible for the inital setup of the database when
- * the application is first launched. It creates the database in the specified default
- * location and creates all required tables and views.
- */
-namespace aDbSetup
-{
-
-/*!
- * \brief createDatabase runs a number of CREATE queries that create the database tables and columns.
- * \return
- */
-bool createDatabase();
-
-/*!
- * \brief commitData commits the data read from a JSON file into a table in the database.
- */
-bool commitData(const QJsonArray &json_arr, const QString &table_name);
-
-/*!
- * \brief importTemplateData fills an empty database with the template
- * data (Aircraft, Airports, currencies, changelog) as read from the JSON
- * templates.
- * \param use_local_ressources determines whether the included ressource files
- * or a previously downloaded file should be used.
- * \return
- */
-bool importTemplateData(bool use_local_ressources);
-
-/*!
- * \brief  Empties all user-generated content in the database.
- * \return true on success
- */
-bool resetUserData();
-}; // namespace aDbSetup
-
-#endif // ADBSETUP_H

+ 3 - 5
src/gui/dialogues/firstrundialog.cpp

@@ -21,7 +21,6 @@
 #include "src/functions/alog.h"
 #include "src/database/adatabase.h"
 #include "src/gui/widgets/backupwidget.h"
-#include "src/database/adbsetup.h"
 #include "src/classes/apilotentry.h"
 #include "src/classes/adownload.h"
 #include "src/classes/asettings.h"
@@ -29,6 +28,7 @@
 #include "src/classes/astyle.h"
 #include "src/functions/adatetime.h"
 #include "src/classes/ahash.h"
+#include "src/testing/atimer.h"
 #include <QErrorMessage>
 #include <QFileDialog>
 #include <QKeyEvent>
@@ -300,19 +300,17 @@ bool FirstRunDialog::setupDatabase()
         useRessourceData = true;
     }
 
-    if(!aDbSetup::createDatabase()) {
+    if(!aDB->createSchema()) {
         WARN(tr("Database creation has been unsuccessful. The following error has ocurred:<br><br>%1")
              .arg(aDB->lastError.text()));
         return false;
     }
 
-
-    if(!aDbSetup::importTemplateData(useRessourceData)) {
+    if(!aDB->importTemplateData(useRessourceData)) {
         WARN(tr("Database creation has been unsuccessful. Unable to fill template data.<br><br>%1")
              .arg(aDB->lastError.text()));
         return false;
     }
-
     return true;
 }
 

+ 24 - 10
src/gui/dialogues/newflightdialog.cpp

@@ -29,6 +29,8 @@
 #include <QCompleter>
 #include <QKeyEvent>
 
+const auto CAT_3 = QLatin1String(OPL::GLOBALS->getApproachTypes()[3].toLatin1());
+
 NewFlightDialog::NewFlightDialog(ACompletionData &completion_data,
                                        QWidget *parent)
     : QDialog(parent),
@@ -39,12 +41,12 @@ NewFlightDialog::NewFlightDialog(ACompletionData &completion_data,
     flightEntry = AFlightEntry();
     // Set up UI (New Flight)
     LOG << ASettings::read(ASettings::FlightLogging::Function);
-    if(ASettings::read(ASettings::FlightLogging::Function).toInt() == OPL::PIC){
+    if(ASettings::read(ASettings::FlightLogging::Function).toInt() == static_cast<int>(OPL::PilotFunction::PIC)){
         ui->picNameLineEdit->setText(self);
         ui->functionComboBox->setCurrentIndex(0);
         emit ui->picNameLineEdit->editingFinished();
     }
-    if (ASettings::read(ASettings::FlightLogging::Function).toInt() == OPL::SIC) {
+    if (ASettings::read(ASettings::FlightLogging::Function).toInt() == static_cast<int>(OPL::PilotFunction::SIC)) {
         ui->sicNameLineEdit->setText(self);
         ui->functionComboBox->setCurrentIndex(2);
         emit ui->sicNameLineEdit->editingFinished();
@@ -132,7 +134,6 @@ void NewFlightDialog::setupRawInputValidation()
     completer->setCompletionMode(QCompleter::PopupCompletion);
     completer->setFilterMode(Qt::MatchContains);
     ui->acftLineEdit->setCompleter(completer);
-
 }
 
 void NewFlightDialog::setupSignalsAndSlots()
@@ -459,7 +460,7 @@ RowData_T NewFlightDialog::prepareFlightEntryData()
         new_data.insert(OPL::Db::FLIGHTS_LDGNIGHT, 0);
         new_data.insert(OPL::Db::FLIGHTS_LDGDAY, ui->landingSpinBox->value());
     }
-    if (ui->approachComboBox->currentText() == OPL::GLOBALS->getApproachTypes()[3]) // ILS CAT III
+    if (ui->approachComboBox->currentText() == CAT_3) // ILS CAT III
         new_data.insert(OPL::Db::FLIGHTS_AUTOLAND, ui->landingSpinBox->value());
 
     // Additional Data
@@ -606,12 +607,21 @@ void NewFlightDialog::on_acftLineEdit_editingFinished()
 {
     const auto line_edit = ui->acftLineEdit;
     int acft_id = completionData.tailsIdMap.key(line_edit->text());
-    DEB << "acft_id: " << acft_id;
+    DEB << line_edit->text() << " has acft_id: " << acft_id;
 
     if (acft_id != 0) { // Success
         onGoodInputReceived(line_edit);
         return;
     }
+    // check for whitespaces
+    acft_id = completionData.tailsIdMap.key(line_edit->text().split(" ").first());
+    if (acft != 0) {
+        line_edit->setText(completionData.tailsIdMap.value(acft_id));
+        onGoodInputReceived(line_edit);
+        return;
+    }
+
+
 
     // try to use a completion
     if (!line_edit->completer()->currentCompletion().isEmpty() && !line_edit->text().isEmpty()) {
@@ -658,7 +668,9 @@ void NewFlightDialog::on_pilotFlyingCheckBox_stateChanged(int arg1)
 
 void NewFlightDialog::on_approachComboBox_currentTextChanged(const QString &arg1)
 {
-    if (arg1 == QLatin1String("OTHER"))
+    if (arg1 == CAT_3)
+        ui->remarksLineEdit->setText(QStringLiteral("Autoland"));
+    else if (arg1 == QLatin1String("OTHER"))
         INFO(tr("Please specify the approach type in the remarks field."));
 }
 
@@ -670,13 +682,13 @@ void NewFlightDialog::on_approachComboBox_currentTextChanged(const QString &arg1
 void NewFlightDialog::on_functionComboBox_currentIndexChanged(int index)
 {
     DEB << "Current Index: " << index;
-    if (index == OPL::PilotFunction::PIC) {
+    if (index == static_cast<int>(OPL::PilotFunction::PIC)) {
         ui->picNameLineEdit->setText(self);
         emit ui->picNameLineEdit->editingFinished();
         if (completionData.pilotsIdMap.key(ui->picNameLineEdit->text())
          == completionData.pilotsIdMap.key(ui->sicNameLineEdit->text()))
                 ui->sicNameLineEdit->setText(QString());
-    } else if (index == OPL::PilotFunction::SIC) {
+    } else if (index == static_cast<int>(OPL::PilotFunction::SIC)) {
         ui->sicNameLineEdit->setText(self);
         emit ui->sicNameLineEdit->editingFinished();
         if (completionData.pilotsIdMap.key(ui->picNameLineEdit->text())
@@ -700,13 +712,13 @@ bool NewFlightDialog::checkPilotFunctionsValid()
     int function_index = ui->functionComboBox->currentIndex();
 
     if (pic_id == 1) {
-        if (!(function_index == OPL::PilotFunction::PIC || function_index == OPL::PilotFunction::FI)) {
+        if (!(function_index == static_cast<int>(OPL::PilotFunction::PIC) || function_index == static_cast<int>(OPL::PilotFunction::FI))) {
             INFO(tr("The PIC is set to %1 but the Pilot Function is set to %2")
                     .arg(ui->picNameLineEdit->text(), ui->functionComboBox->currentText()));
             return false;
         }
     } else {
-        if (function_index == OPL::PilotFunction::PIC || function_index == OPL::PilotFunction::FI) {
+        if (function_index == static_cast<int>(OPL::PilotFunction::PIC) || function_index == static_cast<int>(OPL::PilotFunction::FI)) {
             INFO(tr("The Pilot Function is set to %1, but the PIC is set to %2")
                     .arg(ui->functionComboBox->currentText(), ui->picNameLineEdit->text()));
             return false;
@@ -727,6 +739,8 @@ void NewFlightDialog::on_buttonBox_accepted()
 {
     // Debug
     validationState.printValidationStatus();
+    for (const auto& le : *mandatoryLineEdits)
+        emit le->editingFinished();
     // If input verification is passed, continue, otherwise prompt user to correct
     if (!validationState.allValid()) {
         const auto display_names = QHash<ValidationItem, QString> {

+ 1 - 0
src/gui/dialogues/newflightdialog.h

@@ -196,6 +196,7 @@ private slots:
     void on_pilotFlyingCheckBox_stateChanged(int arg1);
     void on_approachComboBox_currentTextChanged(const QString &arg1);
     void on_functionComboBox_currentIndexChanged(int index);
+
 protected:
     bool eventFilter(QObject* object, QEvent* event) override;
 };

+ 1 - 1
src/gui/dialogues/newsimdialog.cpp

@@ -138,7 +138,7 @@ bool NewSimDialog::verifyInput(QString& error_msg)
     }
 
     // Device Type - for FSTD, aircraft info is required
-    if (ui->deviceTypeComboBox->currentIndex() == OPL::SimulatorType::FSTD
+    if (ui->deviceTypeComboBox->currentIndex() == static_cast<int>(OPL::SimulatorType::FSTD)
             && ui->aircraftTypeLineEdit->text() == QString()) {
         error_msg = tr("For FSTD, please enter the aircraft type.");
         return false;

+ 3 - 4
src/gui/widgets/debugwidget.cpp

@@ -28,7 +28,6 @@
 #include "src/functions/astat.h"
 #include "src/classes/acurrencyentry.h"
 #include "src/classes/atranslator.h"
-#include "src/database/adbsetup.h"
 #include "src/classes/ahash.h"
 #include "src/classes/ajson.h"
 #include "src/functions/adate.h"
@@ -76,7 +75,7 @@ DebugWidget::~DebugWidget()
 void DebugWidget::on_resetUserTablesPushButton_clicked()
 {
     ATimer timer(this);
-    if (aDbSetup::resetUserData()){
+    if (aDB->resetUserData()){
         LOG << "Database successfully reset";
         emit aDB->dataBaseUpdated();
     } else
@@ -140,14 +139,14 @@ void DebugWidget::on_resetDatabasePushButton_clicked()
             LOG << "ssl/network error";
     }
     // Create Database
-    if (!aDbSetup::createDatabase()) {
+    if (!aDB->createSchema()) {
         WARN(QString("Unable to create database.<br>%1").arg(aDB->lastError.text()));
         return;
     }
 
     // Load ressources
     bool use_ressource_data = false; // do not use local data, download from github
-    if(!aDbSetup::importTemplateData(use_ressource_data)) {
+    if(!aDB->importTemplateData(use_ressource_data)) {
         WARN(tr("Database creation has been unsuccessful. Unable to fill template data.<br><br>%1")
              .arg(aDB->lastError.text()));
         return ;

+ 6 - 6
src/opl.cpp

@@ -12,22 +12,22 @@ void OplGlobals::fillLanguageComboBox(QComboBox *combo_box) const
 void OplGlobals::fillViewNamesComboBox(QComboBox *combo_box) const
 {
     const QSignalBlocker blocker(combo_box);
-    for (int i = DbViewName::Default; i < DATABASE_VIEW_DISPLAY_NAMES.size(); i++)
-        combo_box->addItem(DATABASE_VIEW_DISPLAY_NAMES.value(DbViewName(i)));
+    for (const auto &view_name : DATABASE_VIEW_DISPLAY_NAMES)
+        combo_box->addItem(view_name);
 }
 
 void OplGlobals::loadPilotFunctios(QComboBox *combo_box) const
 {
     const QSignalBlocker blocker(combo_box);
-    for (int i = PilotFunction::PIC; i < PILOT_FUNCTIONS.size(); i++)
-        combo_box->addItem(PILOT_FUNCTIONS.value(PilotFunction(i)));
+    for (const auto& pilot_function : PILOT_FUNCTIONS)
+        combo_box->addItem(pilot_function);
 }
 
 void OplGlobals::loadSimulatorTypes(QComboBox *combo_box) const
 {
     const QSignalBlocker blocker(combo_box);
-    for (int i = SimulatorType::FNPTI; i < SIMULATOR_TYPES.size(); i++)
-        combo_box->addItem(SIMULATOR_TYPES.value(SimulatorType(i)));
+    for (const auto &sim_type : SIMULATOR_TYPES)
+        combo_box->addItem(sim_type);
 }
 
 void OplGlobals::loadApproachTypes(QComboBox *combo_box) const

+ 137 - 132
src/opl.h

@@ -69,17 +69,17 @@ public:
     static inline void info(const QString msg, QWidget *parent = nullptr){
 
         qInfo() << msg;
-        auto mb = QMessageBox(QMessageBox::Information, QLatin1String("Info"), msg, QMessageBox::StandardButton::Ok, parent);
+        auto mb = QMessageBox(QMessageBox::Information, QStringLiteral("Info"), msg, QMessageBox::StandardButton::Ok, parent);
         mb.exec();
     };
     static inline void warn(const QString msg, QWidget *parent = nullptr){
         qWarning() << msg;
-        auto mb = QMessageBox(QMessageBox::Warning, QLatin1String("Warning"), msg, QMessageBox::StandardButton::Ok, parent);
+        auto mb = QMessageBox(QMessageBox::Warning, QStringLiteral("Warning"), msg, QMessageBox::StandardButton::Ok, parent);
         mb.exec();
     };
     static inline void crit(const QString msg, QWidget *parent = nullptr){
         qCritical() << msg;
-        auto mb = QMessageBox(QMessageBox::Critical, QLatin1String("Warning"), msg, QMessageBox::StandardButton::Ok, parent);
+        auto mb = QMessageBox(QMessageBox::Critical, QStringLiteral("Warning"), msg, QMessageBox::StandardButton::Ok, parent);
         mb.exec();
     };
 }; // class ANotificationHandler
@@ -98,22 +98,22 @@ enum class DateTimeFormat {Default, Backup};
  * \brief PilotFunction
  * Pilot in Command, Pilot in Command under Supervision, Second in Command (Co-Pilot), Dual, Flight Instructor
  */
-enum PilotFunction {PIC = 0, PICUS = 1, SIC = 2, DUAL = 3, FI = 4};
+enum class PilotFunction {PIC = 0, PICUS = 1, SIC = 2, DUAL = 3, FI = 4};
 
 /*!
  * \brief Enumerates the available translations
  */
-enum Translation {English, German, Spanish};
+enum class Translation {English, German, Spanish};
 
 /*!
  * \brief Enumerates the available SQL views in the database
  */
-enum DbViewName {Default, DefaultWithSim, Easa, EasaWithSim, SimulatorOnly};
+enum class DbViewName {Default, DefaultWithSim, Easa, EasaWithSim, SimulatorOnly};
 
 /*!
  * \brief Enumerates the Simulator Types: Flight and Navigation Procedures Trainer 1/2, Flight Simulation Training Device
  */
-enum SimulatorType {FNPTI = 0, FNPTII = 1, FSTD = 2};
+enum class SimulatorType {FNPTI = 0, FNPTII = 1, FSTD = 2};
 
 /*!
  * \brief The OplGlobals class encapsulates non-POD globals to avoid making them static. It is available
@@ -137,43 +137,43 @@ public:
 private:
     Q_OBJECT
 
-    const QMap<Translation, QString> L10N_FilePaths {
+    const static inline QMap<Translation, QString> L10N_FilePaths {
         {Translation::English, QStringLiteral("l10n/openpilotlog_en")},
         {Translation::German,  QStringLiteral("l10n/openpilotlog_de")},
         {Translation::Spanish, QStringLiteral("l10n/openpilotlog_es")},
     };
-    const QMap<Translation, QString> L10N_DisplayNames {
-        {Translation::English, tr("English")},
-        {Translation::German,  tr("Deutsch")},
-        {Translation::Spanish, tr("Español")},
+    const static inline QMap<Translation, QString> L10N_DisplayNames {
+        {Translation::English, QStringLiteral("English")},
+        {Translation::German,  QStringLiteral("Deutsch")},
+        {Translation::Spanish, QStringLiteral("Español")},
     };
-    const QMap<DbViewName, QString> DATABASE_VIEWS = {
-        {Default,        QStringLiteral("viewDefault")},
-        {DefaultWithSim, QStringLiteral("viewDefaultSim")},
-        {Easa,           QStringLiteral("viewEasa")},
-        {EasaWithSim,    QStringLiteral("viewEasaSim")},
-        {SimulatorOnly,  QStringLiteral("viewSimulators")},
+    const static inline QMap<DbViewName, QString> DATABASE_VIEWS = {
+        {DbViewName::Default,        QStringLiteral("viewDefault")},
+        {DbViewName::DefaultWithSim, QStringLiteral("viewDefaultSim")},
+        {DbViewName::Easa,           QStringLiteral("viewEasa")},
+        {DbViewName::EasaWithSim,    QStringLiteral("viewEasaSim")},
+        {DbViewName::SimulatorOnly,  QStringLiteral("viewSimulators")},
     };
     const QMap<DbViewName, QString> DATABASE_VIEW_DISPLAY_NAMES = {
-        {Default,        tr("Default")},
-        {DefaultWithSim, tr("Default with Simulator")},
-        {Easa,           tr("EASA-FCL")},
-        {EasaWithSim,    tr("EASA-FCL with Simulator")},
-        {SimulatorOnly,  tr("Simulator Sessions Only")},
+        {DbViewName::Default,        tr("Default")},
+        {DbViewName::DefaultWithSim, tr("Default with Simulator")},
+        {DbViewName::Easa,           tr("EASA-FCL")},
+        {DbViewName::EasaWithSim,    tr("EASA-FCL with Simulator")},
+        {DbViewName::SimulatorOnly,  tr("Simulator Sessions Only")},
     };
-    const QMap<PilotFunction, QLatin1String> PILOT_FUNCTIONS = {
-        {PilotFunction::PIC,   QLatin1String("PIC")},
-        {PilotFunction::PICUS, QLatin1String("PICUS")},
-        {PilotFunction::SIC,   QLatin1String("SIC")},
-        {PilotFunction::DUAL,  QLatin1String("DUAL")},
-        {PilotFunction::FI,    QLatin1String("FI")},
+    const static inline QMap<PilotFunction, QString> PILOT_FUNCTIONS = {
+        {PilotFunction::PIC,   QStringLiteral("PIC")},
+        {PilotFunction::PICUS, QStringLiteral("PICUS")},
+        {PilotFunction::SIC,   QStringLiteral("SIC")},
+        {PilotFunction::DUAL,  QStringLiteral("DUAL")},
+        {PilotFunction::FI,    QStringLiteral("FI")},
     };
-    const QMap<SimulatorType, QString> SIMULATOR_TYPES = {
-        {FNPTI,  QStringLiteral("FNPT I")},
-        {FNPTII, QStringLiteral("FNPT II")},
-        {FSTD,   QStringLiteral("FSTD")},
+    const static inline QMap<SimulatorType, QString> SIMULATOR_TYPES = {
+        {SimulatorType::FNPTI,  QStringLiteral("FNPT I")},
+        {SimulatorType::FNPTII, QStringLiteral("FNPT II")},
+        {SimulatorType::FSTD,   QStringLiteral("FSTD")},
     };
-    const QStringList APPROACH_TYPES = {
+    const static inline QStringList APPROACH_TYPES = {
             QStringLiteral("VISUAL"),
             QStringLiteral("ILS CAT I"),
             QStringLiteral("ILS CAT II"),
@@ -202,141 +202,146 @@ private:
 Q_GLOBAL_STATIC(OplGlobals, GLOBALS)
 
 /*!
- *  The OPL::db namespace provides string literals to programatically access the database
+ *  The OPL::Db namespace provides string literals to programatically access the database
  *
  *  Example usage, do:
- *  newData.insert(OPL::db::FLIGHTS_DEP, ui->deptLocLineEdit->text());
- *  newData.value(OPL::db::AIRCRAFT_MULTIPILOT);
+ *  newData.insert(OPL::Db::FLIGHTS_DEP, ui->deptLocLineEdit->text());
+ *  newData.value(OPL::Db::AIRCRAFT_MULTIPILOT);
  *
  *  instead of:
  *  newData.insert("dept", ui->deptLocLineEdit->text());
  *  newData.value("multipilot");
  *
  *  Declaring these literals here avoids memory allocation at runtime for construction of temporary
- *  qstrings like ("dept"). See https://doc.qt.io/qt-5/qstring.html#QStringLiteral and ensures
- *  uniform use throughout the application.
+ *  qstrings like ("dept").
  */
 namespace Db {
 
 
 // Table names
-static const auto TABLE_FLIGHTS          = QStringLiteral("flights");
-static const auto TABLE_PILOTS           = QStringLiteral("pilots");
-static const auto TABLE_TAILS            = QStringLiteral("tails");
-static const auto TABLE_AIRCRAFT         = QStringLiteral("aircraft");
-static const auto TABLE_AIRPORTS         = QStringLiteral("airports");
-static const auto TABLE_CURRENCIES       = QStringLiteral("currencies");
-static const auto TABLE_SIMULATORS       = QStringLiteral("simulators");
+const inline auto TABLE_FLIGHTS          = QStringLiteral("flights");
+const inline auto TABLE_PILOTS           = QStringLiteral("pilots");
+const inline auto TABLE_TAILS            = QStringLiteral("tails");
+const inline auto TABLE_AIRCRAFT         = QStringLiteral("aircraft");
+const inline auto TABLE_AIRPORTS         = QStringLiteral("airports");
+const inline auto TABLE_CURRENCIES       = QStringLiteral("currencies");
+const inline auto TABLE_SIMULATORS       = QStringLiteral("simulators");
 
 // Flights table columns
-static const auto FLIGHTS_ROWID          = QStringLiteral("flight_id");
-static const auto FLIGHTS_DOFT           = QStringLiteral("doft");
-static const auto FLIGHTS_DEPT           = QStringLiteral("dept");
-static const auto FLIGHTS_DEST           = QStringLiteral("dest");
-static const auto FLIGHTS_TOFB           = QStringLiteral("tofb");
-static const auto FLIGHTS_TONB           = QStringLiteral("tonb");
-static const auto FLIGHTS_PIC            = QStringLiteral("pic");
-static const auto FLIGHTS_ACFT           = QStringLiteral("acft");
-static const auto FLIGHTS_TBLK           = QStringLiteral("tblk");
-static const auto FLIGHTS_TSPSE          = QStringLiteral("tSPSE");
-static const auto FLIGHTS_TSPME          = QStringLiteral("tSPME");
-static const auto FLIGHTS_TMP            = QStringLiteral("tMP");
-static const auto FLIGHTS_TNIGHT         = QStringLiteral("tNIGHT");
-static const auto FLIGHTS_TIFR           = QStringLiteral("tIFR");
-static const auto FLIGHTS_TPIC           = QStringLiteral("tPIC");
-static const auto FLIGHTS_TPICUS         = QStringLiteral("tPICUS");
-static const auto FLIGHTS_TSIC           = QStringLiteral("tSIC");
-static const auto FLIGHTS_TDUAL          = QStringLiteral("tDUAL");
-static const auto FLIGHTS_TFI            = QStringLiteral("tFI");
-static const auto FLIGHTS_TSIM           = QStringLiteral("tSIM");
-static const auto FLIGHTS_PILOTFLYING    = QStringLiteral("pilotFlying");
-static const auto FLIGHTS_TODAY          = QStringLiteral("toDay");
-static const auto FLIGHTS_TONIGHT        = QStringLiteral("toNight");
-static const auto FLIGHTS_LDGDAY         = QStringLiteral("ldgDay");
-static const auto FLIGHTS_LDGNIGHT       = QStringLiteral("ldgNight");
-static const auto FLIGHTS_AUTOLAND       = QStringLiteral("autoland");
-static const auto FLIGHTS_SECONDPILOT    = QStringLiteral("secondPilot");
-static const auto FLIGHTS_THIRDPILOT     = QStringLiteral("thirdPilot");
-static const auto FLIGHTS_APPROACHTYPE   = QStringLiteral("approachType");
-static const auto FLIGHTS_FLIGHTNUMBER   = QStringLiteral("flightNumber");
-static const auto FLIGHTS_REMARKS        = QStringLiteral("remarks");
+const inline auto FLIGHTS_ROWID          = QStringLiteral("flight_id");
+const inline auto FLIGHTS_DOFT           = QStringLiteral("doft");
+const inline auto FLIGHTS_DEPT           = QStringLiteral("dept");
+const inline auto FLIGHTS_DEST           = QStringLiteral("dest");
+const inline auto FLIGHTS_TOFB           = QStringLiteral("tofb");
+const inline auto FLIGHTS_TONB           = QStringLiteral("tonb");
+const inline auto FLIGHTS_PIC            = QStringLiteral("pic");
+const inline auto FLIGHTS_ACFT           = QStringLiteral("acft");
+const inline auto FLIGHTS_TBLK           = QStringLiteral("tblk");
+const inline auto FLIGHTS_TSPSE          = QStringLiteral("tSPSE");
+const inline auto FLIGHTS_TSPME          = QStringLiteral("tSPME");
+const inline auto FLIGHTS_TMP            = QStringLiteral("tMP");
+const inline auto FLIGHTS_TNIGHT         = QStringLiteral("tNIGHT");
+const inline auto FLIGHTS_TIFR           = QStringLiteral("tIFR");
+const inline auto FLIGHTS_TPIC           = QStringLiteral("tPIC");
+const inline auto FLIGHTS_TPICUS         = QStringLiteral("tPICUS");
+const inline auto FLIGHTS_TSIC           = QStringLiteral("tSIC");
+const inline auto FLIGHTS_TDUAL          = QStringLiteral("tDUAL");
+const inline auto FLIGHTS_TFI            = QStringLiteral("tFI");
+const inline auto FLIGHTS_TSIM           = QStringLiteral("tSIM");
+const inline auto FLIGHTS_PILOTFLYING    = QStringLiteral("pilotFlying");
+const inline auto FLIGHTS_TODAY          = QStringLiteral("toDay");
+const inline auto FLIGHTS_TONIGHT        = QStringLiteral("toNight");
+const inline auto FLIGHTS_LDGDAY         = QStringLiteral("ldgDay");
+const inline auto FLIGHTS_LDGNIGHT       = QStringLiteral("ldgNight");
+const inline auto FLIGHTS_AUTOLAND       = QStringLiteral("autoland");
+const inline auto FLIGHTS_SECONDPILOT    = QStringLiteral("secondPilot");
+const inline auto FLIGHTS_THIRDPILOT     = QStringLiteral("thirdPilot");
+const inline auto FLIGHTS_APPROACHTYPE   = QStringLiteral("approachType");
+const inline auto FLIGHTS_FLIGHTNUMBER   = QStringLiteral("flightNumber");
+const inline auto FLIGHTS_REMARKS        = QStringLiteral("remarks");
 
 // tails table
 
-static const auto TAILS_ROWID            = QStringLiteral("tail_id");
-static const auto TAILS_REGISTRATION     = QStringLiteral("registration");
-static const auto TAILS_COMPANY          = QStringLiteral("company");
-static const auto TAILS_MAKE             = QStringLiteral("make");
-static const auto TAILS_MODEL            = QStringLiteral("model");
-static const auto TAILS_VARIANT          = QStringLiteral("variant");
-static const auto TAILS_MULTIPILOT       = QStringLiteral("multipilot");
-static const auto TAILS_MULTIENGINE      = QStringLiteral("multiengine");
-static const auto TAILS_ENGINETYPE       = QStringLiteral("engineType");
-static const auto TAILS_WEIGHTCLASS      = QStringLiteral("weightClass");
+const inline auto TAILS_ROWID            = QStringLiteral("tail_id");
+const inline auto TAILS_REGISTRATION     = QStringLiteral("registration");
+const inline auto TAILS_COMPANY          = QStringLiteral("company");
+const inline auto TAILS_MAKE             = QStringLiteral("make");
+const inline auto TAILS_MODEL            = QStringLiteral("model");
+const inline auto TAILS_VARIANT          = QStringLiteral("variant");
+const inline auto TAILS_MULTIPILOT       = QStringLiteral("multipilot");
+const inline auto TAILS_MULTIENGINE      = QStringLiteral("multiengine");
+const inline auto TAILS_ENGINETYPE       = QStringLiteral("engineType");
+const inline auto TAILS_WEIGHTCLASS      = QStringLiteral("weightClass");
 
 // pilots table
 
-static const auto PILOTS_ROWID           = QStringLiteral("pilot_id");
-static const auto PILOTS_LASTNAME        = QStringLiteral("lastname");
-static const auto PILOTS_FIRSTNAME       = QStringLiteral("firstname");
-static const auto PILOTS_ALIAS           = QStringLiteral("alias");
-static const auto PILOTS_COMPANY         = QStringLiteral("company");
-static const auto PILOTS_EMPLOYEEID      = QStringLiteral("employeeid");
-static const auto PILOTS_PHONE           = QStringLiteral("phone");
-static const auto PILOTS_EMAIL           = QStringLiteral("email");
+const inline auto  PILOTS_ROWID           = QStringLiteral("pilot_id");
+const inline auto  PILOTS_LASTNAME        = QStringLiteral("lastname");
+const inline auto  PILOTS_FIRSTNAME       = QStringLiteral("firstname");
+const inline auto  PILOTS_ALIAS           = QStringLiteral("alias");
+const inline auto  PILOTS_COMPANY         = QStringLiteral("company");
+const inline auto  PILOTS_EMPLOYEEID      = QStringLiteral("employeeid");
+const inline auto  PILOTS_PHONE           = QStringLiteral("phone");
+const inline auto  PILOTS_EMAIL           = QStringLiteral("email");
 
 // Currencies table
-static const auto CURRENCIES_EXPIRYDATE  = QStringLiteral("expiryDate");
-static const auto CURRENCIES_DESCRIPTION = QStringLiteral("description");
+const inline auto  CURRENCIES_EXPIRYDATE  = QStringLiteral("expiryDate");
+const inline auto  CURRENCIES_DESCRIPTION = QStringLiteral("description");
 
 // Simulators table
-static const auto SIMULATORS_ROWID       = QStringLiteral("session_id");
-static const auto SIMULATORS_DATE        = QStringLiteral("date");
-static const auto SIMULATORS_TIME        = QStringLiteral("totalTime");
-static const auto SIMULATORS_TYPE        = QStringLiteral("deviceType");
-static const auto SIMULATORS_ACFT        = QStringLiteral("aircraftType");
-static const auto SIMULATORS_REG         = QStringLiteral("registration");
-static const auto SIMULATORS_REMARKS     = QStringLiteral("remarks");
+const inline auto  SIMULATORS_ROWID       = QStringLiteral("session_id");
+const inline auto  SIMULATORS_DATE        = QStringLiteral("date");
+const inline auto  SIMULATORS_TIME        = QStringLiteral("totalTime");
+const inline auto  SIMULATORS_TYPE        = QStringLiteral("deviceType");
+const inline auto  SIMULATORS_ACFT        = QStringLiteral("aircraftType");
+const inline auto  SIMULATORS_REG         = QStringLiteral("registration");
+const inline auto  SIMULATORS_REMARKS     = QStringLiteral("remarks");
 
 // all tables
-static const auto ROWID                  = QStringLiteral("rowid");
-static const auto NULL_TIME_hhmm         = QStringLiteral("00:00");
+const inline auto  ROWID                  = QStringLiteral("rowid");
+const inline auto  NULL_TIME_hhmm         = QStringLiteral("00:00");
 
 } // namespace OPL::db
 
 namespace Assets {
 
-static const auto LOGO                          = QStringLiteral(":/icons/opl-icons/logos/logo_text.png");
-static const auto ICON_MAIN                     = QStringLiteral(":/icons/opl-icons/app/icon_main.png");
-static const auto ICON_APPICON_LINUX            = QStringLiteral(":/icons/opl-icons/app/icon_linux.svg");
-static const auto ICON_APPICON_IOS              = QStringLiteral(":/icons/opl-icons/app/icon_ios.svg");
-static const auto ICON_APPICON_WIN              = QStringLiteral(":/icons/opl-icons/app/icon_windows.ico");
-
-static const auto ICON_TOOLBAR_HOME             = QStringLiteral(":/icons/opl-icons/toolbar/thick/light/icon_home.svg");
-static const auto ICON_TOOLBAR_NEW_FLIGHT       = QStringLiteral(":/icons/opl-icons/toolbar/thick/light/icon_new_flight.svg");
-static const auto ICON_TOOLBAR_LOGBOOK          = QStringLiteral(":/icons/opl-icons/toolbar/thick/light/icon_logbook.svg");
-static const auto ICON_TOOLBAR_AIRCRAFT         = QStringLiteral(":/icons/opl-icons/toolbar/thick/light/icon_airplane.svg");
-static const auto ICON_TOOLBAR_PILOT            = QStringLiteral(":/icons/opl-icons/toolbar/thick/light/icon_pilot.svg");
-static const auto ICON_TOOLBAR_SETTINGS         = QStringLiteral(":/icons/opl-icons/toolbar/thick/light/icon_settings.svg");
-static const auto ICON_TOOLBAR_QUIT             = QStringLiteral(":/icons/opl-icons/toolbar/thick/light/icon_exit.svg");
-
-static const auto ICON_TOOLBAR_BACKUP           = QStringLiteral(":/icons/opl-icons/toolbar/thick/light/icon_backup.svg");
-
-static const auto ICON_TOOLBAR_HOME_DARK        = QStringLiteral(":/icons/opl-icons/toolbar/thick/dark/icon_home_dm.svg");
-static const auto ICON_TOOLBAR_NEW_FLIGHT_DARK  = QStringLiteral(":/icons/opl-icons/toolbar/thick/dark/icon_new_flight_dm.svg");
-static const auto ICON_TOOLBAR_LOGBOOK_DARK     = QStringLiteral(":/icons/opl-icons/toolbar/thick/dark/icon_logbook_dm.svg");
-static const auto ICON_TOOLBAR_AIRCRAFT_DARK    = QStringLiteral(":/icons/opl-icons/toolbar/thick/dark/icon_airplane_dm.svg");
-static const auto ICON_TOOLBAR_PILOT_DARK       = QStringLiteral(":/icons/opl-icons/toolbar/thick/dark/icon_pilot_dm.svg");
-static const auto ICON_TOOLBAR_SETTINGS_DARK    = QStringLiteral(":/icons/opl-icons/toolbar/thick/dark/icon_settings_dm.svg");
-static const auto ICON_TOOLBAR_QUIT_DARK        = QStringLiteral(":/icons/opl-icons/toolbar/thick/dark/icon_exit_dm.svg");
-
-static const auto ICON_TOOLBAR_BACKUP_DARK      = QStringLiteral(":/icons/opl-icons/toolbar/thick/dark/icon_backup_dm.svg");
+const inline auto  DATABASE_SCHEMA               = QStringLiteral(":/database/database_schema.sql");
+const inline auto  DATABASE_TEMPLATE_AIRCRAFT    = QStringLiteral(":/database/templates/aircraft.json");
+const inline auto  DATABASE_TEMPLATE_AIRPORT     = QStringLiteral(":/database/templates/airports.json");
+const inline auto  DATABASE_TEMPLATE_CHANGELOG   = QStringLiteral(":/database/templates/changelog.json");
+const inline auto  DATABASE_TEMPLATE_CURRENCIES  = QStringLiteral(":/database/templates/currencies.json");
+
+const inline auto  LOGO                          = QStringLiteral(":/icons/opl-icons/logos/logo_text.png");
+const inline auto  ICON_MAIN                     = QStringLiteral(":/icons/opl-icons/app/icon_main.png");
+const inline auto  ICON_APPICON_LINUX            = QStringLiteral(":/icons/opl-icons/app/icon_linux.svg");
+const inline auto  ICON_APPICON_IOS              = QStringLiteral(":/icons/opl-icons/app/icon_ios.svg");
+const inline auto  ICON_APPICON_WIN              = QStringLiteral(":/icons/opl-icons/app/icon_windows.ico");
+
+const inline auto  ICON_TOOLBAR_HOME             = QStringLiteral(":/icons/opl-icons/toolbar/thick/light/icon_home.svg");
+const inline auto  ICON_TOOLBAR_NEW_FLIGHT       = QStringLiteral(":/icons/opl-icons/toolbar/thick/light/icon_new_flight.svg");
+const inline auto  ICON_TOOLBAR_LOGBOOK          = QStringLiteral(":/icons/opl-icons/toolbar/thick/light/icon_logbook.svg");
+const inline auto  ICON_TOOLBAR_AIRCRAFT         = QStringLiteral(":/icons/opl-icons/toolbar/thick/light/icon_airplane.svg");
+const inline auto  ICON_TOOLBAR_PILOT            = QStringLiteral(":/icons/opl-icons/toolbar/thick/light/icon_pilot.svg");
+const inline auto  ICON_TOOLBAR_SETTINGS         = QStringLiteral(":/icons/opl-icons/toolbar/thick/light/icon_settings.svg");
+const inline auto  ICON_TOOLBAR_QUIT             = QStringLiteral(":/icons/opl-icons/toolbar/thick/light/icon_exit.svg");
+
+const inline auto  ICON_TOOLBAR_BACKUP           = QStringLiteral(":/icons/opl-icons/toolbar/thick/light/icon_backup.svg");
+
+const inline auto  ICON_TOOLBAR_HOME_DARK        = QStringLiteral(":/icons/opl-icons/toolbar/thick/dark/icon_home_dm.svg");
+const inline auto  ICON_TOOLBAR_NEW_FLIGHT_DARK  = QStringLiteral(":/icons/opl-icons/toolbar/thick/dark/icon_new_flight_dm.svg");
+const inline auto  ICON_TOOLBAR_LOGBOOK_DARK     = QStringLiteral(":/icons/opl-icons/toolbar/thick/dark/icon_logbook_dm.svg");
+const inline auto  ICON_TOOLBAR_AIRCRAFT_DARK    = QStringLiteral(":/icons/opl-icons/toolbar/thick/dark/icon_airplane_dm.svg");
+const inline auto  ICON_TOOLBAR_PILOT_DARK       = QStringLiteral(":/icons/opl-icons/toolbar/thick/dark/icon_pilot_dm.svg");
+const inline auto  ICON_TOOLBAR_SETTINGS_DARK    = QStringLiteral(":/icons/opl-icons/toolbar/thick/dark/icon_settings_dm.svg");
+const inline auto  ICON_TOOLBAR_QUIT_DARK        = QStringLiteral(":/icons/opl-icons/toolbar/thick/dark/icon_exit_dm.svg");
+
+const inline auto  ICON_TOOLBAR_BACKUP_DARK      = QStringLiteral(":/icons/opl-icons/toolbar/thick/dark/icon_backup_dm.svg");
 
 }
 
 namespace Styles {
 
-static const auto RED_BORDER = QStringLiteral("border: 1px solid red");
+const inline auto  RED_BORDER = QStringLiteral("border: 1px solid red");
 } // namespace Styles
 
 } // namespace opl