Browse Source

New Time Class

Added a new time class to allow values greater than 24h. The input verification of a time input assumes that an input is of the form hh:mm and represents a time of day (i.e. within 00:00 to 23:59) but the total times can exceed this value. The new Time class caters for this.
Felix Turowsky 1 year ago
parent
commit
53b8ebf615
6 changed files with 136 additions and 59 deletions
  1. 3 0
      .gitignore
  2. 2 0
      CMakeLists.txt
  3. 41 0
      src/classes/time.cpp
  4. 50 0
      src/classes/time.h
  5. 2 2
      src/gui/verification/timeinput.cpp
  6. 38 57
      src/gui/widgets/totalswidget.cpp

+ 3 - 0
.gitignore

@@ -83,6 +83,9 @@ openPilotLog.vcxproj.filters
 
 openPilotLog.vcxproj.user
 
+.vscode/
+build/
+
 # Disable localization updates
 l10n/
 # DS files

+ 2 - 0
CMakeLists.txt

@@ -114,6 +114,8 @@ set(PROJECT_SOURCES
     src/classes/jsonhelper.cpp
     src/classes/md5sum.h
     src/classes/md5sum.cpp
+    src/classes/time.h
+    src/classes/time.cpp
 
     # Namespaces
     src/functions/calc.h

+ 41 - 0
src/classes/time.cpp

@@ -0,0 +1,41 @@
+#include "time.h"
+
+namespace OPL {
+
+Time::Time()
+{
+}
+
+bool Time::isValidTimeOfDay()
+{
+    return m_minutes <= MINUTES_PER_DAY;
+}
+
+const QString Time::toString() const
+{
+    // convert to hh:mm
+    return QString::number(m_minutes / 60).rightJustified(2, '0') + QLatin1Char(':') + QString::number(m_minutes % 60).rightJustified(2, '0');
+}
+
+int32_t Time::toMinutes()
+{
+    return m_minutes;
+}
+
+Time Time::fromString(const QString &timeString)
+{
+    const QStringList parts = timeString.split(QChar(':'));
+    if(parts.size() < 2)
+        return {};
+
+
+    int32_t hours = parts[0].toInt();
+    int32_t minutes = parts[1].toInt();
+
+    if(hours < 0 || minutes < 0)
+        return{};
+
+    return Time(hours * 60 + minutes);
+}
+
+} // namespace OPL

+ 50 - 0
src/classes/time.h

@@ -0,0 +1,50 @@
+#ifndef TIME_H
+#define TIME_H
+
+#include <QtCore>
+namespace OPL {
+
+/**
+ * \brief The Time class handles conversions between user input / user-facing time data display and
+ * database format.
+ * \details Time data in the database is stored as an integer value of minutes, whereas the user-facing
+ * time data is normally displayed in the Qt::ISODATE format. A database value of 72 would for example be
+ * displayed as 01:12 (1 hour and 12 minutes).
+ */
+class Time
+{
+private:
+    const static inline int MINUTES_PER_DAY = 24 * 60;
+    int32_t m_minutes = 0;
+
+public:
+    Time();
+    Time(int32_t minutes) : m_minutes(minutes) {};
+
+    /**
+     * @brief isValidTimeOfDay - determines whether the instance can be converted to a time hh:mm
+     * @return true if the total amount of minutes does not exceed one day.
+     */
+    bool isValidTimeOfDay();
+
+    /**
+     * @brief toString returns the time as hh:mm
+     */
+    const QString toString() const;
+
+    /**
+     * @brief toMinutes - returns the number of minutes in the time Object
+     */
+    int32_t toMinutes();
+
+    /**
+     * @brief fromString create a Time Object from a String formatted as hh:mm
+     * @param timeString the input string
+     * @return the Time Object corresponding to the string, equivalent to 0 minutes if conversion fails.
+     */
+    static Time fromString(const QString& timeString);
+};
+
+}// namespace OPL
+
+#endif // TIME_H

+ 2 - 2
src/gui/verification/timeinput.cpp

@@ -18,13 +18,13 @@ QString TimeInput::fixup() const
 
     if (input.contains(':')) { // contains seperator
         if(input.length() == 4)
-            fixed.prepend('0');
+            fixed.prepend(QLatin1Char('0'));
     } else { // does not contain seperator
         if(input.length() == 4) {
             fixed.insert(2, ':');
         }
         if(input.length() == 3) {
-            fixed.prepend('0');
+            fixed.prepend(QLatin1Char('0'));
             fixed.insert(2, ':');
         }
     }

+ 38 - 57
src/gui/widgets/totalswidget.cpp

@@ -3,7 +3,7 @@
 #include "src/database/database.h"
 #include "src/gui/verification/timeinput.h"
 #include "src/opl.h"
-#include "src/functions/time.h"
+#include "src/classes/time.h"
 #include "src/database/row.h"
 #include "ui_totalswidget.h"
 
@@ -95,8 +95,8 @@ void TotalsWidget::fillTotals(const WidgetType widgetType)
                 line_edit->setText(field.toString());
             } else {
                 // line edits for total time
-                const QString time_string = OPL::Time::toString(field.toInt());
-                line_edit->setText(time_string);
+                OPL::Time time = OPL::Time(field.toInt());// = Time(field.toInt());
+                line_edit->setText(time.toString());
             }
         }
 
@@ -104,7 +104,7 @@ void TotalsWidget::fillTotals(const WidgetType widgetType)
 }
 
 /**
- * @brief TotalsWidget::fillStubData Fills the row entry object with stub data if it is empty.
+ * \brief TotalsWidget::fillStubData Fills the row entry object with stub data if it is empty.
  * \details The Widget retreives previous experience from the database. If the user has not entered
  * any previous experience this database entry does not exist. The database returns an empty
  * row object in this case. In order for the user to be able to fill in the previous experience,
@@ -165,43 +165,6 @@ void TotalsWidget::connectSignalsAndSlots()
             this, &TotalsWidget::movementLineEditEditingFinished);
 }
 
-/*!
- * \brief TotalsWidget::verifyUserTimeInput verify the user input is correct or can be fixed
- * \param line_edit the line edit that has been edited
- * \param input the user input
- * \return if the input is valid or can be fixed
- */
-bool TotalsWidget::verifyUserTimeInput(QLineEdit *line_edit, const TimeInput &input)
-{
-    if(!input.isValid()) {
-        // try to fix
-        QString fixed = input.fixup();
-        if(fixed == QString()) {
-            WARN(tr("Invalid input. Please use the following format for time:<br><br><tt>hh:mm</tt>"));
-            return false;
-        } else {
-            line_edit->setText(fixed);
-            return true;
-        }
-    }
-    return true;
-}
-
-/*!
- * \brief TotalsWidget::updateTimeEntry Updates the DB with a time entry
- * \param line_edit The time line edit that has been edited
- * \return true on success
- */
-bool TotalsWidget::updateTimeEntry(const QLineEdit* line_edit) {
-    const QString db_field = line_edit->objectName().remove(QLatin1String("LineEdit"));
-    const QVariant value = OPL::Time::toMinutes(line_edit->text());
-
-    m_rowData.insert(db_field, value);
-
-    const auto previous_experience = OPL::FlightEntry(TOTALS_DATA_ROW_ID, m_rowData);
-    return DB->commit(previous_experience);
-}
-
 /*!
  * \brief TotalsWidget::updateMovementEntry Updates the DB with a movement (TO or LDG) entry
  * \param line_edit The line edit that has been edited
@@ -209,38 +172,56 @@ bool TotalsWidget::updateTimeEntry(const QLineEdit* line_edit) {
  */
 bool TotalsWidget::updateMovementEntry(const QLineEdit *line_edit)
 {
-    const QString db_field = line_edit->objectName().remove(QLatin1String("LineEdit"));
-    const QVariant value = line_edit->text().toInt();
 
-    m_rowData.insert(db_field, value);
-
-    const auto previous_experience = OPL::FlightEntry(TOTALS_DATA_ROW_ID, m_rowData);
-    return DB->commit(previous_experience);
 }
 
 void TotalsWidget::timeLineEditEditingFinished()
 {
     LOG << sender()->objectName() + "Editing finished.";
     QLineEdit* line_edit = this->findChild<QLineEdit*>(sender()->objectName());
+    const QString& text = line_edit->text();
 
-    // verify and if possible fix the user input
-    if (!verifyUserTimeInput(line_edit, TimeInput(line_edit->text()))) {
-        // (re-) set to previous value from DB
-        WARN(tr("Invalid time entry. Please use the following format:<br><br><tt> hh:mm </tt>"));
-        int old_value = m_rowData.value(line_edit->objectName().remove(QLatin1String("LineEdit"))).toInt();
-        line_edit->setText(OPL::Time::toString(old_value));
+    // make sure the input is usable
+    if(!text.contains(QChar(':'))) {
+        WARN(tr("Please enter the time as: <br><br> hh:mm"));
+        line_edit->setText(QString());
         return;
     }
 
     // write the updated value to the database
-    updateTimeEntry(line_edit);
+    const QString db_field = line_edit->objectName().remove(QLatin1String("LineEdit"));
+    const QVariant value = OPL::Time::fromString(line_edit->text()).toMinutes();
 
+    m_rowData.insert(db_field, value);
+    LOG << "Added row data: " + db_field + ": " + value.toString();
+
+    const auto previous_experience = OPL::FlightEntry(TOTALS_DATA_ROW_ID, m_rowData);
+    DB->commit(previous_experience);
+
+    // Read back the value and set the line edit to confirm input is correct and provide user feedback
+    m_rowData = DB->getRowData(OPL::DbTable::Flights, TOTALS_DATA_ROW_ID);
+    OPL::Time new_time = OPL::Time(m_rowData.value(db_field).toInt());
+    line_edit->setText(new_time.toString());
 }
 
 void TotalsWidget::movementLineEditEditingFinished()
 {
-    // input validation is already done by the QValidator
-    LOG << sender()->objectName() + "Editing finished.";
-    updateMovementEntry(this->findChild<QLineEdit*>(sender()->objectName()));
+    // input validation is done by the QValidator
+    QLineEdit* line_edit = this->findChild<QLineEdit*>(sender()->objectName());
+    LOG << line_edit->objectName() + "Editing finished.";
+
+    // extract the value from the input and update the DB
+    const QString db_field = line_edit->objectName().remove(QLatin1String("LineEdit"));
+    const QVariant value = line_edit->text().toInt();
+
+    m_rowData.insert(db_field, value);
+
+    const auto previous_experience = OPL::FlightEntry(TOTALS_DATA_ROW_ID, m_rowData);
+    DB->commit(previous_experience);
+
+    // read back the value and set the line edit to the retreived value to give user feedback
+    m_rowData = DB->getRowData(OPL::DbTable::Flights, TOTALS_DATA_ROW_ID);
+    const QString new_value = QString::number(m_rowData.value(db_field).toInt());
+    line_edit->setText(new_value);
 }