AutoBackup/Data Snapshot Feature on close of Athlete Window/Tab

... collects all relevant Files from the Athletes directory subfolders (keeps the folder structure)
    and add them to a .zip file which can be stored in a different folder
... runs automatically when closing an Athlete (window or tab)

... configurable per Athlete
    -- Folder into which the .zip shall be stored
    -- #of times GC shall close without backup before the backup is activated
      (0 == no Auto Backup, every other number "x" means that GC closes
       x-1 times without running the backup and after running, resets
       the counter)
This commit is contained in:
Joern
2015-10-12 19:01:31 +02:00
parent cac9f6cc48
commit c86cae70f0
7 changed files with 273 additions and 2 deletions

View File

@@ -46,6 +46,7 @@
#include "LTMSettings.h"
#include "RideImportWizard.h"
#include "RideAutoImportConfig.h"
#include "AthleteBackup.h"
#include "Route.h"
@@ -185,6 +186,11 @@ Athlete::close()
// set to latest so we don't repeat
appsettings->setCValue(context->athlete->home->root().dirName(), GC_VERSION_USED, VERSION_LATEST);
appsettings->setCValue(context->athlete->home->root().dirName(), GC_SAFEEXIT, true);
// run autobackup on close (if configured)
AthleteBackup *backup = new AthleteBackup(context);
backup->backupOnClose();
}
void
Athlete::loadCharts()

172
src/AthleteBackup.cpp Normal file
View File

@@ -0,0 +1,172 @@
/*
* Copyright (c) 2015 Joern Rischmueller (joern.rm@gmail.com)
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <QProgressDialog>
#include <QMessageBox>
#if QT_VERSION > 0x050400
#include <QStorageInfo>
#endif
#include "Athlete.h"
#include "AthleteBackup.h"
#include "Settings.h"
#include "GcUpgrade.h"
#include "../qzip/zipwriter.h"
#include "../qzip/zipreader.h"
AthleteBackup::AthleteBackup(Context *context)
{
this->context = context;
cyclist = context->athlete->cyclist;
// set the directories to be backed up
// for a FULL backup basically all data folders
sourceFolder.append(context->athlete->home->activities());
sourceFolder.append(context->athlete->home->imports());
sourceFolder.append(context->athlete->home->records());
sourceFolder.append(context->athlete->home->downloads());
sourceFolder.append(context->athlete->home->fileBackup());
sourceFolder.append(context->athlete->home->config());
sourceFolder.append(context->athlete->home->calendar());
sourceFolder.append(context->athlete->home->workouts());
}
AthleteBackup::~AthleteBackup()
{
}
void
AthleteBackup::backupOnClose()
{
int backupPeriod = appsettings->cvalue(context->athlete->cyclist, GC_AUTOBACKUP_PERIOD, 0).toInt();
QString backupFolder = appsettings->cvalue(context->athlete->cyclist, GC_AUTOBACKUP_FOLDER, "").toString();
if (backupPeriod == 0 || backupFolder == "" ) return;
int backupCounter = appsettings->cvalue(context->athlete->cyclist, GC_AUTOBACKUP_COUNTER, 0).toInt();
backupCounter++;
if (backupCounter < backupPeriod) {
appsettings->setCValue(context->athlete->cyclist, GC_AUTOBACKUP_COUNTER, backupCounter);
return;
}
// backup requested so lets see if we have something to backup and if yes, how much
int fileCount = 0;
qint64 fileSize = 0;
// count the files for the progress bar and the calculate the overall size
foreach (QDir folder, sourceFolder) {
// get all files
foreach (QFileInfo fileName, folder.entryInfoList(QDir::Files | QDir::NoDotAndDotDot | QDir::NoSymLinks)) {
fileCount++;
fileSize += fileName.size();
}
}
if (fileCount == 0) {
QMessageBox::information(NULL, tr("Auto Backup"), tr("No files found for athlete %1 - all athlete sub-directories are empty.").arg(cyclist));
return;
}
#if QT_VERSION > 0x050400
// if if there is enough space available for the backup
QStorageInfo storage(backupFolder);
if (storage.isValid() && storage.isReady()) {
// let's assume a 1:5 Zip compression to have enough space available for the ZIP
if (storage.bytesAvailable() < fileSize / 5) {
QMessageBox::warning(NULL, tr("Auto Backup"), tr("Not enough space available on disk: %1 - no backup .zip file created").arg(storage.rootPath()));
return;
}
} else {
QMessageBox::warning(NULL, tr("Auto Backup"), tr("Directory %1 not available. No backup .zip file created for athlete %2.").arg(backupFolder).arg(cyclist));
return;
}
#else
QDir checkDir(backupFolder);
if (!checkDir.exists()) {
QMessageBox::warning(NULL, tr("Auto Backup"), tr("Directory %1 not available. No backup .zip file created for athlete %2.").arg(backupFolder).arg(cyclist));
return;
}
#endif
QChar zero = QLatin1Char('0');
QString targetFileName = QString( "GC_%1_%2_%3_%4_%5_%6_%7_%8.zip" )
.arg ( VERSION_LATEST )
.arg ( cyclist )
.arg ( QDate::currentDate().year(), 4, 10, zero )
.arg ( QDate::currentDate().month(), 2, 10, zero )
.arg ( QDate::currentDate().day(), 2, 10, zero )
.arg ( QTime::currentTime().hour(), 2, 10, zero )
.arg ( QTime::currentTime().minute(), 2, 10, zero )
.arg ( QTime::currentTime().second(), 2, 10, zero );
// add files using zip writer
QFile zipFile(backupFolder+"/"+targetFileName);
if (!zipFile.open(QIODevice::WriteOnly)) {
QMessageBox::warning(NULL, tr("Auto Backup"), tr("Backup file %1 cannot be created.").arg(zipFile.fileName()));
return;
}
zipFile.close();
ZipWriter writer(zipFile.fileName());
QProgressDialog progress(tr("Adding files to backup %1 for athlete %2 ...").arg(targetFileName).arg(cyclist), tr("Abort Backup"), 0, fileCount, NULL);
progress.setWindowModality(Qt::WindowModal);
// now do the Zipping
bool userCanceled = false;
int fileCounter = 0;
foreach (QDir folder, sourceFolder) {
// get all files
writer.addDirectory(folder.dirName());
foreach (QFileInfo fileName, folder.entryInfoList(QDir::Files | QDir::NoDotAndDotDot | QDir::NoSymLinks)) {
QFile file(fileName.canonicalFilePath());
if (file.open(QIODevice::ReadOnly)) {
if (progress.wasCanceled()) {
userCanceled = true;
break;
}
writer.addFile(folder.dirName()+"/"+fileName.fileName(), file.readAll());
progress.setValue(fileCounter);
fileCounter++;
file.close();
}
}
if (userCanceled) break;
}
// final processing
writer.close();
// delete the .ZIP file if the user canceled the backup
if (userCanceled) {
zipFile.remove();
} else {
// we are done, full progress and reset of counter
progress.setValue(fileCount);
appsettings->setCValue(context->athlete->cyclist, GC_AUTOBACKUP_COUNTER, 0);
}
}

44
src/AthleteBackup.h Normal file
View File

@@ -0,0 +1,44 @@
/*
* Copyright (c) 2015 Joern Rischmueller (joern.rm@gmail.com)
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _GC_AthleteBackup_h
#define _GC_AthleteBackup_h 1
#include <QString>
#include "Context.h"
#include "Athlete.h"
class AthleteBackup : public QObject
{
Q_OBJECT
public:
AthleteBackup(Context *context);
~AthleteBackup();
void backupOnClose();
private:
Context *context;
QString cyclist;
QList<QDir> sourceFolder;
};
#endif

View File

@@ -1049,6 +1049,24 @@ RiderPage::RiderPage(QWidget *parent, Context *context) : QWidget(parent), conte
showSBToday = new QCheckBox(tr("PMC Stress Balance Today"), this);
showSBToday->setChecked(appsettings->cvalue(context->athlete->cyclist, GC_SB_TODAY).toInt());
//
// Auto Backup
//
// Selecting the storage folder folder of the Local File Store
QLabel *autoBackupFolderLabel = new QLabel(tr("Auto Backup Folder"));
autoBackupFolder = new QLineEdit(this);
autoBackupFolder->setText(appsettings->cvalue(context->athlete->cyclist, GC_AUTOBACKUP_FOLDER, "").toString());
autoBackupFolderBrowse = new QPushButton(tr("Browse"));
connect(autoBackupFolderBrowse, SIGNAL(clicked()), this, SLOT(chooseAutoBackupFolder()));
autoBackupPeriod = new QSpinBox(this);
autoBackupPeriod->setMinimum(0);
autoBackupPeriod->setMaximum(9999);
autoBackupPeriod->setSingleStep(1);
QLabel *autoBackupPeriodLabel = new QLabel(tr("Auto Backup Period"));
autoBackupPeriod->setValue(appsettings->cvalue(context->athlete->cyclist, GC_AUTOBACKUP_PERIOD, 0).toInt());
Qt::Alignment alignment = Qt::AlignLeft|Qt::AlignVCenter;
grid->addWidget(nicklabel, 0, 0, alignment);
@@ -1075,9 +1093,14 @@ RiderPage::RiderPage(QWidget *parent, Context *context) : QWidget(parent), conte
grid->addWidget(perfManLTSLabel, 9, 0, alignment);
grid->addWidget(perfManLTSavg, 9, 1, alignment);
grid->addWidget(showSBToday, 10, 1, alignment);
grid->addWidget(autoBackupFolderLabel, 11,0, alignment);
grid->addWidget(autoBackupFolder, 11, 1, alignment);
grid->addWidget(autoBackupFolderBrowse, 11, 2, alignment);
grid->addWidget(autoBackupPeriodLabel, 12, 0,alignment);
grid->addWidget(autoBackupPeriod, 12, 1, alignment);
grid->addWidget(biolabel, 11, 0, alignment);
grid->addWidget(bio, 12, 0, 1, 3);
grid->addWidget(biolabel, 13, 0, alignment);
grid->addWidget(bio, 14, 0, 1, 3);
grid->addWidget(avatarButton, 0, 1, 5, 2, Qt::AlignRight|Qt::AlignVCenter);
all->addLayout(grid);
@@ -1113,6 +1136,17 @@ RiderPage::chooseAvatar()
}
}
void RiderPage::chooseAutoBackupFolder()
{
// did the user type something ? if not, get it from the Settings
QString path = autoBackupFolder->text();
if (path == "") path = appsettings->cvalue(context->athlete->cyclist, GC_AUTOBACKUP_FOLDER, "").toString();
QString dir = QFileDialog::getExistingDirectory(this, tr("Choose Backup Directory"),
path, QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
if (dir != "") autoBackupFolder->setText(dir); //only overwrite current dir, if a new was selected
}
void
RiderPage::unitChanged(int currentIndex)
{
@@ -1173,6 +1207,11 @@ RiderPage::saveClicked()
appsettings->setCValue(context->athlete->cyclist, GC_LTS_DAYS, perfManLTSavg->text());
appsettings->setCValue(context->athlete->cyclist, GC_SB_TODAY, (int) showSBToday->isChecked());
// Auto Backup
appsettings->setCValue(context->athlete->cyclist, GC_AUTOBACKUP_FOLDER, autoBackupFolder->text());
appsettings->setCValue(context->athlete->cyclist, GC_AUTOBACKUP_PERIOD, autoBackupPeriod->value());
qint32 state=0;
// general stuff changed ?

View File

@@ -158,6 +158,9 @@ class RiderPage : public QWidget
QIntValidator *perfManSTSavgValidator;
QIntValidator *perfManLTSavgValidator;
QCheckBox *showSBToday;
QSpinBox *autoBackupPeriod;
QLineEdit *autoBackupFolder;
QPushButton *autoBackupFolderBrowse;
struct {
@@ -171,6 +174,8 @@ class RiderPage : public QWidget
private slots:
void calcWheelSize();
void resetWheelSize();
void chooseAutoBackupFolder();
};

View File

@@ -210,6 +210,9 @@
#define GC_WHEELSIZE "<athlete-preferences>wheelsize"
#define GC_USE_CP_FOR_FTP "<athlete-preferences>cp/useforftp" // use CP for FTP
#define GC_NETWORKFILESTORE_FOLDER "<athlete-preferences>networkfilestore/folder" // folder to sync with
#define GC_AUTOBACKUP_FOLDER "<athlete-preferences>autobackup/folder"
#define GC_AUTOBACKUP_PERIOD "<athlete-preferences>autobackup/period" // how often is the Athlete Folder backuped up / 0 == never
#define GC_AUTOBACKUP_COUNTER "<athlete-preferences>autobackup/counter" // counts to the next backup
// ride navigator
#define GC_NAVHEADINGS "<athlete-preferences>navigator/headings"

View File

@@ -334,6 +334,7 @@ HEADERS += \
ANTMessages.h \
ANTlocalController.h \
Athlete.h \
AthleteBackup.h \
BatchExportDialog.h \
BestIntervalDialog.h \
BinRideFile.h \
@@ -558,6 +559,7 @@ SOURCES += \
ANTMessage.cpp \
ANTlocalController.cpp \
Athlete.cpp \
AthleteBackup.cpp \
BasicRideMetrics.cpp \
BatchExportDialog.cpp \
BestIntervalDialog.cpp \