Browse Source

Some work on Date Formats

Carved out the workings of the ADate functions (input verification for user-provided date inputs)
Added a check for a valid database at application launch with options to restore a backup or create a new database if no valid database has been found.
Felix Turo 3 years ago
parent
commit
9fb307a39c

+ 63 - 3
mainwindow.cpp

@@ -21,6 +21,14 @@
 #include "src/database/adatabase.h"
 #include "src/classes/astyle.h"
 
+// Quick and dirty Debug area
+#include "src/gui/dialogues/firstrundialog.h"
+void MainWindow::doDebugStuff()
+{
+    auto fr = new FirstRunDialog(this);
+    fr->exec();
+}
+
 MainWindow::MainWindow(QWidget *parent)
     : QMainWindow(parent)
     , ui(new Ui::MainWindow)
@@ -29,9 +37,20 @@ MainWindow::MainWindow(QWidget *parent)
     // connect to the Database
     QFileInfo database_file(AStandardPaths::directory(AStandardPaths::Database).
                                          absoluteFilePath(QStringLiteral("logbook.db")));
-            if (!database_file.exists()) {
-                WARN(tr("Error: Database file not found."));
-            }
+    bool db_invalid = false;
+    if (!database_file.exists()) {
+        WARN(tr("Error: Database file not found."));
+        db_invalid = true;
+    }
+    if (database_file.size() == 0) {
+        WARN(tr("Database file invalid."));
+        db_invalid = true;
+    }
+
+    if (db_invalid)
+        onDatabaseInvalid();
+
+
     if(!aDB->connect()){
         WARN(tr("Error establishing database connection."));
     }
@@ -141,6 +160,47 @@ void MainWindow::connectWidgets()
                      aircraftWidget,  &AircraftWidget::repopulateModel);
 }
 
+void MainWindow::onDatabaseInvalid()
+{
+    QMessageBox db_error(this);
+    //db_error.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
+    db_error.addButton(tr("Restore Backup"), QMessageBox::ButtonRole::AcceptRole);
+    db_error.addButton(tr("Create New Database"), QMessageBox::ButtonRole::RejectRole);
+    db_error.addButton(tr("Abort"), QMessageBox::ButtonRole::DestructiveRole);
+    db_error.setIcon(QMessageBox::Warning);
+    db_error.setWindowTitle(tr("No valid database found"));
+    db_error.setText(tr("No valid database has been found.<br>"
+                       "Would you like to create a new database or import a backup?"));
+
+    int ret = db_error.exec();
+    if (ret == QMessageBox::DestructiveRole) {
+        DEB << "No valid database found. Exiting.";
+        on_actionQuit_triggered();
+    } else if (ret == QMessageBox::ButtonRole::AcceptRole) {
+        DEB << "Yes(Import Backup)";
+        QString db_path = QFileDialog::getOpenFileName(this,
+                                                       tr("Select Database"),
+                                                       AStandardPaths::directory(AStandardPaths::Backup).canonicalPath(),
+                                                       tr("Database file (*.db)"));
+        if (!db_path.isEmpty()) {
+            if(!aDB->restoreBackup(db_path)) {
+               WARN(tr("Unable to restore Backup file: %1").arg(db_path));
+               on_actionQuit_triggered();
+            } else {
+                INFO(tr("Backup successfully restored."));
+            }
+        }
+    } else if (ret == QMessageBox::ButtonRole::RejectRole){
+        DEB << "No(Create New)";
+        if(FirstRunDialog().exec() == QDialog::Rejected){
+            LOG << "Initial setup incomplete or unsuccessfull.";
+            on_actionQuit_triggered();
+        }
+        ASettings::write(ASettings::Main::SetupComplete, true);
+        LOG << "Initial Setup Completed successfully";
+    }
+}
+
 /*
  * Slots
  */

+ 11 - 2
mainwindow.h

@@ -98,22 +98,31 @@ private:
 
     DebugWidget* debugWidget;
 
+    // Completion Data for QCompleters and Mapping
+    ACompletionData completionData;
+
     void nope();
 
     void connectWidgets();
 
-    // Completion Data for QCompleters and Mapping
-    ACompletionData completionData;
+    // Prompts the user to fix a broken database or import a backup
+    void onDatabaseInvalid();
+
+    //
+    void doDebugStuff();
 
 protected:
     /*!
      * \brief Shows the debug widget by pressing <ctrl + t>
+     * <Shift+Enter for a quick and dirty debug>
      */
     void keyPressEvent(QKeyEvent* keyEvent) override
     {
         if(keyEvent->type() == QKeyEvent::KeyPress) {
             if(keyEvent->matches(QKeySequence::AddTab)) {
                 on_actionDebug_triggered();
+            } else if (keyEvent->matches(QKeySequence::InsertLineSeparator)) {
+                doDebugStuff();
             }
         }
     }

+ 106 - 10
src/functions/adate.cpp

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

+ 18 - 2
src/functions/adate.h

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

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

@@ -66,6 +66,8 @@ FirstRunDialog::FirstRunDialog(QWidget *parent) :
                     ADate::getFormatString(Opl::Date::ADateFormat::ISODate));
         date_edit->setDate(QDate::currentDate());
     }
+    // De-activate non-default date formats for now, implement in future version
+    ui->dateFormatComboBox->setVisible(false);
     // Debug - use ctrl + t to enable branchLineEdit to select from which git branch the templates are pulled
     ui->branchLineEdit->setVisible(false);
 }

+ 62 - 62
src/gui/dialogues/firstrundialog.ui

@@ -31,7 +31,7 @@
    <item row="1" column="0" colspan="2">
     <widget class="QStackedWidget" name="stackedWidget">
      <property name="currentIndex">
-      <number>4</number>
+      <number>1</number>
      </property>
      <widget class="QWidget" name="personalDataPage">
       <layout class="QGridLayout" name="gridLayout_9">
@@ -173,6 +173,67 @@
      </widget>
      <widget class="QWidget" name="currencyPage">
       <layout class="QGridLayout" name="gridLayout_6">
+       <item row="4" column="0" colspan="2">
+        <layout class="QHBoxLayout" name="horizontalLayout">
+         <item>
+          <widget class="QCheckBox" name="currWarningCheckBox">
+           <property name="text">
+            <string>Warn me about expiring currencies</string>
+           </property>
+           <property name="checked">
+            <bool>true</bool>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QSpinBox" name="currWarningThresholdSpinBox">
+           <property name="maximum">
+            <number>365</number>
+           </property>
+           <property name="value">
+            <number>30</number>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QLabel" name="label_3">
+           <property name="text">
+            <string>days before expiry</string>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </item>
+       <item row="0" column="0" colspan="2">
+        <widget class="QLabel" name="label_6">
+         <property name="text">
+          <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-size:20pt;&quot;&gt;Welcome to openPilotLog!&lt;/span&gt;&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-size:14pt;&quot;&gt;The Free and Open Source Logbook application&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;If you would like to keep track of your license and/or medical expiration dates, you can enter them here. By default, only Take-Off and Landing currency is shown on the home page, but any other currency can also be shown as well. This can be enabled in the settings.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+         </property>
+         <property name="textFormat">
+          <enum>Qt::RichText</enum>
+         </property>
+         <property name="wordWrap">
+          <bool>true</bool>
+         </property>
+        </widget>
+       </item>
+       <item row="1" column="1">
+        <widget class="QComboBox" name="dateFormatComboBox"/>
+       </item>
+       <item row="2" column="0" colspan="2">
+        <widget class="Line" name="line">
+         <property name="orientation">
+          <enum>Qt::Horizontal</enum>
+         </property>
+        </widget>
+       </item>
+       <item row="1" column="0">
+        <widget class="QLabel" name="dateFormatLabel">
+         <property name="text">
+          <string>Date Format</string>
+         </property>
+        </widget>
+       </item>
        <item row="3" column="0" colspan="2">
         <layout class="QGridLayout" name="gridLayout_5">
          <item row="0" column="1">
@@ -432,67 +493,6 @@
          </item>
         </layout>
        </item>
-       <item row="1" column="1">
-        <widget class="QComboBox" name="dateFormatComboBox"/>
-       </item>
-       <item row="0" column="0" colspan="2">
-        <widget class="QLabel" name="label_6">
-         <property name="text">
-          <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-size:20pt;&quot;&gt;Welcome to openPilotLog!&lt;/span&gt;&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-size:14pt;&quot;&gt;The Free and Open Source Logbook application&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;If you would like to keep track of your license and/or medical expiration dates, you can enter them here. By default, only Take-Off and Landing currency is shown on the home page, but any other currency can also be shown as well. This can be enabled in the settings.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
-         </property>
-         <property name="textFormat">
-          <enum>Qt::RichText</enum>
-         </property>
-         <property name="wordWrap">
-          <bool>true</bool>
-         </property>
-        </widget>
-       </item>
-       <item row="1" column="0">
-        <widget class="QLabel" name="dateFormatLabel">
-         <property name="text">
-          <string>Date Format</string>
-         </property>
-        </widget>
-       </item>
-       <item row="4" column="0" colspan="2">
-        <layout class="QHBoxLayout" name="horizontalLayout">
-         <item>
-          <widget class="QCheckBox" name="currWarningCheckBox">
-           <property name="text">
-            <string>Warn me about expiring currencies</string>
-           </property>
-           <property name="checked">
-            <bool>true</bool>
-           </property>
-          </widget>
-         </item>
-         <item>
-          <widget class="QSpinBox" name="currWarningThresholdSpinBox">
-           <property name="maximum">
-            <number>365</number>
-           </property>
-           <property name="value">
-            <number>30</number>
-           </property>
-          </widget>
-         </item>
-         <item>
-          <widget class="QLabel" name="label_3">
-           <property name="text">
-            <string>days before expiry</string>
-           </property>
-          </widget>
-         </item>
-        </layout>
-       </item>
-       <item row="2" column="0" colspan="2">
-        <widget class="Line" name="line">
-         <property name="orientation">
-          <enum>Qt::Horizontal</enum>
-         </property>
-        </widget>
-       </item>
       </layout>
      </widget>
      <widget class="QWidget" name="flightLoggingPage">

+ 4 - 2
src/gui/dialogues/newflightdialog.cpp

@@ -87,6 +87,7 @@ void NewFlightDialog::setup()
     ui->flightDataTabWidget->removeTab(2); // hide calendar widget
     ui->doftLineEdit->setText(QDate::currentDate().toString(Qt::ISODate));
     emit ui->doftLineEdit->editingFinished();
+    ui->doftLineEdit->setToolTip(tr("Date Format: %1").arg(ADate::getFormatString(Opl::Date::ADateFormat::ISODate)));
     ui->deptLocLineEdit->setFocus();
 }
 void NewFlightDialog::readSettings()
@@ -909,9 +910,9 @@ void NewFlightDialog::on_doftLineEdit_editingFinished()
     auto label = ui->doftDisplayLabel;
     //DEB << line_edit->objectName() << "Editing finished - " << text;
 
-    TODO << "Implement other Date Formats";
+    TODO << "Non-default Date formats not implemented yet.";
     Opl::Date::ADateFormat date_format = Opl::Date::ADateFormat::ISODate;
-    auto date = ADate::formatInput(text, date_format);
+    auto date = ADate::parseInput(text, date_format);
     if (date.isValid()) {
         label->setText(date.toString(Qt::TextDate));
         line_edit->setText(ADate::toString(date, date_format));
@@ -966,6 +967,7 @@ void NewFlightDialog::on_calendarPushButton_clicked()
 
 void NewFlightDialog::validationStatus()
 {
+
     QString deb_string("Validation State:\n");
     deb_string += "doft: \t" + QString::number(mandatoryLineEditsValid[0]) + "\n";
     deb_string += "dept: \t" + QString::number(mandatoryLineEditsValid[1]) + "\n";

+ 2 - 6
src/gui/widgets/debugwidget.cpp

@@ -30,16 +30,12 @@
 #include "src/classes/atranslator.h"
 #include "src/database/adatabasesetup.h"
 #include "src/classes/ahash.h"
-
 #include "src/classes/ajson.h"
+#include "src/functions/adate.h"
+
 void DebugWidget::on_debugPushButton_clicked()
 {
     // Debug
-    auto state = aDB->getUserDataState();
-    DEB << "Tails:" << state.numTails << "Pilots:" << state.numPilots;
-    UserDataState state2 = aDB->getUserDataState();
-    DEB << "inequal?" << (state != state2);
-
 }
 
 DebugWidget::DebugWidget(QWidget *parent) :

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

@@ -112,6 +112,9 @@ void SettingsWidget::setupDateEdits()
     for (const auto &date_edit : date_edits) {
         date_edit->setDisplayFormat(date_format_string);
     }
+    // De-activate non-default date settings for now, implement in future release
+    ui->dateFormatComboBox->setVisible(false);
+    ui->dateFormatLabel->setVisible(false);
     // Fill currencies
     const QList<QPair<ACurrencyEntry::CurrencyName, QDateEdit* >> currencies = {
         {ACurrencyEntry::CurrencyName::Licence,     ui->currLicDateEdit},