Ver código fonte

Working on BackupWidget

- Adjusted implementation of ADatabese::restoreBackup to be a bit more conservative (move, copy, delete on success instead of delete, copy)
- Added Slots for PilotsWidget and AircraftWidget to deal with DB connection reset
- Create External and Restore from External Backup Preliminary Implementation
Felix Turo 3 anos atrás
pai
commit
490ee9b865

+ 4 - 0
mainwindow.cpp

@@ -129,6 +129,10 @@ void MainWindow::connectWidgets()
 
     QObject::connect(aDB,             &ADatabase::connectionReset,
                      logbookWidget,   &LogbookWidget::repopulateModel);
+    QObject::connect(aDB,             &ADatabase::connectionReset,
+                     pilotsWidget,    &PilotsWidget::repopulateModel);
+    QObject::connect(aDB,             &ADatabase::connectionReset,
+                     aircraftWidget,  &AircraftWidget::repopulateModel);
 }
 
 /*

+ 10 - 4
src/database/adatabase.cpp

@@ -753,21 +753,27 @@ bool ADatabase::restoreBackup(const QString& backup_file)
 {
     INFO << "Restoring backup from file:" << backup_file;
 
+    QString default_loc = databaseFile.absoluteFilePath();
+
     ADatabase::disconnect();
     QFile backup(backup_file);
-    QFile current_db(databaseFile.absoluteFilePath());
+    QFile current_db(default_loc);
 
-    if (!current_db.remove(databaseFile.absoluteFilePath())) {
-        WARN << current_db.errorString() << "Unable to delete current db file";
+    if (!current_db.rename(default_loc + QLatin1String(".tmp"))) { // move previously used db out of the way
+        WARN << current_db.errorString() << "Unable to remove current db file";
         return false;
     }
 
-    if (!backup.copy(databaseFile.absoluteFilePath()))
+    if (!backup.copy(default_loc))
     {
         WARN << backup.errorString() << "Could not copy" << backup << "to" << databaseFile;
+        // try to restore previously used db
+        current_db.rename(default_loc);
         return false;
     }
 
+    // backup has been restored, clean up the previously moved file
+    current_db.remove();
     INFO << "Backup successfully restored!";
     ADatabase::connect();
     emit connectionReset();

+ 16 - 0
src/gui/widgets/aircraftwidget.cpp

@@ -66,6 +66,11 @@ void AircraftWidget::setupModelAndView()
     view->show();
     selection = view->selectionModel();
 
+    connectSignalsAndSlots();
+}
+
+void AircraftWidget::connectSignalsAndSlots()
+{
     QObject::connect(ui->tableView->selectionModel(),   &QItemSelectionModel::selectionChanged,
                      this,                              &AircraftWidget::tableView_selectionChanged);
     QObject::connect(ui->tableView->horizontalHeader(), &QHeaderView::sectionClicked,
@@ -237,3 +242,14 @@ void AircraftWidget::onDeleteUnsuccessful()
         message_box.exec();
     }
 }
+
+void AircraftWidget::repopulateModel()
+{
+    // unset the current model and delete it to avoid leak
+    view->setModel(nullptr);
+    delete model;
+    // create a new model and populate it
+    model = new QSqlTableModel(this);
+    setupModelAndView();
+    connectSignalsAndSlots();
+}

+ 7 - 0
src/gui/widgets/aircraftwidget.h

@@ -76,6 +76,11 @@ public slots:
      */
     void onAircraftWidget_dataBaseUpdated();
 
+    /*!
+     * \brief AircraftWidget::repopulateModel (public slot) - re-populates the model to cater for a change
+     * to the database connection (for example, when a backup is created)
+     */
+    void repopulateModel();
 private:
     Ui::AircraftWidget *ui;
 
@@ -94,6 +99,8 @@ private:
 
     void setupModelAndView();
 
+    void connectSignalsAndSlots();
+
     void onDeleteUnsuccessful();
 
     inline void refreshView(){model->select();}

+ 54 - 67
src/gui/widgets/backupwidget.cpp

@@ -38,6 +38,7 @@ BackupWidget::BackupWidget(QWidget *parent) :
     model = new QStandardItemModel(this);
     model->setHorizontalHeaderLabels(QStringList{"Backup File","Total Flights", "Total Tails",
                                                  "Total Pilots", "Max Doft", "Total Time"});  // [G]: TODO make const but where?
+    view = ui->tableView;
     refresh();
 }
 
@@ -46,13 +47,42 @@ BackupWidget::~BackupWidget()
     delete ui;
 }
 
-QString BackupWidget::absoluteBackupPath()
+void BackupWidget::refresh()
+{
+    // First column in table, would be created by listing the files in backupdir
+    QDir backup_dir = QDir(AStandardPaths::directory(AStandardPaths::Backup));
+    const QStringList entries = backup_dir.entryList(QStringList{"*.db"}, QDir::Files, QDir::Time);
+    QFileIconProvider provider;
+
+    // Get summary of each db file and populate lists (columns) of data
+    for (const auto &entry : entries) {
+        QMap<ADatabaseSummaryKey, QString> summary = aDB->databaseSummary(backup_dir.absoluteFilePath(entry));
+        model->appendRow({new AFileStandardItem(provider.icon(QFileIconProvider::File), entry, AStandardPaths::Backup),
+                          new QStandardItem(summary[ADatabaseSummaryKey::total_flights]),
+                          new QStandardItem(summary[ADatabaseSummaryKey::total_tails]),
+                          new QStandardItem(summary[ADatabaseSummaryKey::total_pilots]),
+                          new QStandardItem(summary[ADatabaseSummaryKey::max_doft]),
+                          new QStandardItem(summary[ADatabaseSummaryKey::total_time])
+                         });
+    }
+
+    ui->tableView->setModel(model);
+    ui->tableView->resizeColumnsToContents();  // [G]: Bit hacky couldnt do it by default
+}
+
+const QString BackupWidget::absoluteBackupPath()
 {
     const QString backup_name = QLatin1String("logbook_backup_")
             + ADateTime::toString(QDateTime::currentDateTime(), Opl::Datetime::Backup)
             + QLatin1String(".db");
     return AStandardPaths::asChildOfDir(AStandardPaths::Backup, backup_name);
 }
+const QString BackupWidget::backupName()
+{
+    return  QLatin1String("logbook_backup_")
+            + ADateTime::toString(QDateTime::currentDateTime(), Opl::Datetime::Backup)
+            + QLatin1String(".db");
+}
 
 void BackupWidget::on_tableView_clicked(const QModelIndex &index)
 {
@@ -85,8 +115,6 @@ void BackupWidget::on_createLocalPushButton_clicked()
 
 void BackupWidget::on_restoreLocalPushButton_clicked()
 {
-    NOT_IMPLEMENTED("TODO");
-
     if(selectedFileInfo == nullptr) {
         INFO << "No backup selected";
         return;
@@ -125,26 +153,25 @@ void BackupWidget::on_deleteSelectedPushButton_clicked()
     }
 
     model->removeRow(selectedFileInfo->row());
-    // [G] TODO: figure out selection coordination between view model and selected
-    if(selectedFileInfo->row() - 1 < 0) {
-        selectedFileInfo = nullptr;
-    }
-    else {
-        selectedFileInfo = static_cast<AFileStandardItem*>(model->item(selectedFileInfo->row()-1));
-    }
+    view->clearSelection();
+    selectedFileInfo = nullptr;
 }
 
 void BackupWidget::on_createExternalPushButton_clicked()
 {
-    NOT_IMPLEMENTED("TODO");
     QString filename = QFileDialog::getSaveFileName(
                 this,
                 "Choose destination file",
                 // [G]: Is this necessary?
-                QDir(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)).absoluteFilePath("untitled.db"),
+                //QDir(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)).absoluteFilePath("untitled.db"),
+                QDir::homePath() + QDir::separator() + backupName(),
                 "*.db"
     );
 
+    if(filename.isEmpty()) { // QFileDialog has been cancelled
+        return;
+    }
+
     if(!filename.endsWith(".db")) {
         filename.append(".db");
     }
@@ -153,34 +180,27 @@ void BackupWidget::on_createExternalPushButton_clicked()
         DEB << "Unable to backup file:" << filename;
         return;
     }
-
-    // [G] TODO: propably make a function out of this for future tweaks
-    QFileIconProvider provider;
-    QMap<ADatabaseSummaryKey, QString> summary = aDB->databaseSummary(filename);
-    model->appendRow({new AFileStandardItem(provider.icon(QFileIconProvider::File), QFileInfo(filename)),
-                      new QStandardItem(summary[ADatabaseSummaryKey::total_flights]),
-                      new QStandardItem(summary[ADatabaseSummaryKey::total_tails]),
-                      new QStandardItem(summary[ADatabaseSummaryKey::total_pilots]),
-                      new QStandardItem(summary[ADatabaseSummaryKey::max_doft]),
-                      new QStandardItem(summary[ADatabaseSummaryKey::total_time])
-                     });
-    model->sort(0);
-
-    // [G]: The window isn resizable and i cant easily debug the buttons (cant find them xD)
 }
 
 void BackupWidget::on_restoreExternalPushButton_clicked()
 {
-    NOT_IMPLEMENTED("TODO");
-    QString filename = QFileDialog::getSaveFileName(
+    QString filename = QFileDialog::getOpenFileName(
                 this,
-                "Choose backup file",
-                // [G] TODO: home is the debug directory. Will it be ~ correctly?
-                // Qt docs say it is (Confirm debug exception)
-                QStandardPaths::displayName(QStandardPaths::HomeLocation),
-                ".db"
+                tr("Choose backup file"),
+                QDir::homePath(),
+                "*.db"
     );
-    // Open something like a QFileDialog and let the user choose where to load the backup from
+
+    if(filename.isEmpty()) { // QFileDialog has been cancelled
+        return;
+    }
+
+    // Maybe create a Message Box asking for confirmation here and displaying the summary of backup and active DB
+
+    if(!aDB->restoreBackup(filename)) {
+        DEB << "Unable to backup file:" << filename;
+        return;
+    }
 }
 
 void BackupWidget::on_aboutPushButton_clicked() {
@@ -190,36 +210,3 @@ void BackupWidget::on_aboutPushButton_clicked() {
 }
 
 
-
-// ===================================================================================
-
-// feel free to delete this as you go along, just here for demonstration purposes
-void BackupWidget::refresh()
-{
-    // First column in table, would be created by listing the files in backupdir
-    QDir backup_dir = QDir(AStandardPaths::directory(AStandardPaths::Backup));
-    QStringList entries = backup_dir.entryList(QStringList{"*.db"}, QDir::Files, QDir::Time);
-    QFileIconProvider provider;
-
-    // [G]: works but a bit too hardcoded perhaps? The aviation industry wont change overnight
-    // but still it could be worthwile to at least have the names a bit more encapsulated in the
-    // database so we have them more "centralised" at least.
-
-    // Get summary of each db file and populate lists (columns) of data
-    for (const auto &entry : entries) {
-        QMap<ADatabaseSummaryKey, QString> summary = aDB->databaseSummary(backup_dir.absoluteFilePath(entry));
-        model->appendRow({new AFileStandardItem(provider.icon(QFileIconProvider::File), entry, AStandardPaths::Backup),
-                          new QStandardItem(summary[ADatabaseSummaryKey::total_flights]),
-                          new QStandardItem(summary[ADatabaseSummaryKey::total_tails]),
-                          new QStandardItem(summary[ADatabaseSummaryKey::total_pilots]),
-                          new QStandardItem(summary[ADatabaseSummaryKey::max_doft]),
-                          new QStandardItem(summary[ADatabaseSummaryKey::total_time])
-                         });
-    }
-
-    // [G]: Sort entries? based on what? the files are abit inconsistent in their naming atm
-    // but i assume we could sort based on the time in the file name?
-
-    ui->tableView->setModel(model);
-    ui->tableView->resizeColumnsToContents();  // [G]: Bit hacky couldnt do it by default
-}

+ 11 - 5
src/gui/widgets/backupwidget.h

@@ -48,11 +48,6 @@ public:
     explicit BackupWidget(QWidget *parent = nullptr);
     ~BackupWidget();
 
-    /*!
-     * \brief Generates the absolute path for a new backup file.
-     */
-    QString absoluteBackupPath();
-
 private slots:
     void on_tableView_clicked(const QModelIndex &index);
 
@@ -72,10 +67,21 @@ private:
     Ui::BackupWidget *ui;
 
     QStandardItemModel *model;
+    QTableView *view;
     AFileStandardItem *selectedFileInfo = nullptr;  // Only the first column is necessary for
                                                     // any operation and it is encapsulated in the
                                                     // AFileStandardItem class
     void refresh();
+
+    /*!
+     * \brief Generates a filename for creating a backup
+     */
+    const QString backupName();
+
+    /*!
+     * \brief Generates the absolute path for a new backup file.
+     */
+    const QString absoluteBackupPath();
 };
 
 #endif // BACKUPWIDGET_H

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

@@ -294,4 +294,5 @@ void LogbookWidget::repopulateModel()
     // create a new model and populate it
     displayModel = new QSqlTableModel(this);
     setupModelAndView(ASettings::read(ASettings::Main::LogbookView).toInt());
+    connectSignalsAndSlots();
 }

+ 19 - 3
src/gui/widgets/pilotswidget.cpp

@@ -62,10 +62,15 @@ void PilotsWidget::setupModelAndView()
     view->show();
     selectionModel = view->selectionModel();
 
+    connectSignalsAndSlots();
+}
+
+void PilotsWidget::connectSignalsAndSlots()
+{
     QObject::connect(ui->tableView->selectionModel(),   &QItemSelectionModel::selectionChanged,
-                     this,                                    &PilotsWidget::tableView_selectionChanged);
+                     this,                              &PilotsWidget::tableView_selectionChanged);
     QObject::connect(ui->tableView->horizontalHeader(), &QHeaderView::sectionClicked,
-                     this,                                    &PilotsWidget::tableView_headerClicked);
+                     this,                              &PilotsWidget::tableView_headerClicked);
 }
 
 void PilotsWidget::onPilotsWidget_settingChanged(SettingsWidget::SettingSignal signal)
@@ -185,7 +190,7 @@ void PilotsWidget::on_deletePilotButton_clicked()
  */
 void PilotsWidget::onDeleteUnsuccessful()
 {
-    QList<int> foreign_key_constraints = aDB->getForeignKeyConstraints(selectedPilots.first(),
+    const QList<int> foreign_key_constraints = aDB->getForeignKeyConstraints(selectedPilots.first(),
                                                                        ADatabaseTarget::pilots);
     QList<AFlightEntry> constrained_flights;
     for (const auto &row_id : foreign_key_constraints) {
@@ -220,3 +225,14 @@ void PilotsWidget::onDeleteUnsuccessful()
         message_box.exec();
     }
 }
+
+void PilotsWidget::repopulateModel()
+{
+    // unset the current model and delete it to avoid leak
+    view->setModel(nullptr);
+    delete model;
+    // create a new model and populate it
+    model = new QSqlTableModel(this);
+    setupModelAndView();
+    connectSignalsAndSlots();
+}

+ 7 - 0
src/gui/widgets/pilotswidget.h

@@ -77,6 +77,11 @@ public slots:
      */
     void onPilotsWidget_databaseUpdated();
 
+    /*!
+     * \brief PilotsWidget::repopulateModel (public slot) - re-populates the model to cater for a change
+     * to the database connection (for example, when a backup is created)
+     */
+    void repopulateModel();
 private:
     Ui::PilotsWidget *ui;
 
@@ -92,6 +97,8 @@ private:
 
     void setupModelAndView();
 
+    void connectSignalsAndSlots();
+
     inline void refreshView(){model->select();}
 };