mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-14 00:28:42 +00:00
394 lines
14 KiB
C++
394 lines
14 KiB
C++
/*
|
|
* Copyright (c) 2017 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 "BodyMeasuresDownload.h"
|
|
#include "BodyMeasures.h"
|
|
#include "Athlete.h"
|
|
#include "RideCache.h"
|
|
#include "HelpWhatsThis.h"
|
|
|
|
#include <QList>
|
|
#include <QMutableListIterator>
|
|
#include <QGroupBox>
|
|
#include <QMessageBox>
|
|
|
|
|
|
BodyMeasuresDownload::BodyMeasuresDownload(Context *context) : context(context) {
|
|
|
|
setWindowTitle(tr("Body Measurements download"));
|
|
HelpWhatsThis *help = new HelpWhatsThis(this);
|
|
this->setWhatsThis(help->getWhatsThisText(HelpWhatsThis::MenuBar_Tools_Download_BodyMeasures));
|
|
|
|
QVBoxLayout *mainLayout = new QVBoxLayout(this);
|
|
|
|
QGroupBox *groupBox1 = new QGroupBox(tr("Choose the download or import source"));
|
|
downloadWithings = new QRadioButton(tr("Nokia Health (Withings)"));
|
|
downloadTP = new QRadioButton(tr("Today's Plan"));
|
|
downloadCSV = new QRadioButton(tr("Import CSV file"));
|
|
QVBoxLayout *vbox1 = new QVBoxLayout;
|
|
vbox1->addWidget(downloadWithings);
|
|
vbox1->addWidget(downloadTP);
|
|
vbox1->addWidget(downloadCSV);
|
|
groupBox1->setLayout(vbox1);
|
|
mainLayout->addWidget(groupBox1);
|
|
|
|
QGroupBox *groupBox2 = new QGroupBox(tr("Choose date range for download"));
|
|
dateRangeAll = new QRadioButton(tr("From date of first recorded activity to today"));
|
|
dateRangeLastMeasure = new QRadioButton(tr("From date of last downloaded measurement to today"));
|
|
dateRangeManual = new QRadioButton(tr("Enter manually:"));
|
|
QVBoxLayout *vbox2 = new QVBoxLayout;
|
|
vbox2->addWidget(dateRangeAll);
|
|
vbox2->addWidget(dateRangeLastMeasure);
|
|
vbox2->addWidget(dateRangeManual);
|
|
vbox2->addStretch();
|
|
QLabel *fromLabel = new QLabel("From");
|
|
QLabel *toLabel = new QLabel("To");
|
|
manualFromDate = new QDateEdit(this);
|
|
manualFromDate->setDate(QDate::currentDate().addMonths(-1));
|
|
manualToDate = new QDateEdit(this);
|
|
manualToDate->setDate(QDate::currentDate());
|
|
manualFromDate->setCalendarPopup(true);
|
|
manualToDate->setCalendarPopup(true);
|
|
manualFromDate->setEnabled(false);
|
|
manualToDate->setEnabled(false);
|
|
QHBoxLayout *dateRangeLayout = new QHBoxLayout;
|
|
dateRangeLayout->addStretch();
|
|
dateRangeLayout->addWidget(fromLabel);
|
|
dateRangeLayout->addWidget(manualFromDate);
|
|
dateRangeLayout->addWidget(toLabel);
|
|
dateRangeLayout->addWidget(manualToDate);
|
|
vbox2->addLayout(dateRangeLayout);
|
|
groupBox2->setLayout(vbox2);
|
|
mainLayout->addWidget(groupBox2);
|
|
|
|
discardExistingMeasures = new QCheckBox(tr("Discard all existing measurements"), this);
|
|
discardExistingMeasures->setChecked(false);
|
|
mainLayout->addWidget(discardExistingMeasures);
|
|
|
|
progressBar = new QProgressBar(this);
|
|
progressBar->setMinimum(0);
|
|
progressBar->setMaximum(1);
|
|
progressBar->setValue(0);
|
|
QHBoxLayout *progressLayout = new QHBoxLayout;
|
|
progressLayout->addWidget(progressBar);
|
|
mainLayout->addLayout(progressLayout);
|
|
|
|
downloadButton = new QPushButton(tr("Download"));
|
|
closeButton = new QPushButton(tr("Close"));
|
|
QHBoxLayout *buttonLayout = new QHBoxLayout;
|
|
buttonLayout->addWidget(downloadButton);
|
|
buttonLayout->addStretch();
|
|
buttonLayout->addWidget(closeButton);
|
|
mainLayout->addLayout(buttonLayout);
|
|
|
|
|
|
connect(downloadButton, SIGNAL(clicked()), this, SLOT(download()));
|
|
connect(closeButton, SIGNAL(clicked()), this, SLOT(close()));
|
|
connect(dateRangeAll, SIGNAL(toggled(bool)), this, SLOT(dateRangeAllSettingChanged(bool)));
|
|
connect(dateRangeLastMeasure, SIGNAL(toggled(bool)), this, SLOT(dateRangeLastSettingChanged(bool)));
|
|
connect(dateRangeManual, SIGNAL(toggled(bool)), this, SLOT(dateRangeManualSettingChanged(bool)));
|
|
|
|
connect(downloadWithings, SIGNAL(toggled(bool)), this, SLOT(downloadWithingsSettingChanged(bool)));
|
|
connect(downloadTP, SIGNAL(toggled(bool)), this, SLOT(downloadTPSettingChanged(bool)));
|
|
connect(downloadCSV, SIGNAL(toggled(bool)), this, SLOT(downloadCSVSettingChanged(bool)));
|
|
|
|
// don't allow options which are not authorized
|
|
QString strToken =appsettings->cvalue(context->athlete->cyclist, GC_WITHINGS_TOKEN, "").toString();
|
|
QString strSecret= appsettings->cvalue(context->athlete->cyclist, GC_WITHINGS_SECRET, "").toString();
|
|
|
|
QString strToken2 =appsettings->cvalue(context->athlete->cyclist, GC_NOKIA_TOKEN, "").toString();
|
|
|
|
if (strToken2 =="" && strToken == "" && strSecret == "") {
|
|
downloadWithings->setEnabled(false);
|
|
}
|
|
|
|
QString token = appsettings->cvalue(context->athlete->cyclist, GC_TODAYSPLAN_TOKEN, "").toString();
|
|
if (token == "") {
|
|
downloadTP->setEnabled(false);
|
|
}
|
|
|
|
|
|
// select the default checked / based on available properties and last selection
|
|
int last_selection = appsettings->cvalue(context->athlete->cyclist, GC_BM_LAST_TYPE, 0).toInt();
|
|
bool done = false;
|
|
if (downloadWithings->isEnabled()) {
|
|
if (last_selection == 0 || last_selection == WITHINGS) {
|
|
downloadWithings->setChecked(true);
|
|
done = true;
|
|
}
|
|
}
|
|
if (!done && downloadTP->isEnabled()) {
|
|
if (last_selection == 0 || last_selection == TP) {
|
|
downloadTP->setChecked(true);
|
|
done = true;
|
|
}
|
|
}
|
|
if (!done) {
|
|
downloadCSV->setChecked(true);
|
|
}
|
|
|
|
|
|
// set the default from "last"
|
|
int last_timeframe = appsettings->cvalue(context->athlete->cyclist, GC_BM_LAST_TIMEFRAME, ALL).toInt();
|
|
switch (last_timeframe) {
|
|
case ALL:
|
|
dateRangeAll->setChecked(true);
|
|
break;
|
|
case LAST:
|
|
dateRangeLastMeasure->setChecked(true);
|
|
break;
|
|
case MANUAL:
|
|
dateRangeManual->setChecked(true);
|
|
manualFromDate->setEnabled(true);
|
|
manualToDate->setEnabled(true);
|
|
break;
|
|
default:
|
|
dateRangeAll->setChecked(true);
|
|
}
|
|
// initialize the downloader
|
|
withingsDownload = new WithingsDownload(context);
|
|
todaysPlanBodyMeasureDownload = new TodaysPlanBodyMeasures(context);
|
|
csvFileImport = new BodyMeasuresCsvImport(context);
|
|
|
|
// connect the progress bar
|
|
connect(todaysPlanBodyMeasureDownload, SIGNAL(downloadStarted(int)), this, SLOT(downloadProgressStart(int)));
|
|
connect(todaysPlanBodyMeasureDownload, SIGNAL(downloadProgress(int)), this, SLOT(downloadProgress(int)));
|
|
connect(todaysPlanBodyMeasureDownload, SIGNAL(downloadEnded(int)), this, SLOT(downloadProgressEnd(int)));
|
|
|
|
connect(withingsDownload, SIGNAL(downloadStarted(int)), this, SLOT(downloadProgressStart(int)));
|
|
connect(withingsDownload, SIGNAL(downloadProgress(int)), this, SLOT(downloadProgress(int)));
|
|
connect(withingsDownload, SIGNAL(downloadEnded(int)), this, SLOT(downloadProgressEnd(int)));
|
|
|
|
connect(csvFileImport, SIGNAL(downloadStarted(int)), this, SLOT(downloadProgressStart(int)));
|
|
connect(csvFileImport, SIGNAL(downloadProgress(int)), this, SLOT(downloadProgress(int)));
|
|
connect(csvFileImport, SIGNAL(downloadEnded(int)), this, SLOT(downloadProgressEnd(int)));
|
|
}
|
|
|
|
BodyMeasuresDownload::~BodyMeasuresDownload() {
|
|
|
|
delete withingsDownload;
|
|
delete todaysPlanBodyMeasureDownload;
|
|
delete csvFileImport;
|
|
|
|
}
|
|
|
|
|
|
void
|
|
BodyMeasuresDownload::download() {
|
|
|
|
// de-activate the buttons
|
|
downloadButton->setEnabled(false);
|
|
closeButton->setEnabled(false);
|
|
|
|
progressBar->setMaximum(1);
|
|
progressBar->setValue(0);
|
|
|
|
// do the job
|
|
BodyMeasures* pBodyMeasures = dynamic_cast <BodyMeasures*>(context->athlete->measures->getGroup(Measures::Body));
|
|
QList<BodyMeasure> current = pBodyMeasures->bodyMeasures();
|
|
QList<BodyMeasure> bodyMeasures;
|
|
QDateTime fromDate;
|
|
QDateTime toDate;
|
|
QDateTime firstRideDate;
|
|
QString err = "";
|
|
bool downloadOk = false;
|
|
|
|
// get the date of first ride as potential "from" value
|
|
QList<QDateTime> rideDates = context->athlete->rideCache->getAllDates();
|
|
if (rideDates.count() > 0) {
|
|
firstRideDate = rideDates.at(0);
|
|
} else {
|
|
firstRideDate = QDateTime::fromMSecsSinceEpoch(0);
|
|
};
|
|
|
|
// determine the date range
|
|
if (dateRangeAll->isChecked()) {
|
|
fromDate = firstRideDate;
|
|
toDate = QDateTime::currentDateTimeUtc();
|
|
} else if (dateRangeLastMeasure->isChecked()) {
|
|
if (current.count() > 0) {
|
|
fromDate = current.last().when.addSecs(1);
|
|
} else {
|
|
// use a reasonable default
|
|
fromDate = firstRideDate;
|
|
}
|
|
toDate = QDateTime::currentDateTimeUtc();
|
|
} else if (dateRangeManual->isChecked()) {
|
|
if (manualFromDate->dateTime() > manualToDate->dateTime()) {
|
|
QMessageBox::warning(this, tr("Body Measurements"), tr("Invalid date range - please check your input"));
|
|
// re-activate the buttons
|
|
downloadButton->setEnabled(true);
|
|
closeButton->setEnabled(true);
|
|
return;
|
|
}
|
|
fromDate.setDate(manualFromDate->date());
|
|
fromDate.setTime(QTime(0,0));
|
|
toDate.setDate(manualToDate->date());
|
|
} else return;
|
|
// to Time is always "end of the day"
|
|
toDate.setTime(QTime(23,59));
|
|
|
|
// do the download
|
|
if (downloadWithings->isChecked()) {
|
|
downloadOk = withingsDownload->getBodyMeasures(err, fromDate, toDate, bodyMeasures);
|
|
} else if (downloadTP->isChecked()) {
|
|
downloadOk = todaysPlanBodyMeasureDownload->getBodyMeasures(err, fromDate, toDate, bodyMeasures);
|
|
} else if (downloadCSV->isChecked()) {
|
|
downloadOk = csvFileImport->getBodyMeasures(err, fromDate, toDate, bodyMeasures);
|
|
} else return;
|
|
|
|
if (downloadOk) {
|
|
|
|
// selection from various source may not be 100% accurate w.r.t. the from/to date filtering
|
|
// e.g. on TodaysPlan the measureDate and selectionDate can deviate
|
|
// so remove all measures which do not fit the selection from/to interval
|
|
QMutableListIterator<BodyMeasure> i(bodyMeasures);
|
|
BodyMeasure c;
|
|
while (i.hasNext()) {
|
|
c = i.next();
|
|
if (c.when <= fromDate || c.when >= toDate) {
|
|
i.remove();
|
|
}
|
|
}
|
|
|
|
// we discard only if we have new data loaded - otherwise keep what is there
|
|
if (discardExistingMeasures->isChecked()) {
|
|
// now the new measures do not contain any "ts" of the current data any more
|
|
current.clear();
|
|
};
|
|
|
|
// if exists, merge current data with new data - new data has preferences
|
|
// no merging of weight data which has the same time stamp - new wins
|
|
if (current.count() > 0) {
|
|
// remove entry from current list if a new entry with same timestamp exists
|
|
QMutableListIterator<BodyMeasure> i(current);
|
|
BodyMeasure c;
|
|
while (i.hasNext()) {
|
|
c = i.next();
|
|
foreach(BodyMeasure m, bodyMeasures) {
|
|
if (m.when == c.when) {
|
|
i.remove();
|
|
}
|
|
}
|
|
}
|
|
// combine the result (without duplicates)
|
|
bodyMeasures.append(current);
|
|
}
|
|
|
|
// update measures in cache and on file store
|
|
context->athlete->rideCache->cancel();
|
|
|
|
// store in athlete
|
|
pBodyMeasures->setBodyMeasures(bodyMeasures);
|
|
|
|
// now save data away if we actually got something !
|
|
// doing it here means we don't overwrite previous responses
|
|
// when we fail to get any data (e.g. errors / network problems)
|
|
pBodyMeasures->write();
|
|
|
|
// do a refresh, it will check if needed
|
|
context->athlete->rideCache->refresh();
|
|
|
|
} else {
|
|
// handle error document in err String
|
|
QMessageBox::warning(this, tr("Body Measurements"), tr("Downloading of body measurements failed with error: %1").arg(err));
|
|
}
|
|
|
|
// re-activate the buttons
|
|
downloadButton->setEnabled(true);
|
|
closeButton->setEnabled(true);
|
|
|
|
}
|
|
|
|
void
|
|
BodyMeasuresDownload::close() {
|
|
|
|
accept();
|
|
|
|
}
|
|
|
|
void
|
|
BodyMeasuresDownload::dateRangeAllSettingChanged(bool checked) {
|
|
|
|
if (checked) {
|
|
manualFromDate->setEnabled(false);
|
|
manualToDate->setEnabled(false);
|
|
appsettings->setCValue(context->athlete->cyclist, GC_BM_LAST_TIMEFRAME, ALL);
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
BodyMeasuresDownload::dateRangeLastSettingChanged(bool checked) {
|
|
|
|
if (checked) {
|
|
manualFromDate->setEnabled(false);
|
|
manualToDate->setEnabled(false);
|
|
appsettings->setCValue(context->athlete->cyclist, GC_BM_LAST_TIMEFRAME, LAST);
|
|
}
|
|
}
|
|
|
|
void
|
|
BodyMeasuresDownload::dateRangeManualSettingChanged(bool checked) {
|
|
|
|
if (checked) {
|
|
manualFromDate->setEnabled(true);
|
|
manualToDate->setEnabled(true);
|
|
appsettings->setCValue(context->athlete->cyclist, GC_BM_LAST_TIMEFRAME, MANUAL);
|
|
}
|
|
}
|
|
|
|
void
|
|
BodyMeasuresDownload::downloadWithingsSettingChanged(bool checked) {
|
|
|
|
if (checked) {
|
|
appsettings->setCValue(context->athlete->cyclist, GC_BM_LAST_TYPE, WITHINGS);
|
|
}
|
|
}
|
|
|
|
void
|
|
BodyMeasuresDownload::downloadTPSettingChanged(bool checked) {
|
|
|
|
if (checked) {
|
|
appsettings->setCValue(context->athlete->cyclist, GC_BM_LAST_TYPE, TP);
|
|
}
|
|
}
|
|
|
|
void
|
|
BodyMeasuresDownload::downloadCSVSettingChanged(bool checked) {
|
|
|
|
if (checked) {
|
|
appsettings->setCValue(context->athlete->cyclist, GC_BM_LAST_TYPE, CSV);
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
BodyMeasuresDownload::downloadProgressStart(int total) {
|
|
progressBar->setMaximum(total);
|
|
}
|
|
void
|
|
BodyMeasuresDownload::downloadProgress(int current) {
|
|
progressBar->setValue(current);
|
|
}
|
|
|
|
void
|
|
BodyMeasuresDownload::downloadProgressEnd(int final) {
|
|
progressBar->setValue(final);
|
|
}
|