Browse Source

added DbSetup class and debugwidget

fiffty-50 4 years ago
parent
commit
3e6c5d4f1e

+ 12 - 2
mainwindow.cpp

@@ -38,6 +38,7 @@ MainWindow::MainWindow(QWidget *parent)
     ui->actionLogbook->setIcon(QIcon(":/icons/ionicon-icons/book-outline.png"));
     ui->actionAircraft->setIcon(QIcon(":/icons/ionicon-icons/airplane-outline.png"));
     ui->actionPilots->setIcon(QIcon(":/icons/ionicon-icons/settings-outline.png"));
+    ui->actionDebug->setIcon(QIcon(":/icons/ionicon-icons/settings-outline.png"));
     ui->actionSettings->setIcon(QIcon(":/icons/ionicon-icons/settings-outline.png"));
     ui->actionQuit->setIcon(QIcon(":/icons/ionicon-icons/power-outline.png"));
 
@@ -45,7 +46,7 @@ MainWindow::MainWindow(QWidget *parent)
     auto *spacer = new QWidget();
     spacer->setMinimumWidth(10);
     spacer->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
-    ui->toolBar->insertWidget(ui->actionSettings, spacer);
+    ui->toolBar->insertWidget(ui->actionDebug, spacer);
 
 
     DEB("Construction MainWindow Widgets\n");
@@ -62,7 +63,11 @@ MainWindow::MainWindow(QWidget *parent)
     ui->stackedWidget->addWidget(aircraftWidget);
 
     // Startup Screen (Home Screen)
-    ui->stackedWidget->setCurrentWidget(homeWidget);
+    // ui->stackedWidget->setCurrentWidget(homeWidget);
+    // Debup Widget for now
+    debugWidget = new DebugWidget(this);
+    ui->stackedWidget->addWidget(debugWidget);
+    ui->stackedWidget->setCurrentWidget(debugWidget);
 
 }
 
@@ -98,6 +103,11 @@ void MainWindow::on_actionLogbook_triggered()
     ui->stackedWidget->setCurrentWidget(logbookWidget);
 }
 
+void MainWindow::on_actionDebug_triggered()
+{
+    ui->stackedWidget->setCurrentWidget(debugWidget);
+}
+
 void MainWindow::on_actionSettings_triggered()
 {
     ui->stackedWidget->setCurrentWidget(settingsWidget);

+ 5 - 0
mainwindow.h

@@ -32,6 +32,7 @@
 #include "src/gui/widgets/logbookwidget.h"
 #include "src/gui/widgets/aircraftwidget.h"
 #include "src/gui/widgets/pilotswidget.h"
+#include "src/gui/widgets/debugwidget.h"
 #include "src/gui/dialogues/newtaildialog.h"
 #include "src/gui/dialogues/newpilotdialog.h"
 #include "src/classes/runguard.h"
@@ -72,6 +73,8 @@ private slots:
 
     void on_actionNewPilot_triggered();
 
+    void on_actionDebug_triggered();
+
 private:
     Ui::MainWindow *ui;
 
@@ -85,6 +88,8 @@ private:
 
     PilotsWidget* pilotsWidget;
 
+    DebugWidget* debugWidget;
+
 
 
 

+ 9 - 0
mainwindow.ui

@@ -39,6 +39,7 @@
    <addaction name="actionLogbook"/>
    <addaction name="actionAircraft"/>
    <addaction name="actionPilots"/>
+   <addaction name="actionDebug"/>
    <addaction name="actionSettings"/>
    <addaction name="actionQuit"/>
   </widget>
@@ -120,6 +121,14 @@
     <string>Add a new Pilot to the database</string>
    </property>
   </action>
+  <action name="actionDebug">
+   <property name="text">
+    <string>Debug</string>
+   </property>
+   <property name="shortcut">
+    <string>Ctrl+D</string>
+   </property>
+  </action>
  </widget>
  <resources/>
  <connections/>

+ 10 - 1
openPilotLog.pro

@@ -1,4 +1,4 @@
-QT       += core gui sql
+QT       += core gui sql network
 
 greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
 
@@ -21,6 +21,8 @@ SOURCES += \
     src/classes/aircraft.cpp \
     src/classes/calc.cpp \
     src/classes/completionlist.cpp \
+    src/classes/csv.cpp \
+    src/classes/download.cpp \
     src/classes/flight.cpp \
     src/classes/pilot.cpp \
     src/classes/runguard.cpp \
@@ -29,12 +31,14 @@ SOURCES += \
     src/classes/strictrxvalidator.cpp \
     src/database/db.cpp \
     src/database/dbinfo.cpp \
+    src/database/dbsetup.cpp \
     src/database/entry.cpp \
     src/gui/dialogues/firstrundialog.cpp \
     src/gui/dialogues/newflightdialog.cpp \
     src/gui/dialogues/newpilotdialog.cpp \
     src/gui/dialogues/newtaildialog.cpp \
     src/gui/widgets/aircraftwidget.cpp \
+    src/gui/widgets/debugwidget.cpp \
     src/gui/widgets/homewidget.cpp \
     src/gui/widgets/logbookwidget.cpp \
     src/gui/widgets/pilotswidget.cpp \
@@ -47,6 +51,8 @@ HEADERS += \
     src/classes/aircraft.h \
     src/classes/calc.h \
     src/classes/completionlist.h \
+    src/classes/csv.h \
+    src/classes/download.h \
     src/classes/flight.h \
     src/classes/pilot.h \
     src/classes/runguard.h \
@@ -55,12 +61,14 @@ HEADERS += \
     src/classes/strictrxvalidator.h \
     src/database/db.h \
     src/database/dbinfo.h \
+    src/database/dbsetup.h \
     src/database/entry.h \
     src/gui/dialogues/firstrundialog.h \
     src/gui/dialogues/newflightdialog.h \
     src/gui/dialogues/newpilotdialog.h \
     src/gui/dialogues/newtaildialog.h \
     src/gui/widgets/aircraftwidget.h \
+    src/gui/widgets/debugwidget.h \
     src/gui/widgets/homewidget.h \
     src/gui/widgets/logbookwidget.h \
     src/gui/widgets/pilotswidget.h \
@@ -74,6 +82,7 @@ FORMS += \
     src/gui/dialogues/newpilot.ui \
     src/gui/dialogues/newtail.ui \
     src/gui/widgets/aircraftwidget.ui \
+    src/gui/widgets/debugwidget.ui \
     src/gui/widgets/homewidget.ui \
     src/gui/widgets/logbookwidget.ui \
     src/gui/widgets/pilotswidget.ui \

+ 37 - 0
src/classes/csv.cpp

@@ -0,0 +1,37 @@
+#include "csv.h"
+
+Csv::Csv()
+{
+
+}
+/*!
+ * \brief Csv::read reads from a CSV file
+ * \param filename - QString to csv file.
+ * \return QVector<QStringList> of the CSV data, where each QStringList is one column of the input file
+ */
+QVector<QStringList> Csv::read(QString filename)
+{
+    QFile csvfile(filename);
+    csvfile.open(QIODevice::ReadOnly);
+    QTextStream stream(&csvfile);
+
+    QVector<QStringList> values;
+
+    //Read CSV headers and create QStringLists accordingly
+    QString line = stream.readLine();
+    auto items = line.split(",");
+    for (int i = 0; i < items.length(); i++) {
+        QStringList list;
+        list.append(items[i]);
+        values.append(list);
+    }
+    //Fill QStringLists with data
+    while (!stream.atEnd()) {
+        QString line = stream.readLine();
+        auto items = line.split(",");
+        for (int i = 0; i < values.length(); i++) {
+            values[i].append(items[i]);
+        }
+    }
+    return values;
+}

+ 14 - 0
src/classes/csv.h

@@ -0,0 +1,14 @@
+#ifndef CSV_H
+#define CSV_H
+#include <QtCore>
+
+class Csv
+{
+public:
+    Csv();
+
+    static QVector<QStringList> read(QString filename);
+
+};
+
+#endif // CSV_H

+ 83 - 0
src/classes/download.cpp

@@ -0,0 +1,83 @@
+/*
+ *openPilot Log - A FOSS Pilot Logbook Application
+ *Copyright (C) 2020  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 "download.h"
+#include "debug.h"
+
+
+
+
+Download::Download() : QObject(nullptr)
+{
+    QObject::connect(&manager, SIGNAL(finished(QNetworkReply*)),this, SLOT(downloadFinished(QNetworkReply*)));
+}
+
+Download::~Download()
+{
+    DEB("Deleting Download object");
+}
+
+void Download::setTarget(const QUrl &value)
+{
+    this->target = value;
+}
+
+void Download::setFileName(const QString &value)
+{
+    this->fileName = value;
+}
+
+void Download::download()
+{
+    QNetworkRequest request(target);
+    DEB("Downloading from: " << target.toString());
+
+    QObject::connect(manager.get(request), SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(downloadProgress(qint64,qint64)));
+}
+
+
+void Download::downloadProgress(qint64 received, qint64 total)
+{
+    auto shutupcompilerwarning = received;
+    shutupcompilerwarning += total;
+    //qDebug() << "Received " << received << " out of " << total;
+}
+
+void Download::downloadFinished(QNetworkReply *data)
+{
+    QFile localFile(fileName);
+    if (!localFile.open(QIODevice::WriteOnly))
+        return;
+    const QByteArray sdata = data->readAll();
+    localFile.write(sdata);
+    localFile.close();
+    qDebug() << "Download finished. Output file: " << fileName;
+
+    emit done();
+}
+
+/* usage:
+ *
+ *  auto dl = new Download(); // QNetworkAccessManager is asynchronous, so it needs an event loop to do any downloading
+ *  dl->setTarget(QUrl("https://raw.githubusercontent.com/fiffty-50/openpilotlog/master/README.md"));
+ *  dl->setFileName("out.md");
+ *  dl->download();
+ *
+ *  //do whatever when the download is done.
+ *  QObject::connect(&dl, SIGNAL(done()), &a, SLOT(whatever()));
+ *
+ */

+ 57 - 0
src/classes/download.h

@@ -0,0 +1,57 @@
+/*
+ *openPilot Log - A FOSS Pilot Logbook Application
+ *Copyright (C) 2020  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 DOWNLOAD_H
+#define DOWNLOAD_H
+
+#include <QObject>
+#include <QNetworkAccessManager>
+#include <QNetworkReply>
+#include <QNetworkRequest>
+#include <QUrl>
+#include <QFile>
+#include <QDebug>
+
+
+class Download : public QObject {
+    Q_OBJECT
+public:
+    explicit Download();
+
+    ~Download();
+
+    void setTarget(const QUrl &value);
+
+    void setFileName(const QString &value);
+
+    void download();
+
+private:
+
+    QNetworkAccessManager manager;
+    QUrl target;
+    QString fileName;
+
+signals:
+    void done();
+
+public slots:
+    void downloadFinished(QNetworkReply* data);
+    void downloadProgress(qint64 recieved, qint64 total);
+};
+
+#endif // DOWNLOAD_H

+ 418 - 0
src/database/dbsetup.cpp

@@ -0,0 +1,418 @@
+/*
+ *openPilot Log - A FOSS Pilot Logbook Application
+ *Copyright (C) 2020  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 "dbsetup.h"
+#include "debug.h"
+
+// Statements for creation of database tables, Revision 10
+
+const QString createTablePilots = "CREATE TABLE \"pilots\" ( "
+            "\"pilot_id\"       INTEGER NOT NULL, "
+            "\"piclastname\"	TEXT    NOT NULL, "
+            "\"picfirstname\"	TEXT, "
+            "\"alias\"          TEXT, "
+            "\"company\"        TEXT, "
+            "\"employeeid\"     TEXT, "
+            "\"phone\"          TEXT, "
+            "\"email\"          TEXT, "
+            "\"displayname\"	TEXT, "
+            "PRIMARY KEY(\"pilot_id\" AUTOINCREMENT)"
+            ")";
+
+const QString createTableTails = "CREATE TABLE \"tails\" ( "
+            "\"tail_id\"        INTEGER NOT NULL, "
+            "\"registration\"	TEXT NOT NULL, "
+            "\"company\"        TEXT, "
+            "\"make\"           TEXT, "
+            "\"model\"          TEXT, "
+            "\"variant\"        TEXT, "
+            "\"singlepilot\"	INTEGER, "
+            "\"multipilot\"     INTEGER, "
+            "\"singleengine\"	INTEGER, "
+            "\"multiengine\"	INTEGER, "
+            "\"unpowered\"      INTEGER, "
+            "\"piston\"         INTEGER, "
+            "\"turboprop\"      INTEGER, "
+            "\"jet\"            INTEGER, "
+            "\"light\"          INTEGER, "
+            "\"medium\"         INTEGER, "
+            "\"heavy\"          INTEGER, "
+            "\"super\"          INTEGER, "
+            "PRIMARY KEY(\"tail_id\" AUTOINCREMENT)"
+            ")";
+
+const QString createTableFlights = "CREATE TABLE \"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) "
+        ")";
+
+const QString createTableAirports = "CREATE TABLE \"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) "
+            ")";
+
+const QString createTableAircraft = "CREATE TABLE \"aircraft\" ( "
+            "\"aircraft_id\"	INTEGER NOT NULL, "
+            "\"make\"           TEXT, "
+            "\"model\"          TEXT, "
+            "\"variant\"        TEXT, "
+            "\"name\"           TEXT, "
+            "\"iata\"           TEXT, "
+            "\"icao\"           TEXT, "
+            "\"singlepilot\"	INTEGER, "
+            "\"multipilot\"     INTEGER, "
+            "\"singleengine\"	INTEGER, "
+            "\"multiengine\"	INTEGER, "
+            "\"unpowered\"      INTEGER, "
+            "\"piston\"         INTEGER, "
+            "\"turboprop\"      INTEGER, "
+            "\"jet\"            INTEGER, "
+            "\"light\"          INTEGER, "
+            "\"medium\"         INTEGER, "
+            "\"heavy\"          INTEGER, "
+            "\"super\"          INTEGER, "
+            "PRIMARY KEY(\"aircraft_id\" AUTOINCREMENT)"
+            ")";
+
+const QString createTableChangelog = "CREATE TABLE \"changelog\" ( "
+            "\"revision\"	INTEGER NOT NULL, "
+            "\"comment\"	TEXT, "
+            "\"date\"	NUMERIC, "
+            "PRIMARY KEY(\"revision\") "
+            ")";
+
+// Statements for creation of views in the database
+const QString createViewDefault = "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', "
+        "displayname AS 'Name PIC', "
+        "make||' '||model||'-'||variant AS 'Type', "
+        "registration AS 'Registration', "
+        "FlightNumber AS 'Flight #', "
+        "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 QString createViewEASA = "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 ', "
+        "make||' '||model||'-'||variant AS 'Type', "
+        "registration AS 'Registration', "
+        "(SELECT printf('%02d',(tSPSE/60))||':'||printf('%02d',(tSPSE%60)) WHERE tSPSE IS NOT \"\") AS 'SP SE', "
+        "(SELECT printf('%02d',(tSPME/60))||':'||printf('%02d',(tSPME%60)) WHERE tSPME IS NOT \"\") AS 'SP ME', "
+        "(SELECT printf('%02d',(tMP/60))||':'||printf('%02d',(tMP%60)) WHERE tMP IS NOT \"\") AS 'MP', "
+        "printf('%02d',(tblk/60))||':'||printf('%02d',(tblk%60)) AS 'Total', "
+        "displayname AS 'Name PIC', "
+        "ldgDay AS 'L/D', "
+        "ldgNight AS 'L/N', "
+        "(SELECT printf('%02d',(tNight/60))||':'||printf('%02d',(tNight%60)) WHERE tNight IS NOT \"\")  AS 'Night', "
+        "(SELECT printf('%02d',(tIFR/60))||':'||printf('%02d',(tIFR%60)) WHERE tIFR IS NOT \"\")  AS 'IFR', "
+        "(SELECT printf('%02d',(tPIC/60))||':'||printf('%02d',(tPIC%60)) WHERE tPIC IS NOT \"\")  AS 'PIC', "
+        "(SELECT printf('%02d',(tSIC/60))||':'||printf('%02d',(tSIC%60)) WHERE tSIC IS NOT \"\")  AS 'SIC', "
+        "(SELECT printf('%02d',(tDual/60))||':'||printf('%02d',(tDual%60)) WHERE tDual IS NOT \"\")  AS 'Dual', "
+        "(SELECT printf('%02d',(tFI/60))||':'||printf('%02d',(tFI%60)) WHERE tFI IS NOT \"\")  AS 'FI', "
+        "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 QString createViewTails = "CREATE VIEW viewTails AS "
+        "SELECT "
+        "tail_id AS 'ID', registration AS 'Registration', "
+        "make||' '||model||'-'||variant AS 'Type', "
+        "company AS 'Company' "
+        "FROM tails";
+
+const QString createViewPilots = "CREATE VIEW viewPilots AS "
+        "SELECT "
+        "pilot_id AS 'ID', "
+        "piclastname AS 'Last Name', "
+        "picfirstname AS 'First Name', company AS 'Company' "
+        "FROM pilots";
+
+const QString createViewQCompleter = "CREATE VIEW viewQCompleter AS "
+        "SELECT airport_id, icao, iata, tail_id, registration, pilot_id, "
+        "piclastname||', '||picfirstname 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 QString createViewTotals = "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 QStringList tables = {
+    createTablePilots,
+    createTableTails,
+    createTableFlights,
+    createTableAircraft,
+    createTableAirports,
+    createTableChangelog
+};
+const QStringList views = {
+    createViewDefault,
+    createViewEASA,
+    createViewTails,
+    createViewPilots,
+    createViewTotals,
+    createViewQCompleter
+};
+const QStringList userTables = {
+    "flights",
+    "pilots",
+    "tails"
+};
+const QStringList templateTables= {
+    "aircraft",
+    "airports",
+    "changelog"
+};
+
+
+bool DbSetup::createDatabase()
+{
+    QVector<bool> returnValues;
+
+    DEB("Creating tables...");
+    returnValues << createSchemata(tables);
+    DEB("Creating views...");
+    returnValues << createSchemata(views);
+    DEB("Populating tables...");
+    returnValues << importDefaultData();
+
+    for (const auto& allGood : returnValues) {
+        if (!allGood) {
+            return false;
+        } else {
+            DEB("Database successfully created!");
+            return true;
+        }
+    }
+    return false;
+}
+
+
+bool DbSetup::importDefaultData()
+{
+    QSqlQuery query;
+    // reset template tables
+    for (const auto& table : templateTables) {
+        //clear tables
+        query.prepare("DELETE FROM " + table);
+        if (!query.exec()) {
+            DEB("Error: " << query.lastError().text());
+        }
+        //fill with data from csv
+        if (!commitData(Csv::read("data/templates/" + table + ".csv"), table)) {
+            DEB("Error importing data.");
+            return false;
+        }
+    }
+    return true;
+};
+
+/*!
+ * \brief DbSetup::resetToDefault Empties all user-generated content in the database.
+ * \return true on success
+ */
+bool DbSetup::resetToDefault()
+{
+    QSqlQuery query;
+
+    // clear user tables
+    for (const auto& table : userTables) {
+        query.prepare("DELETE FROM " + table);
+        if (!query.exec()) {
+            DEB("Error: " << query.lastError().text());
+        }
+    }
+    return true;
+}
+
+/*!
+ * \brief dbSetup::debug prints Database Layout
+ */
+void DbSetup::debug()
+{
+    DEB("Database tables and views: ");
+    QSqlQuery query;
+    const QVector<QString> types = { "\"table\"", "\"view\"" };
+    for (const auto& var : types){
+        query.prepare("SELECT name FROM sqlite_master WHERE type=" + var);
+        query.exec();
+        while (query.next()) {
+            QString table = query.value(0).toString();
+            QSqlQuery entries("SELECT COUNT(*) FROM " + table);
+            entries.next();
+            DEB("Element " << query.value(0).toString()) << "with"
+                << entries.value(0).toString() << "rows";
+        }
+    }
+}
+
+/*!
+ * \brief dbSetup::createTables Create the required tables for the database
+ * \return true on success
+ */
+bool DbSetup::createSchemata(const QStringList &statements)
+{
+    QSqlQuery query;
+    QStringList errors;
+
+    for (const auto& statement : statements) {
+        query.prepare(statement);
+        query.exec();
+        if(!query.isActive()) {
+            errors << statement.section(QLatin1Char(' '),2,2) + " ERROR - " + query.lastError().text();
+        } else {
+            DEB("Schema added: " << statement.section(QLatin1Char(' '),2,2));
+        }
+    }
+
+    if (!errors.isEmpty()) {
+        DEB("The following errors have ocurred: ");
+        for (const auto& error : errors) {
+            DEB(error);
+        }
+        return false;
+    } else {
+        DEB("All schemas added successfully");
+        return true;
+    }
+}
+/*!
+ * \brief DbSetup::commitData inserts the data parsed from a csv file into the
+ * database. The first line of the csv file has to contain the column names
+ * of the corresponding table in the database.
+ * \param fromCSV input as parsed from CSV::read()
+ * \param tableName as in the database
+ * \return
+ */
+bool DbSetup::commitData(QVector<QStringList> fromCSV, const QString &tableName)
+{
+    DEB("Importing Data to" << tableName);
+    auto dbLayout = DbInfo();
+    if (!dbLayout.tables.contains(tableName)){
+        DEB(tableName << "is not a table in the database. Aborting.");
+        DEB("Please check input data.");
+        return false;
+    }
+    // create insert statement
+    QString statement = "INSERT INTO " + tableName + " (";
+    QString placeholder = ") VALUES (";
+    for (auto& csvColumn : fromCSV) {
+        if(dbLayout.format.value(tableName).contains(csvColumn.first())){
+            statement += csvColumn.first() + ',';
+            csvColumn.removeFirst();
+            placeholder.append("?,");
+        } else {
+            DEB(csvColumn.first() << "is not a column of " << tableName << "Aborting.");
+            DEB("Please check input data.");
+            return false;
+        }
+    }
+    statement.chop(1);
+    placeholder.chop(1);
+    placeholder.append(')');
+    statement.append(placeholder);
+
+    /*
+     * Using exclusive transaction and the loop below is MUCH faster than
+     * passing the QStringLists to QSqlQuery::addBindValue and using QSqlQuery::execBatch()
+     */
+    QSqlQuery query;
+    query.exec("BEGIN EXCLUSIVE TRANSACTION;");
+    for (int i = 0; i < fromCSV.first().length(); i++){
+        query.prepare(statement);
+        for(int j = 0; j < fromCSV.length(); j++) {
+            query.addBindValue(fromCSV[j][i]);
+        }
+        query.exec();
+    }
+
+    query.exec("COMMIT;"); //commit transaction
+    if (query.lastError().text().length() > 3) {
+        DEB("Error:" << query.lastError().text());
+        return false;
+    } else {
+        qDebug() << tableName << "Database successfully updated!";
+        return true;
+    }
+}

+ 55 - 0
src/database/dbsetup.h

@@ -0,0 +1,55 @@
+/*
+ *openPilot Log - A FOSS Pilot Logbook Application
+ *Copyright (C) 2020  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 DBSETUP_H
+#define DBSETUP_H
+
+#include <QCoreApplication>
+#include "src/database/db.h"
+#include "src/database/dbinfo.h"
+#include "src/classes/csv.h"
+
+
+/*!
+ * \brief The DbSetup class 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. It can also be used to reset the
+ * database currently used
+ */
+class DbSetup
+{
+public: 
+    static void debug();
+
+    static bool createDatabase();
+
+    static bool fillTemplates();
+
+    static bool importDefaultData();
+
+    static bool resetToDefault();
+
+    static bool commitData(QVector<QStringList> fromCSV, const QString &tableName);
+
+private:
+
+    static bool createSchemata(const QStringList &statements);
+
+
+};
+
+#endif // DBSETUP_H

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

@@ -372,7 +372,7 @@ inline void NewFlightDialog::setupLineEdit(QLineEdit* line_edit, LineEditSetting
 
     auto comp_model = new QSqlRelationalTableModel(line_edit, db);
     comp_model->database().open();
-    comp_model->setTable("QCompleterView");
+    comp_model->setTable("ViewQCompleter");
     comp_model->select();
 
     auto completer = new QCompleter(comp_model, line_edit);

+ 162 - 0
src/gui/widgets/debugwidget.cpp

@@ -0,0 +1,162 @@
+#include "debugwidget.h"
+#include "ui_debugwidget.h"
+#include "debug.h"
+
+DebugWidget::DebugWidget(QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::DebugWidget)
+{
+    ui->setupUi(this);
+    for (const auto& table : DbInfo().tables) {
+        if( table != "sqlite_sequence") {
+            ui->tableComboBox->addItem(table);
+        }
+    }
+
+}
+
+DebugWidget::~DebugWidget()
+{
+    delete ui;
+}
+
+void DebugWidget::on_resetUserTablesPushButton_clicked()
+{
+    QMessageBox result;
+    if (DbSetup::resetToDefault()){
+        result.setText("Database successfully reset.\n\nRestarting app.");
+        result.exec();
+        qApp->quit();
+        QProcess::startDetached(qApp->arguments()[0], qApp->arguments());
+    } else {
+        result.setText("Errors have occurred. Check console for Debug output. ");
+        result.exec();
+    }
+
+}
+
+void DebugWidget::on_resetDatabasePushButton_clicked()
+{
+    QMessageBox mb(this);
+    //check if template dir exists and create if needed.
+    QDir dir("data/templates");
+    DEB(dir.path());
+    if (!dir.exists())
+        dir.mkpath(".");
+    // download latest csv
+    QStringList templateTables = {"aircraft", "airports", "changelog"};
+    const auto& linkStub = "https://raw.githubusercontent.com/fiffty-50/openpilotlog/devel/assets/database/templates/";
+    for (const auto& table : templateTables) {
+        QEventLoop loop;
+        Download* dl = new Download;
+        connect(dl, &Download::done, &loop, &QEventLoop::quit );
+        dl->setTarget(QUrl(linkStub + table + ".csv"));
+        dl->setFileName("data/templates/" + table + ".csv");
+        dl->download();
+        loop.exec(); // event loop waits for download done signal before allowing loop to continue
+        dl->deleteLater();
+    }
+    //close database connection
+    auto db = Db::Database();
+    db.close();
+    db.removeDatabase(db.connectionName());
+    DEB("Closing database connection.");
+
+    // back up old database
+    auto oldDatabase = QFile("data/logbook.db");
+    if (oldDatabase.exists()) {
+        auto dateString = QDateTime::currentDateTime().toString(Qt::ISODate);
+        DEB("Backing up old database as: " << "logbook-backup-" + dateString);
+        if (!oldDatabase.rename("data/logbook-backup-" + dateString)) {
+            DEB("Warning: Creating backup of old database has failed.");
+        }
+    }
+    // re-connct and create new database
+    Db::connect();
+
+    if (DbSetup::createDatabase()) {
+        mb.setText("Database has been successfully reset.\n\nRestarting application.");
+        mb.exec();
+        qApp->quit();
+        QProcess::startDetached(qApp->arguments()[0], qApp->arguments());
+    } else {
+        mb.setText("Errors have ocurred. Check console for details.");
+    }
+}
+
+void DebugWidget::downloadFinished()
+{
+
+}
+
+void DebugWidget::on_fillUserDataPushButton_clicked()
+{
+    QMessageBox mb(this);
+    //check if template dir exists and create if needed.
+    QDir dir("data/templates");
+    DEB(dir.path());
+    if (!dir.exists())
+        dir.mkpath(".");
+    // download latest csv
+    QStringList userTables = {"pilots", "tails", "flights"};
+    const auto& linkStub = "https://raw.githubusercontent.com/fiffty-50/openpilotlog/devel/assets/database/templates/sample_";
+    for (const auto& table : userTables) {
+        QEventLoop loop;
+        Download* dl = new Download;
+        connect(dl, &Download::done, &loop, &QEventLoop::quit );
+        dl->setTarget(QUrl(linkStub + table + ".csv"));
+        dl->setFileName("data/templates/sample_" + table + ".csv");
+        dl->download();
+        loop.exec(); // event loop waits for download done signal before allowing loop to continue
+        dl->deleteLater();
+    }
+    QVector<bool> allGood;
+    for (const auto& table : userTables) {
+        auto data = Csv::read("data/templates/sample_" + table + ".csv");
+        allGood.append(DbSetup::commitData(data, table));
+    }
+
+    for (const auto& item : allGood) {
+        if (!item) {
+            mb.setText("Errors have ocurred. Check console for details.");
+            mb.exec();
+            return;
+        }
+
+    }
+
+    mb.setText("User tables successfully populated.\n\nRestarting app.");
+    mb.exec();
+    qApp->quit();
+    QProcess::startDetached(qApp->arguments()[0], qApp->arguments());
+}
+
+void DebugWidget::on_selectCsvPushButton_clicked()
+{
+    auto fileName = QFileDialog::getOpenFileName(this,
+        tr("Open CSV File for import"), QDir::homePath(), tr("CSV files (*.csv)"));
+    ui->importCsvLineEdit->setText(fileName);
+}
+
+void DebugWidget::on_importCsvPushButton_clicked()
+{
+    auto file = QFileInfo(ui->importCsvLineEdit->text());
+    DEB("File exists/is file: " << file.exists() << file.isFile() << " Path: " << file.absoluteFilePath());
+
+    if (file.exists() && file.isFile()) {
+
+        if (DbSetup::commitData(Csv::read(file.absoluteFilePath()), ui->tableComboBox->currentText())) {
+            auto mb = QMessageBox(this);
+            mb.setText("Data inserted successfully.");
+            mb.exec();
+        } else {
+            auto mb = QMessageBox(this);
+            mb.setText("Errors have ocurred. Check console for details.");
+            mb.exec();
+        }
+    } else {
+        auto mb = QMessageBox(this);
+        mb.setText("Please select a valid file.");
+        mb.exec();
+    }
+}

+ 46 - 0
src/gui/widgets/debugwidget.h

@@ -0,0 +1,46 @@
+#ifndef DEBUGWIDGET_H
+#define DEBUGWIDGET_H
+
+#include <QWidget>
+#include <QDir>
+#include <QStandardPaths>
+#include <QFile>
+#include <QFileDialog>
+#include <QMessageBox>
+#include "src/database/db.h"
+#include "src/database/dbsetup.h"
+#include "src/database/dbinfo.h"
+#include "src/classes/download.h"
+
+namespace Ui {
+class DebugWidget;
+}
+
+class DebugWidget : public QWidget
+{
+    Q_OBJECT
+
+public:
+    explicit DebugWidget(QWidget *parent = nullptr);
+    ~DebugWidget();
+
+private slots:
+    void on_resetUserTablesPushButton_clicked();
+
+    void on_resetDatabasePushButton_clicked();
+
+    void downloadFinished();
+
+    void on_fillUserDataPushButton_clicked();
+
+    void on_selectCsvPushButton_clicked();
+
+    void on_importCsvPushButton_clicked();
+
+private:
+    Ui::DebugWidget *ui;
+
+    bool downloadComplete = false;
+};
+
+#endif // DEBUGWIDGET_H

+ 143 - 0
src/gui/widgets/debugwidget.ui

@@ -0,0 +1,143 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>DebugWidget</class>
+ <widget class="QWidget" name="DebugWidget">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>640</width>
+    <height>480</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QGridLayout" name="gridLayout">
+   <item row="0" column="0">
+    <widget class="QTabWidget" name="tabWidget">
+     <property name="currentIndex">
+      <number>0</number>
+     </property>
+     <widget class="QWidget" name="databaseTab">
+      <attribute name="title">
+       <string>Database</string>
+      </attribute>
+      <layout class="QGridLayout" name="gridLayout_2">
+       <item row="0" column="0">
+        <widget class="QPushButton" name="resetDatabasePushButton">
+         <property name="minimumSize">
+          <size>
+           <width>220</width>
+           <height>0</height>
+          </size>
+         </property>
+         <property name="text">
+          <string>Reset Database</string>
+         </property>
+        </widget>
+       </item>
+       <item row="3" column="1" colspan="2">
+        <widget class="QComboBox" name="tableComboBox">
+         <property name="minimumSize">
+          <size>
+           <width>110</width>
+           <height>0</height>
+          </size>
+         </property>
+         <item>
+          <property name="text">
+           <string>Select Table</string>
+          </property>
+         </item>
+        </widget>
+       </item>
+       <item row="3" column="0">
+        <widget class="QPushButton" name="importCsvPushButton">
+         <property name="minimumSize">
+          <size>
+           <width>110</width>
+           <height>0</height>
+          </size>
+         </property>
+         <property name="text">
+          <string>Import CSV</string>
+         </property>
+        </widget>
+       </item>
+       <item row="1" column="0">
+        <widget class="QPushButton" name="resetUserTablesPushButton">
+         <property name="minimumSize">
+          <size>
+           <width>220</width>
+           <height>0</height>
+          </size>
+         </property>
+         <property name="text">
+          <string>Reset User Tables</string>
+         </property>
+        </widget>
+       </item>
+       <item row="3" column="3">
+        <widget class="QLineEdit" name="importCsvLineEdit">
+         <property name="minimumSize">
+          <size>
+           <width>110</width>
+           <height>0</height>
+          </size>
+         </property>
+         <property name="placeholderText">
+          <string>/path/to/file.csv</string>
+         </property>
+        </widget>
+       </item>
+       <item row="3" column="4">
+        <widget class="QPushButton" name="selectCsvPushButton">
+         <property name="minimumSize">
+          <size>
+           <width>110</width>
+           <height>0</height>
+          </size>
+         </property>
+         <property name="text">
+          <string>Select File</string>
+         </property>
+        </widget>
+       </item>
+       <item row="2" column="0">
+        <widget class="QPushButton" name="fillUserDataPushButton">
+         <property name="text">
+          <string>Fill User Table with test data</string>
+         </property>
+        </widget>
+       </item>
+       <item row="0" column="3">
+        <widget class="QLabel" name="label">
+         <property name="text">
+          <string>Backup current database (if exists) and create a new one from scratch.</string>
+         </property>
+        </widget>
+       </item>
+       <item row="1" column="3">
+        <widget class="QLabel" name="label_2">
+         <property name="text">
+          <string>Keep current database but delete entries in pilots, aircraft and flights</string>
+         </property>
+        </widget>
+       </item>
+       <item row="2" column="3">
+        <widget class="QLabel" name="label_3">
+         <property name="text">
+          <string>Fill a new database with sample entries</string>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </widget>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

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

@@ -77,7 +77,7 @@ void LogbookWidget::defaultView()
 {
     DEB("Loading Default View...");
     QSqlTableModel *model = new QSqlTableModel;
-    model->setTable("Logbook");
+    model->setTable("viewDefault");
     model->select();
 
     QTableView *view = ui->tableView;