mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-13 16:18:42 +00:00
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:
@@ -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
172
src/AthleteBackup.cpp
Normal 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
44
src/AthleteBackup.h
Normal 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
|
||||
@@ -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 ?
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 \
|
||||
|
||||
Reference in New Issue
Block a user