Procházet zdrojové kódy

New Flight Dialog first draft complete

- Basic functionality (Adding, editing) functional
- Adjusted database views and optimized handling of NULL fields. New Revision 13, added a Debug output to warn if DB rev is out of date.
Felix Turo před 4 roky
rodič
revize
f58fe00b3c

+ 1 - 0
assets/database/templates/changelog.csv

@@ -11,3 +11,4 @@ revision,comment,date
 10,added viewDefault as a copy of Logbook (deprecated),2020-11-23
 11,added viewQCompleter as a copy of QCompleterView (deprecated),2020-11-23
 12,reworked views to display self or picname according CASE,2020-12-11
+13,Reworked viewEASA to incorporate NULL handling,2020-12-17

+ 19 - 0
mainwindow.cpp

@@ -70,6 +70,25 @@ MainWindow::MainWindow(QWidget *parent)
     ui->stackedWidget->addWidget(debugWidget);
     ui->stackedWidget->setCurrentWidget(debugWidget);
 
+    //// START DEBUG ////
+    /// [F] I understand how it is annoying to not have the database
+    /// working when something has changed. Hopefully this check
+    /// helps to avoid that in the future!
+    const int DATABASE_REVISION_NUMBER = 13;
+    QSqlQuery query;
+    query.prepare("SELECT COUNT (*) FROM changelog");
+    query.exec();
+    query.next();
+    if (query.value(0).toInt() < DATABASE_REVISION_NUMBER) {
+        DEB("##########################################");
+        DEB("Your database is out of date.");
+        DEB("Curren Revision: " << DATABASE_REVISION_NUMBER);
+        DEB("You have revision: " << query.value(0).toInt());
+        DEB("Use of DebugWidget to udpate recommended.");
+        DEB("##########################################");
+    }
+    //// END DEBUG ////
+
 }
 
 MainWindow::~MainWindow()

+ 15 - 12
src/database/adatabasesetup.cpp

@@ -19,7 +19,7 @@
 #include "src/testing/adebug.h"
 
 
-// Statements for creation of database tables, Revision 12
+// Statements for creation of database tables, Revision 13
 
 const QString createTablePilots = "CREATE TABLE \"pilots\" ( "
             "\"pilot_id\"       INTEGER NOT NULL, "
@@ -165,9 +165,9 @@ const QString createViewEASA = "CREATE VIEW viewEASA AS "
         "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', "
+        "(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 "
@@ -176,12 +176,12 @@ const QString createViewEASA = "CREATE VIEW viewEASA AS "
         "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', "
+        "(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 "
         "FROM flights "
         "INNER JOIN pilots on flights.pic = pilots.pilot_id "
@@ -428,8 +428,11 @@ bool ADataBaseSetup::commitData(QVector<QStringList> fromCSV, const QString &tab
     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]);
-        }
+             fromCSV[j][i] == QString("") ? // make sure NULL is committed for empty values
+                         query.addBindValue(QVariant(QVariant::String))
+                       : query.addBindValue(fromCSV[j][i]);
+             //query.addBindValue(fromCSV[j][i]);
+         }
         query.exec();
     }
 

+ 4 - 0
src/experimental/decl.h

@@ -16,6 +16,7 @@ using RowId = int;
 using TableNames = QStringList;
 /// [G]: May lead to some confusion. TableData suggest data for the entire table.
 /// but in reallity it is data per column *of single row* (unless i misunderstand)
+/// [F]: That's correct. We could maybe call it EntryData or RowData?
 using TableData = QMap<ColName, ColData>;
 using ColumnData = QPair<ColName, ColData>;
 using ColumnNames = QStringList;
@@ -24,6 +25,9 @@ using TableColumns = QMap<TableName, ColumnNames>;
 /// [G]: Needs some work. Inheriting from QPair may be helpful but
 /// may also be overkill. Lets determine the specific uses of DataPosition
 /// and provide our own interface i would say.
+/// [F]: Good idea! Implementing something similar to first and second methods
+/// of QPair would be useful to carry over, or some other way of quickly and
+/// unambiguously accessing the elements.
 struct DataPosition : QPair<TableName, RowId> {
     TableName tableName;
     RowId rowId;

+ 341 - 48
src/experimental/expnewflightdialog.cpp

@@ -16,6 +16,8 @@ void ExpNewFlightDialog::onInputRejected()
 ///                                         constants                                           ///
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
+static const auto TIME_FORMAT = QLatin1String("hh:mm");
+
 static const auto IATA_RX = QLatin1String("[a-zA-Z0-9]{3}");
 static const auto ICAO_RX = QLatin1String("[a-zA-Z0-9]{4}");
 static const auto NAME_RX = QLatin1String("(\\p{L}+('|\\-|,)?\\p{L}+?)");
@@ -37,12 +39,15 @@ static const auto DATE_VALID_RGX       = QRegularExpression("^([1-9][0-9]{3}).?(
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 /// [F] The general idea for this dialog is this:
-/// - line edits have validators and completers.
+/// - Most line edits have validators and completers.
+/// - Validators are based on regular expressions, serving as raw input validation
+/// - The Completers are based off the database and provide auto-completion
 /// - mandatory line edits only emit editing finished if their content is
 /// valid. This means mapping user inputs to database keys where required.
-/// - A QBitArray is mainained with the state of the mandatory line edits
+/// - A QBitArray is mainained containing the state of validity of the mandatory line edits
 /// - The deducted entries are automatically filled if all mandatory entries
 /// are valid.
+/// - Comitting an entry to the database is only allowed if all mandatory inputs are valid.
 ///
 /// if the user presses "OK", check if all mandatory inputs are valid,
 /// check if optional user inputs are valid and commit.
@@ -51,8 +56,10 @@ static const auto DATE_VALID_RGX       = QRegularExpression("^([1-9][0-9]{3}).?(
 /// Completers based on QStringLists and mapping with QMaps.
 ///
 /// I implemented the Completers and mapping based on a QSqlTableModel which would
-/// have been quite nice, since it would keep all data in one place, but as we have
-/// seen before with the more high-level qt classes, they are quite slow on execution.
+/// have been quite nice, since it would keep all data in one place, providing both completion
+/// and mapping in one model.
+/// But as we have seen before with the more high-level qt classes, they are quite slow on execution
+/// when used for tasks they were probably not designed to do.
 /// Mapping a registration to an ID for example took around 300ms, which is very
 /// noticeable in the UI and not an acceptable user experience. Using QStringLists and QMaps
 /// this goes down to around 5ms.
@@ -81,7 +88,7 @@ ExpNewFlightDialog::~ExpNewFlightDialog()
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-///                                         Methods                                             ///
+///                        Methods - setup and maintenance of dialog                            ///
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 void ExpNewFlightDialog::setup()
@@ -107,7 +114,6 @@ void ExpNewFlightDialog::setup()
     if(ASettings::read("NewFlight/FunctionComboBox").toString() == "PIC"){
         ui->picNameLineEdit_2->setText("self");
         emit ui->picNameLineEdit_2->editingFinished();
-        ui->secondPilotNameLineEdit_2->setText("");
     }
 }
 void ExpNewFlightDialog::readSettings()
@@ -128,10 +134,9 @@ void ExpNewFlightDialog::readSettings()
     ui->IfrCheckBox_2->setChecked(ASettings::read("NewFlight/IfrCheckBox").toBool());
     ui->VfrCheckBox_2->setChecked(ASettings::read("NewFlight/VfrCheckBox").toBool());
     ui->FlightNumberLineEdit_2->setText(ASettings::read("flightlogging/flightnumberPrefix").toString());
-
     ui->calendarCheckBox->setChecked(ASettings::read("NewFlight/calendarCheckBox").toBool());
-    setPopUpCalendarEnabled(ASettings::read("NewFlight/calendarCheckBox").toBool());
 
+    setPopUpCalendarEnabled(ASettings::read("NewFlight/calendarCheckBox").toBool());
 
     if (ASettings::read("NewFlight/FunctionComboBox").toString() == "Co-Pilot") {
         ui->picNameLineEdit_2->setText("");
@@ -229,6 +234,10 @@ void ExpNewFlightDialog::setupRawInputValidation()
         ui->acftLineEdit_2,
     };
     mandatoryLineEditsGood.resize(mandatoryLineEdits.size());
+    primaryTimeLineEdits = {
+        ui->tofbTimeLineEdit_2,
+        ui->tonbTimeLineEdit_2
+    };
 }
 
 void ExpNewFlightDialog::setupLineEditSignalsAndSlots()
@@ -255,6 +264,10 @@ void ExpNewFlightDialog::setupLineEditSignalsAndSlots()
             QObject::connect(line_edit, &QLineEdit::editingFinished,
                              this, &ExpNewFlightDialog::onPilotLineEdit_editingFinished);
         }
+        if(line_edit->objectName().contains("Time")){
+            QObject::connect(line_edit, &QLineEdit::editingFinished,
+                             this, &ExpNewFlightDialog::onTimeLineEdit_editingFinished);
+        }
     }
     QObject::connect(this, &ExpNewFlightDialog::goodInputReceived,
                      this, &ExpNewFlightDialog::onGoodInputReceived);
@@ -303,7 +316,9 @@ bool ExpNewFlightDialog::eventFilter(QObject* object, QEvent* event)
     }
     return false;
 }
-
+///////////////////////////////////////////////////////////////////////////////////////////////////
+///                                 Methods - Input Processing                                  ///
+///////////////////////////////////////////////////////////////////////////////////////////////////
 /*!
  * \brief Fills the deductable items in the Dialog based on mandatory data ui selections.
  */
@@ -320,11 +335,11 @@ void ExpNewFlightDialog::fillDeductibleData()
     for(const auto& widget : LB) {widget->setText("00:00");}
 
     //Calculate block time
-    auto tofb = QTime::fromString(ui->tofbTimeLineEdit_2->text(), "hh:mm");
-    auto tonb = QTime::fromString(ui->tonbTimeLineEdit_2->text(), "hh:mm");
-    QString blockTime = ACalc::blocktime(tofb, tonb).toString("hh:mm");
-    QString block_minutes = QString::number(ACalc::stringToMinutes(blockTime));
-    ui->tblkTimeLineEdit_2->setText(blockTime);
+    auto tofb = QTime::fromString(ui->tofbTimeLineEdit_2->text(), TIME_FORMAT);
+    auto tonb = QTime::fromString(ui->tonbTimeLineEdit_2->text(), TIME_FORMAT);
+    QString block_time = ACalc::blocktime(tofb, tonb).toString(TIME_FORMAT);
+    QString block_minutes = QString::number(ACalc::stringToMinutes(block_time));
+    ui->tblkTimeLineEdit_2->setText(block_minutes);
 
     // get acft data and fill deductible entries
     auto acft = aDB()->getTailEntry(tailsIdMap.value(ui->acftLineEdit_2->text()));
@@ -334,29 +349,29 @@ void ExpNewFlightDialog::fillDeductibleData()
         DEB("No valid aircraft object available.");
     // SP SE
     if(acft.getData().value("singlepilot") == "1" && acft.getData().value("singleengine") == "1"){
-        ui->tSPSETimeLineEdit_2->setText(blockTime);
-        ui->tSPSELabel_2->setText(blockTime);
+        ui->tSPSETimeLineEdit_2->setText(block_minutes);
+        ui->tSPSELabel_2->setText(block_minutes);
     }
     // SP ME
     if(acft.getData().value("singlepilot") == "1" && acft.getData().value("multiengine") == "1"){
-        ui->tSPMETimeLineEdit_2->setText(blockTime);
-        ui->tSPMELabel_2->setText(blockTime);
+        ui->tSPMETimeLineEdit_2->setText(block_minutes);
+        ui->tSPMELabel_2->setText(block_minutes);
     }
     // MP
     if(acft.getData().value("multipilot") == "1"){
-        ui->tMPTimeLineEdit_2->setText(blockTime);
-        ui->tMPLabel_2->setText(blockTime);
+        ui->tMPTimeLineEdit_2->setText(block_minutes);
+        ui->tMPLabel_2->setText(block_minutes);
     }
     // TOTAL
-    ui->tblkLabel_3->setText("<b>" + blockTime + "</b>");
+    ui->tblkLabel_3->setText("<b>" + block_minutes + "</b>");
     ui->tblkLabel_3->setStyleSheet("color: green;");
     // IFR
     if(ui->IfrCheckBox_2->isChecked()){
-        ui->tIFRTimeLineEdit_2->setText(blockTime);
-        ui->tIFRLabel_2->setText(blockTime);
+        ui->tIFRTimeLineEdit_2->setText(block_minutes);
+        ui->tIFRLabel_2->setText(block_minutes);
     }
     // Night
-    QString dept_date = ui->doftLineEdit_2->text() + 'T' + tofb.toString("hh:mm");
+    QString dept_date = ui->doftLineEdit_2->text() + 'T' + tofb.toString(TIME_FORMAT);
     QDateTime dept_date_time = QDateTime::fromString(dept_date,"yyyy-MM-ddThh:mm");
     int tblk = block_minutes.toInt();
     const int night_angle = ASettings::read("flightlogging/nightangle").toInt();
@@ -373,24 +388,214 @@ void ExpNewFlightDialog::fillDeductibleData()
     // Function times
     switch (ui->FunctionComboBox_2->currentIndex()) {
     case 0://PIC
-        ui->tPICTimeLineEdit_2->setText(blockTime);
-        ui->tPICLabel_2->setText(blockTime);
+        ui->tPICTimeLineEdit_2->setText(block_minutes);
+        ui->tPICLabel_2->setText(block_minutes);
         break;
     case 1://PICus
-        ui->tPICUSTimeLineEdit_2->setText(blockTime);
-        ui->tPICUSLabel_2->setText(blockTime);
+        ui->tPICUSTimeLineEdit_2->setText(block_minutes);
+        ui->tPICUSLabel_2->setText(block_minutes);
         break;
     case 2://Co-Pilot
-        ui->tSICTimeLineEdit_2->setText(blockTime);
-        ui->tSICLabel_2->setText(blockTime);
+        ui->tSICTimeLineEdit_2->setText(block_minutes);
+        ui->tSICLabel_2->setText(block_minutes);
         break;
     case 3://Dual
-        ui->tDUALTimeLineEdit_2->setText(blockTime);
-        ui->tDUALLabel_2->setText(blockTime);
+        ui->tDUALTimeLineEdit_2->setText(block_minutes);
+        ui->tDUALLabel_2->setText(block_minutes);
         break;
     case 4://Instructor
-        ui->tFITimeLineEdit_2->setText(blockTime);
-        ui->tFILabel_2->setText(blockTime);
+        ui->tFITimeLineEdit_2->setText(block_minutes);
+        ui->tFILabel_2->setText(block_minutes);
+    }
+}
+/*!
+ * \brief Collect input and create a Data map for the entry object.
+ *
+ * This function should only be called if input validation has been passed, since
+ * no input validation is done in this step and input data is assumed to be valid.
+ * \return
+ */
+TableData ExpNewFlightDialog::collectInput()
+{
+    TableData newData;
+    DEB("Collecting Input...");
+    // Mandatory data
+    newData.insert("doft", ui->doftLineEdit_2->text());
+    newData.insert("dept",ui->deptLocLineEdit_2->text());
+    newData.insert("tofb", QString::number(
+                       ACalc::stringToMinutes(ui->tofbTimeLineEdit_2->text())));
+    newData.insert("dest",ui->destLocLineEdit_2->text());
+    newData.insert("tonb", QString::number(
+                       ACalc::stringToMinutes(ui->tonbTimeLineEdit_2->text())));
+    //Block Time
+    const auto tofb = QTime::fromString(ui->tofbTimeLineEdit_2->text(), TIME_FORMAT);
+    const auto tonb = QTime::fromString(ui->tonbTimeLineEdit_2->text(), TIME_FORMAT);
+    const QString block_time = ACalc::blocktime(tofb, tonb).toString(TIME_FORMAT);
+    const QString block_minutes = QString::number(ACalc::stringToMinutes(block_time));
+
+    newData.insert("tblk", block_minutes);
+    // Aircraft and Pilots
+    newData.insert("acft", QString::number(tailsIdMap.value(ui->acftLineEdit_2->text())));
+    newData.insert("pic", QString::number(pilotsIdMap.value(ui->picNameLineEdit_2->text())));
+    newData.insert("secondPilot", QString::number(pilotsIdMap.value(ui->secondPilotNameLineEdit_2->text())));
+    newData.insert("thirdPilot", QString::number(pilotsIdMap.value(ui->thirdPilotNameLineEdit_2->text())));
+
+    // Extra Times
+    auto acft = ATailEntry(newData.value("acft").toInt());
+    if (acft.getData().isEmpty())
+        DEB("Invalid Aircraft. Unable to automatically determine extra times.");
+
+    if (acft.getData().value("multipilot") == "1") {
+        newData.insert("tSPSE", "");
+        newData.insert("tSPME", "");
+        newData.insert("tMP", block_minutes);
+    } else if (acft.getData().value("singlepilot") == "1" && acft.getData().value("singleengine") == "1") {
+        newData.insert("tSPSE", block_minutes);
+        newData.insert("tSPME", "");
+        newData.insert("tMP", "");
+    } else if (acft.getData().value("singlepilot") == "1" && acft.getData().value("multiengine") == "1") {
+        newData.insert("tSPSE", "");
+        newData.insert("tSPME", block_minutes);
+        newData.insert("tMP", "");
+    }
+
+    if (ui->IfrCheckBox_2->isChecked()) {
+        newData.insert("tIFR", block_minutes);
+    } else {
+        newData.insert("tIFR", "");
+    }
+    // Night
+    const auto dept_date = ui->doftLineEdit_2->text() + 'T' + tofb.toString(TIME_FORMAT);
+    const auto dept_date_time = QDateTime::fromString(dept_date,"yyyy-MM-ddThh:mm");
+    const auto tblk = block_minutes.toInt();
+    const auto night_angle = ASettings::read("flightlogging/nightangle").toInt();
+    const auto night_minutes = QString::number(
+                ACalc::calculateNightTime(
+                ui->deptLocLineEdit_2->text(),
+                ui->destLocLineEdit_2->text(),
+                dept_date_time,
+                tblk,
+                night_angle));
+    newData.insert("tNIGHT", night_minutes);
+
+    // Function times - This is a little explicit but these are mutually exclusive so its better to be safe than sorry here.
+    switch (ui->FunctionComboBox_2->currentIndex()) {
+    case 0://PIC
+        newData.insert("tPIC", block_minutes);
+        newData.insert("tPICUS", "");
+        newData.insert("tSIC", "");
+        newData.insert("tDUAL", "");
+        newData.insert("tFI", "");
+        break;
+    case 1://PICUS
+        newData.insert("tPIC", "");
+        newData.insert("tPICUS", block_minutes);
+        newData.insert("tSIC", "");
+        newData.insert("tDUAL", "");
+        newData.insert("tFI", "");
+        break;
+    case 2://Co-Pilot
+        newData.insert("tPIC", "");
+        newData.insert("tPICUS", "");
+        newData.insert("tSIC", block_minutes);
+        newData.insert("tDUAL", "");
+        newData.insert("tFI", "");
+        break;
+    case 3://Dual
+        newData.insert("tPIC", "");
+        newData.insert("tPICUS", "");
+        newData.insert("tSIC", "");
+        newData.insert("tDUAL", block_minutes);
+        newData.insert("tFI", "");
+        break;
+    case 4://Instructor
+        newData.insert("tPIC", "");
+        newData.insert("tPICUS", "");
+        newData.insert("tSIC", "");
+        newData.insert("tDUAL", "");
+        newData.insert("tFI", block_minutes);
+    }
+    // Pilot Flying
+    newData.insert("pilotFlying", QString::number(ui->PilotFlyingCheckBox_2->isChecked()));
+    // TO and LDG - again a bit explicit, but we  need to check for both night to day as well as day to night transitions.
+    if (ui->TakeoffCheckBox_2->isChecked()) {
+        if (night_minutes == "0") { // all day
+            newData.insert("toDay", QString::number(ui->TakeoffSpinBox_2->value()));
+            newData.insert("toNight", "0");
+        } else if (night_minutes == block_minutes) { // all night
+            newData.insert("toDay", "0");
+            newData.insert("toNight", QString::number(ui->TakeoffSpinBox_2->value()));
+        } else {
+            if(ACalc::isNight(ui->deptLocLineEdit_2->text(), dept_date_time,  night_angle)) {
+                newData.insert("toDay", "0");
+                newData.insert("toNight", QString::number(ui->TakeoffSpinBox_2->value()));
+            } else {
+                newData.insert("toDay", QString::number(ui->TakeoffSpinBox_2->value()));
+                newData.insert("toNight", "0");
+            }
+        }
+    } else {
+        newData.insert("toDay", "0");
+        newData.insert("toNight", "0");
+    }
+
+    if (ui->LandingCheckBox_2->isChecked()) {
+        if (night_minutes == "0") { // all day
+            newData.insert("ldgDay", QString::number(ui->LandingSpinBox_2->value()));
+            newData.insert("ldgNight", "0");
+        } else if (night_minutes == block_minutes) { // all night
+            newData.insert("ldgDay", "0");
+            newData.insert("ldgNight", QString::number(ui->LandingSpinBox_2->value()));
+        } else { //check
+            const auto dest_date = ui->doftLineEdit_2->text() + 'T' + tonb.toString(TIME_FORMAT);
+            const auto dest_date_time = QDateTime::fromString(dest_date,"yyyy-MM-ddThh:mm");
+            if (ACalc::isNight(ui->destLocLineEdit_2->text(), dest_date_time,  night_angle)) {
+                newData.insert("ldgDay", "0");
+                newData.insert("ldgNight", QString::number(ui->LandingSpinBox_2->value()));
+            } else {
+                newData.insert("ldgDay", QString::number(ui->LandingSpinBox_2->value()));
+                newData.insert("ldgNight", "0");
+            }
+        }
+    } else {
+        newData.insert("ldgDay", "0");
+        newData.insert("ldgNight", "0");
+    }
+
+
+    newData.insert("autoland", QString::number(ui->AutolandSpinBox_2->value()));
+    newData.insert("ApproachType", ui->ApproachComboBox_2->currentText());
+    newData.insert("FlightNumber", ui->FlightNumberLineEdit_2->text());
+    newData.insert("Remarks", ui->RemarksLineEdit_2->text());
+
+    return newData;
+}
+
+bool ExpNewFlightDialog::isLessOrEqualThanBlockTime(const QString time_string)
+{
+    if (mandatoryLineEditsGood.count(true) != 7){
+        auto message_box = QMessageBox(this);
+        message_box.setText("Unable to determine total block time.\n"
+                            "Please fill out Departure and Arrival Time\n"
+                            "before manually editing these times.");
+        message_box.exec();
+        return false;
+    }
+
+    auto extra_time = QTime::fromString(time_string,TIME_FORMAT);
+    auto block_time = ACalc::blocktime(QTime::fromString(
+                                           ui->tofbTimeLineEdit_2->text(),TIME_FORMAT),
+                                       QTime::fromString(
+                                           ui->tonbTimeLineEdit_2->text(), TIME_FORMAT));
+    if (extra_time <= block_time) {
+        return true;
+    } else {
+        auto message_box = QMessageBox(this);
+        message_box.setText("Cannot be more than Total Time of Flight:<br><br><center><b>"
+                    + block_time.toString(TIME_FORMAT)
+                    + "</b></center><br>");
+        message_box.exec();
+        return false;
     }
 }
 
@@ -408,8 +613,19 @@ void ExpNewFlightDialog::on_cancelButton_clicked()
 void ExpNewFlightDialog::on_submitButton_clicked()
 {
     DEB("Submit Button clicked.");
-    //submitFlight..
-    //accept();
+    auto newData = collectInput();
+    DEB("Setting Data for flightEntry...");
+    flightEntry.setData(newData);
+    DEB("Committing...");
+    if (!aDB()->commit(flightEntry)) {
+        auto message_box = QMessageBox(this);
+        message_box.setText("An error has ocurred. Your entry has not been saved.");
+        message_box.setIcon(QMessageBox::Warning);
+        message_box.exec();
+        return;
+        /// [F] To do: get error info and display here.
+    }
+    QDialog::accept();
 }
 
 /*
@@ -624,22 +840,21 @@ void ExpNewFlightDialog::onLocLineEdit_editingFinished(QLineEdit *line_edit, QLa
  * Time Line Edits
  */
 
-void ExpNewFlightDialog::on_tofbTimeLineEdit_2_editingFinished()
-{
-    emit timeEditingFinished(ui->tofbTimeLineEdit_2);
-}
-
-void ExpNewFlightDialog::on_tonbTimeLineEdit_2_editingFinished()
+void ExpNewFlightDialog::onTimeLineEdit_editingFinished()
 {
-    emit timeEditingFinished(ui->tonbTimeLineEdit_2);
-}
+    auto sender_object = sender();
+    auto line_edit = this->findChild<QLineEdit*>(sender_object->objectName());
+    DEB(line_edit->objectName() << "Editing Finished -" << line_edit->text());
 
-void ExpNewFlightDialog::onTimeLineEdit_editingFinished(QLineEdit *line_edit)
-{
     line_edit->setText(ACalc::formatTimeInput(line_edit->text()));
-    const auto time = QTime::fromString(line_edit->text(),"hh:mm");
+    const auto time = QTime::fromString(line_edit->text(),TIME_FORMAT);
     if(time.isValid()){
-        emit goodInputReceived(line_edit);
+        if(primaryTimeLineEdits.contains(line_edit)) {
+            emit goodInputReceived(line_edit);
+        } else { // is extra time line edit
+
+        }
+
     } else {
         emit badInputReceived(line_edit);
     }
@@ -704,3 +919,81 @@ void ExpNewFlightDialog::onPilotLineEdit_editingFinished()
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 ///                               Auto Logging Tab Slots                                        ///
 ///////////////////////////////////////////////////////////////////////////////////////////////////
+
+void ExpNewFlightDialog::on_setAsDefaultButton_2_clicked()
+{
+    writeSettings();
+}
+
+void ExpNewFlightDialog::on_restoreDefaultButton_2_clicked()
+{
+    readSettings();
+}
+
+void ExpNewFlightDialog::on_PilotFlyingCheckBox_2_stateChanged(int)
+{
+    DEB("PF checkbox state changed.");
+    if(ui->PilotFlyingCheckBox_2->isChecked()){
+        ui->TakeoffSpinBox_2->setValue(1);
+        ui->TakeoffCheckBox_2->setCheckState(Qt::Checked);
+        ui->LandingSpinBox_2->setValue(1);
+        ui->LandingCheckBox_2->setCheckState(Qt::Checked);
+
+    }else if(!ui->PilotFlyingCheckBox_2->isChecked()){
+        ui->TakeoffSpinBox_2->setValue(0);
+        ui->TakeoffCheckBox_2->setCheckState(Qt::Unchecked);
+        ui->LandingSpinBox_2->setValue(0);
+        ui->LandingCheckBox_2->setCheckState(Qt::Unchecked);
+    }
+}
+
+void ExpNewFlightDialog::on_IfrCheckBox_2_stateChanged(int)
+{
+    if (mandatoryLineEditsGood.count(true) == 7 && updateEnabled)
+        emit mandatoryLineEditsFilled();
+}
+
+void ExpNewFlightDialog::on_manualEditingCheckBox_2_stateChanged(int arg1)
+{
+    if (!(mandatoryLineEditsGood.count(true) == 7) && ui->manualEditingCheckBox_2->isChecked()) {
+        auto message_box = QMessageBox(this);
+        message_box.setText("Before editing times manually, please fill out the required fields in the flight data tab,"
+                            " so that total time can be calculated.");
+        message_box.exec();
+        ui->manualEditingCheckBox_2->setChecked(false);
+        return;
+    }
+    QList<QLineEdit*>   LE = {ui->tSPSETimeLineEdit_2, ui->tSPMETimeLineEdit_2, ui->tMPTimeLineEdit_2,    ui->tIFRTimeLineEdit_2,
+                              ui->tNIGHTTimeLineEdit_2,ui->tPICTimeLineEdit_2,  ui->tPICUSTimeLineEdit_2, ui->tSICTimeLineEdit_2,
+                              ui->tDUALTimeLineEdit_2, ui->tFITimeLineEdit_2};
+    switch (arg1) {
+    case 0:
+        for(const auto& le : LE){
+            le->setFocusPolicy(Qt::NoFocus);
+            le->setStyleSheet("");
+        }
+        updateEnabled = true;
+        if (mandatoryLineEditsGood.count(true) == 7 && updateEnabled)
+            emit mandatoryLineEditsFilled();
+        break;
+    case 2:
+        for(const auto& le : LE){
+            le->setFocusPolicy(Qt::StrongFocus);
+        }
+        updateEnabled = false;
+        break;
+    default:
+        break;
+    }
+}
+
+void ExpNewFlightDialog::on_ApproachComboBox_2_currentTextChanged(const QString &arg1)
+{
+    if(arg1 == "ILS CAT III"){  //for a CAT III approach an Autoland is mandatory, so we can preselect it.
+        ui->AutolandCheckBox_2->setCheckState(Qt::Checked);
+        ui->AutolandSpinBox_2->setValue(1);
+    }else{
+        ui->AutolandCheckBox_2->setCheckState(Qt::Unchecked);
+        ui->AutolandSpinBox_2->setValue(0);
+    }
+}

+ 17 - 7
src/experimental/expnewflightdialog.h

@@ -52,7 +52,7 @@ private slots:
     void onTextChangedToUpper(const QString&);
     void onPilotLineEdit_editingFinished();
     void onLocLineEdit_editingFinished(QLineEdit*, QLabel*);
-    void onTimeLineEdit_editingFinished(QLineEdit*);
+    void onTimeLineEdit_editingFinished();
     void onMandatoryLineEditsFilled();
     void onCompleterHighlighted(const QString&);
     void onDateClicked(const QDate &date);
@@ -68,12 +68,6 @@ private slots:
     void onInputRejected();
 /////// DEBUG
 
-    void on_tofbTimeLineEdit_2_editingFinished();
-
-    void on_tonbTimeLineEdit_2_editingFinished();
-
-
-
     void on_calendarCheckBox_stateChanged(int arg1);
 
     void on_doftLineEdit_2_editingFinished();
@@ -82,12 +76,25 @@ private slots:
 
     void on_submitButton_clicked();
 
+    void on_setAsDefaultButton_2_clicked();
+
+    void on_restoreDefaultButton_2_clicked();
+
+    void on_PilotFlyingCheckBox_2_stateChanged(int arg1);
+
+    void on_IfrCheckBox_2_stateChanged(int);
+
+    void on_manualEditingCheckBox_2_stateChanged(int arg1);
+
+    void on_ApproachComboBox_2_currentTextChanged(const QString &arg1);
+
 private:
     Ui::ExpNewFlightDialog *ui;
 
     experimental::AFlightEntry flightEntry;
 
     QList<QLineEdit*> mandatoryLineEdits;
+    QList<QLineEdit*> primaryTimeLineEdits;
 
     QBitArray mandatoryLineEditsGood;
 
@@ -105,6 +112,8 @@ private:
 
     bool updateEnabled;
 
+    bool isLessOrEqualThanBlockTime(const QString time_string);
+
     void setup();
     void readSettings();
     void writeSettings();
@@ -114,6 +123,7 @@ private:
     void setupLineEditSignalsAndSlots();
 
     void fillDeductibleData();
+    experimental::TableData collectInput();
 
 };
 

+ 11 - 1
src/experimental/expnewflightdialog.ui

@@ -1410,7 +1410,14 @@
         </widget>
        </item>
        <item row="7" column="4">
-        <widget class="QLineEdit" name="tblkTimeLineEdit_2"/>
+        <widget class="QLineEdit" name="tblkTimeLineEdit_2">
+         <property name="enabled">
+          <bool>true</bool>
+         </property>
+         <property name="focusPolicy">
+          <enum>Qt::NoFocus</enum>
+         </property>
+        </widget>
        </item>
       </layout>
      </widget>
@@ -1451,6 +1458,9 @@
      <property name="enabled">
       <bool>false</bool>
      </property>
+     <property name="whatsThis">
+      <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Add your flight to the logbook.&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;You can only perform this action if all of the mandatory fields are filled.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+     </property>
      <property name="styleSheet">
       <string notr="true">color: rgb(78, 154, 6);</string>
      </property>

+ 32 - 189
src/functions/acalc.cpp

@@ -4,76 +4,40 @@
 using namespace ACalc;
 
 /*!
- * \brief ACalc::blocktime Calculates Block Time for a given departure and arrival time
- * \param tofb QTime Time Off Blocks
- * \param tonb QTime Time On Blocks
- * \return Block Time in minutes
+ * \brief ACalc::formatTimeInput verifies user input and formats to hh:mm
+ * if the output is not a valid time, an empty string is returned. Accepts
+ * input as hh:mm, h:mm, hhmm or hmm.
+ * \param userinput from a QLineEdit
+ * \return formatted QString "hh:mm" or Empty String
  */
-QTime ACalc::blocktime(QTime tofb, QTime tonb)
+QString ACalc::formatTimeInput(QString user_input)
 {
-    QTime blocktime_out(0, 0); // initialise return value at midnight
-
-    if (tonb > tofb) { // landing same day
-        int blockseconds = tofb.secsTo(tonb);
-        blocktime_out = blocktime_out.addSecs(blockseconds);
-    } else { // landing next day
-        QTime midnight(0, 0);
-        int blockseconds = tofb.secsTo(midnight);
-        blocktime_out = blocktime_out.addSecs(blockseconds);
-        blockseconds = midnight.secsTo(tonb);
-        blocktime_out = blocktime_out.addSecs(blockseconds);
-    }
-    return blocktime_out;
-}
-
+    QString output; //
+    QTime temp_time; //empty time object is invalid by default
 
-/*!
- * \brief ACalc::minutes_to_string Converts database time to String Time
- * \param blockminutes from database
- * \return String hh:mm
- */
-QString ACalc::minutesToString(QString blockminutes)
-{
-    int minutes = blockminutes.toInt();
-    QString hour = QString::number(minutes / 60);
-    if (hour.size() < 2) {
-        hour.prepend("0");
-    }
-    QString minute = QString::number(minutes % 60);
-    if (minute.size() < 2) {
-        minute.prepend("0");
+    bool contains_seperator = user_input.contains(":");
+    if (user_input.length() == 4 && !contains_seperator) {
+        temp_time = QTime::fromString(user_input, "hhmm");
+    } else if (user_input.length() == 3 && !contains_seperator) {
+        if (user_input.toInt() < 240) { //Qtime is invalid if time is between 000 and 240 for this case
+            QString tempstring = user_input.prepend("0");
+            temp_time = QTime::fromString(tempstring, "hhmm");
+        } else {
+            temp_time = QTime::fromString(user_input, "Hmm");
+        }
+    } else if (user_input.length() == 4 && contains_seperator) {
+        temp_time = QTime::fromString(user_input, "h:mm");
+    } else if (user_input.length() == 5 && contains_seperator) {
+        temp_time = QTime::fromString(user_input, "hh:mm");
     }
-    QString blocktime = hour + ":" + minute;
-    return blocktime;
-};
 
-/*!
- * \brief ACalc::time_to_minutes converts QTime to int minutes
- * \param time QTime
- * \return int time as number of minutes
- */
-int ACalc::QTimeToMinutes(QTime time)
-{
-    QString timestring = time.toString("hh:mm");
-    int minutes = (timestring.left(2).toInt()) * 60;
-    minutes += timestring.right(2).toInt();
-    return minutes;
-}
-
-/*!
- * \brief ACalc::string_to_minutes Converts String Time to String Number of Minutes
- * \param timestring "hh:mm"
- * \return String number of minutes
- */
-int ACalc::stringToMinutes(QString timestring)
-{
-    int minutes = (timestring.left(2).toInt()) * 60;
-    minutes += timestring.right(2).toInt();
-    timestring = QString::number(minutes);
-    return minutes;
+    output = temp_time.toString("hh:mm");
+    if (output.isEmpty()) {
+        qDebug() << "Time input is invalid.";
+    }
+    return output;
 }
 
-
 /*!
  * The purpose of the following functions is to provide functionality enabling the Calculation of
  * night flying time. EASA defines night as follows:
@@ -98,47 +62,7 @@ int ACalc::stringToMinutes(QString timestring)
  * Radians.
  */
 
-/*!
- * \brief radToDeg Converts radians to degrees
- * \param rad
- * \return degrees
- */
-double ACalc::radToDeg(double rad)
-{
-    double deg = rad * (180 / M_PI);
-    return deg;
-}
-
-/*!
- * \brief degToRad Converts degrees to radians
- * \param deg
- * \return radians
- */
-double ACalc::degToRad(double deg)
-{
-    double rad = deg * (M_PI / 180);
-    return rad;
-}
-
-/*!
- * \brief radToNauticalMiles Convert Radians to nautical miles
- * \param rad
- * \return nautical miles
- */
-double ACalc::radToNauticalMiles(double rad)
-{
-    double nm = rad * 3440.06479482;
-    return nm;
-}
 
-/*!
- * \brief greatCircleDistance Calculates Great Circle distance between two coordinates, return in Radians.
- * \param lat1 Location Latitude in degrees -90:90 ;S(-) N(+)
- * \param lon1 Location Longitude in degrees -180:180 W(-) E(+)
- * \param lat2 Location Latitude in degrees -90:90 ;S(-) N(+)
- * \param lon2 Location Longitude in degrees -180:180 W(-) E(+)
- * \return
- */
 double ACalc::greatCircleDistance(double lat1, double lon1, double lat2, double lon2)
 {
     // Converting Latitude and Longitude to Radians
@@ -157,13 +81,7 @@ double ACalc::greatCircleDistance(double lat1, double lon1, double lat2, double
     return result;
 }
 
-/*!
- * \brief ACalc::greatCircleDistanceBetweenAirports Calculates Great
- * Circle distance between two coordinates, return in nautical miles.
- * \param dept ICAO 4-letter Airport Identifier
- * \param dest ICAO 4-letter Airport Identifier
- * \return Nautical Miles From Departure to Destination
- */
+
 double ACalc::greatCircleDistanceBetweenAirports(QString dept, QString dest)
 {
     QVector<QString> dept_coordinates = Db::multiSelect({"lat", "long"}, "airports", "icao", dept,
@@ -192,16 +110,7 @@ double ACalc::greatCircleDistanceBetweenAirports(QString dept, QString dest)
     return radToNauticalMiles(result);
 }
 
-/*!
- * \brief  Calculates a list of points (lat,lon) along the Great Circle between two points.
- * The points are spaced equally, one minute of block time apart.
- * \param lat1 Location Latitude in degrees -90:90 ;S(-) N(+)
- * \param lon1 Location Longitude in degrees -180:180 W(-) E(+)
- * \param lat2 Location Latitude in degrees -90:90 ;S(-) N(+)
- * \param lon2 Location Longitude in degrees -180:180 W(-) E(+)
- * \param tblk Total Blocktime in minutes
- * \return coordinates {lat,lon} along the Great Circle Track
- */
+
 QVector<QVector<double>> ACalc::intermediatePointsOnGreatCircle(double lat1, double lon1,
                                                                double lat2, double lon2, int tblk)
 {
@@ -232,22 +141,7 @@ QVector<QVector<double>> ACalc::intermediatePointsOnGreatCircle(double lat1, dou
     return coordinates;
 }
 
-/*!
- * \brief Calculates solar elevation angle for a given point in time and latitude/longitude coordinates
- *
- * It is based on the formulas found here: http://stjarnhimlen.se/comp/tutorial.html#5
- *
- * Credit also goes to Darin C. Koblick for his matlab implementation of various of these
- * formulas and to Kevin Godden for porting it to C++.
- *
- * Darin C. Koblock: https://www.mathworks.com/matlabcentral/profile/authors/1284781
- * Kevin Godden: https://www.ridgesolutions.ie/index.php/about-us/
- *
- * \param utc_time_point - QDateTime (UTC) for which the elevation is Calculated
- * \param lat - Location Latitude in degrees -90:90 ;S(-) N(+)
- * \param lon - Location Longitude in degrees -180:180 W(-) E(+)
- * \return elevation - double of solar elevation in degrees.
- */
+
 double ACalc::solarElevation(QDateTime utc_time_point, double lat, double lon)
 {
     double Alt =
@@ -307,16 +201,7 @@ double ACalc::solarElevation(QDateTime utc_time_point, double lat, double lon)
     return elevation;
 }
 
-/*!
- * \brief Calculates which portion of a flight was conducted in night conditions.
- * \param dept - ICAO 4-letter code of Departure Airport
- * \param dest - ICAO 4-letter Code of Destination Airport
- * \param departureTime - QDateTime of Departure (UTC)
- * \param tblk - Total block time in minutes
- * \param nightAngle - the solar elevation angle where night conditons exist.
- * Default -6 (end of civil evening twilight)
- * \return Total number of minutes under night flying conditions
- */
+
 int ACalc::calculateNightTime(const QString &dept, const QString &dest, QDateTime departureTime, int tblk, int nightAngle)
 {
     QVector<QString> dept_coordinates = Db::multiSelect({"lat", "long"}, "airports", "icao", dept,
@@ -334,12 +219,7 @@ int ACalc::calculateNightTime(const QString &dept, const QString &dest, QDateTim
     double dept_lon = degToRad(dept_coordinates[1].toDouble());
     double dest_lat = degToRad(dest_coordinates[0].toDouble());
     double dest_lon = degToRad(dest_coordinates[1].toDouble());
-/*
-    qDebug() << "ACalc::CalculateNightTime deptLat = " << deptLat;
-    qDebug() << "ACalc::CalculateNightTime deptLon = " << deptLon;
-    qDebug() << "ACalc::CalculateNightTime destLat = " << destLat;
-    qDebug() << "ACalc::CalculateNightTime destLon = " << destLon;
-*/
+
     QVector<QVector<double>> route = intermediatePointsOnGreatCircle(dept_lat, dept_lon, dest_lat, dest_lon,
                                                                      tblk);
 
@@ -350,8 +230,6 @@ int ACalc::calculateNightTime(const QString &dept, const QString &dest, QDateTim
             night_time ++;
         }
     }
-    //qDebug() << "ACalc::CalculateNightTime result for angle: "<< nightAngle
-    //         << " :" << nightTime << " minutes night flying time.";
     return night_time;
 }
 
@@ -374,41 +252,6 @@ bool ACalc::isNight(QString icao, QDateTime event_time, int nightAngle)
     }
 }
 
-/*!
- * \brief ACalc::formatTimeInput verifies user input and formats to hh:mm
- * if the output is not a valid time, an empty string is returned. Accepts
- * input as hh:mm, h:mm, hhmm or hmm.
- * \param userinput from a QLineEdit
- * \return formatted QString "hh:mm" or Empty String
- */
-QString ACalc::formatTimeInput(QString user_input)
-{
-    QString output; //
-    QTime temp_time; //empty time object is invalid by default
-
-    bool contains_seperator = user_input.contains(":");
-    if (user_input.length() == 4 && !contains_seperator) {
-        temp_time = QTime::fromString(user_input, "hhmm");
-    } else if (user_input.length() == 3 && !contains_seperator) {
-        if (user_input.toInt() < 240) { //Qtime is invalid if time is between 000 and 240 for this case
-            QString tempstring = user_input.prepend("0");
-            temp_time = QTime::fromString(tempstring, "hhmm");
-        } else {
-            temp_time = QTime::fromString(user_input, "Hmm");
-        }
-    } else if (user_input.length() == 4 && contains_seperator) {
-        temp_time = QTime::fromString(user_input, "h:mm");
-    } else if (user_input.length() == 5 && contains_seperator) {
-        temp_time = QTime::fromString(user_input, "hh:mm");
-    }
-
-    output = temp_time.toString("hh:mm");
-    if (output.isEmpty()) {
-        qDebug() << "Time input is invalid.";
-    }
-    return output;
-}
-
 /*!
  * \brief ACalc::updateAutoTimes When the details of an aircraft are changed,
  * this function recalculates deductable times for this aircraft and updates

+ 146 - 9
src/functions/acalc.h

@@ -16,32 +16,169 @@
 
 namespace ACalc {
 
-QTime blocktime(QTime tofb, QTime tonb);
-
-QString minutesToString(QString blockminutes);
+/*!
+ * \brief ACalc::blocktime Calculates Block Time for a given departure and arrival time
+ * \param tofb QTime Time Off Blocks
+ * \param tonb QTime Time On Blocks
+ * \return Block Time in minutes
+ */
+inline QTime blocktime(QTime tofb, QTime tonb)
+{
+    QTime blocktime_out(0, 0); // initialise return value at midnight
+
+    if (tonb > tofb) { // landing same day
+        int blockseconds = tofb.secsTo(tonb);
+        blocktime_out = blocktime_out.addSecs(blockseconds);
+    } else { // landing next day
+        QTime midnight(0, 0);
+        int blockseconds = tofb.secsTo(midnight);
+        blocktime_out = blocktime_out.addSecs(blockseconds);
+        blockseconds = midnight.secsTo(tonb);
+        blocktime_out = blocktime_out.addSecs(blockseconds);
+    }
+    return blocktime_out;
+}
+/*!
+ * \brief ACalc::minutes_to_string Converts database time to String Time
+ * \param blockminutes from database
+ * \return String hh:mm
+ */
+inline QString minutesToString(QString blockminutes)
+{
+    int minutes = blockminutes.toInt();
+    QString hour = QString::number(minutes / 60);
+    if (hour.size() < 2) {
+        hour.prepend("0");
+    }
+    QString minute = QString::number(minutes % 60);
+    if (minute.size() < 2) {
+        minute.prepend("0");
+    }
+    QString blocktime = hour + ":" + minute;
+    return blocktime;
+};
 
-int stringToMinutes(QString time);
+/*!
+ * \brief ACalc::time_to_minutes converts QTime to int minutes
+ * \param time QTime
+ * \return int time as number of minutes
+ */
+inline int QTimeToMinutes(QTime time)
+{
+    QString timestring = time.toString("hh:mm");
+    int minutes = (timestring.left(2).toInt()) * 60;
+    minutes += timestring.right(2).toInt();
+    return minutes;
+}
 
-int QTimeToMinutes(QTime time);
+/*!
+ * \brief ACalc::string_to_minutes Converts String Time to String Number of Minutes
+ * \param timestring "hh:mm"
+ * \return String number of minutes
+ */
+inline int stringToMinutes(QString timestring)
+{
+    int minutes = (timestring.left(2).toInt()) * 60;
+    minutes += timestring.right(2).toInt();
+    timestring = QString::number(minutes);
+    return minutes;
+}
 
-double radToDeg(double rad);
+/*!
+ * \brief radToDeg Converts radians to degrees
+ * \param rad
+ * \return degrees
+ */
+inline double radToDeg(double rad)
+{
+    double deg = rad * (180 / M_PI);
+    return deg;
+}
 
-double degToRad(double deg);
+/*!
+ * \brief degToRad Converts degrees to radians
+ * \param deg
+ * \return radians
+ */
+inline double degToRad(double deg)
+{
+    double rad = deg * (M_PI / 180);
+    return rad;
+}
 
-double radToNauticalMiles(double rad);
+/*!
+ * \brief radToNauticalMiles Convert Radians to nautical miles
+ * \param rad
+ * \return nautical miles
+ */
+inline double radToNauticalMiles(double rad)
+{
+    double nm = rad * 3440.06479482;
+    return nm;
+}
 
+/*!
+ * \brief greatCircleDistance Calculates Great Circle distance between two coordinates, return in Radians.
+ * \param lat1 Location Latitude in degrees -90:90 ;S(-) N(+)
+ * \param lon1 Location Longitude in degrees -180:180 W(-) E(+)
+ * \param lat2 Location Latitude in degrees -90:90 ;S(-) N(+)
+ * \param lon2 Location Longitude in degrees -180:180 W(-) E(+)
+ * \return
+ */
 double greatCircleDistance(double lat1, double lon1, double lat2, double lon2);
 
+/*!
+ * \brief ACalc::greatCircleDistanceBetweenAirports Calculates Great
+ * Circle distance between two coordinates, return in nautical miles.
+ * \param dept ICAO 4-letter Airport Identifier
+ * \param dest ICAO 4-letter Airport Identifier
+ * \return Nautical Miles From Departure to Destination
+ */
 double greatCircleDistanceBetweenAirports(QString dept, QString dest);
 
+/*!
+ * \brief  Calculates a list of points (lat,lon) along the Great Circle between two points.
+ * The points are spaced equally, one minute of block time apart.
+ * \param lat1 Location Latitude in degrees -90:90 ;S(-) N(+)
+ * \param lon1 Location Longitude in degrees -180:180 W(-) E(+)
+ * \param lat2 Location Latitude in degrees -90:90 ;S(-) N(+)
+ * \param lon2 Location Longitude in degrees -180:180 W(-) E(+)
+ * \param tblk Total Blocktime in minutes
+ * \return coordinates {lat,lon} along the Great Circle Track
+ */
 QVector<QVector<double>> intermediatePointsOnGreatCircle(double lat1,
                                                          double lon1,
                                                          double lat2,
                                                          double lon2,
                                                          int tblk);
-
+/*!
+ * \brief Calculates solar elevation angle for a given point in time and latitude/longitude coordinates
+ *
+ * It is based on the formulas found here: http://stjarnhimlen.se/comp/tutorial.html#5
+ *
+ * Credit also goes to Darin C. Koblick for his matlab implementation of various of these
+ * formulas and to Kevin Godden for porting it to C++.
+ *
+ * Darin C. Koblock: https://www.mathworks.com/matlabcentral/profile/authors/1284781
+ * Kevin Godden: https://www.ridgesolutions.ie/index.php/about-us/
+ *
+ * \param utc_time_point - QDateTime (UTC) for which the elevation is Calculated
+ * \param lat - Location Latitude in degrees -90:90 ;S(-) N(+)
+ * \param lon - Location Longitude in degrees -180:180 W(-) E(+)
+ * \return elevation - double of solar elevation in degrees.
+ */
 double solarElevation(QDateTime utc_time_point, double lat, double lon);
 
+/*!
+ * \brief Calculates which portion of a flight was conducted in night conditions.
+ * \param dept - ICAO 4-letter code of Departure Airport
+ * \param dest - ICAO 4-letter Code of Destination Airport
+ * \param departureTime - QDateTime of Departure (UTC)
+ * \param tblk - Total block time in minutes
+ * \param nightAngle - the solar elevation angle where night conditons exist.
+ * Default -6 (end of civil evening twilight)
+ * \return Total number of minutes under night flying conditions
+ */
 int calculateNightTime(const QString &dept, const QString &dest, QDateTime departureTime, int tblk, int nightAngle);
 
 bool isNight(QString icao, QDateTime event_time, int nightAngle);