Workout Library Part 3 fixups

Some fixups to recent update for the workout
library support;

- ergDB download now updates the DB
- mainwindow menu options names more consistent
- sort order in the video and workout list
- hooks for import/drag and drop

Will now write the importer for drag-n-drop and
import workouts menu option.
This commit is contained in:
Mark Liversedge
2012-12-23 17:56:41 +00:00
parent bc0489f33e
commit 2a149bd454
11 changed files with 133 additions and 16 deletions

View File

@@ -17,6 +17,7 @@
*/
#include "ErgDBDownloadDialog.h"
#include "TrainDB.h"
ErgDBDownloadDialog::ErgDBDownloadDialog(MainWindow *main) : QDialog(main), main(main)
{
@@ -151,6 +152,9 @@ ErgDBDownloadDialog::downloadFiles()
// what format to export as?
QString type = RideFileFactory::instance().writeSuffixes().at(0);
// for library updating, transactional for 10x performance
trainDB->startLUW();
// loop through the table and export all selected
for(int i=0; i<files->invisibleRootItem()->childCount(); i++) {
@@ -158,7 +162,10 @@ ErgDBDownloadDialog::downloadFiles()
QApplication::processEvents();
// did they?
if (aborted == true) return; // user aborted!
if (aborted == true) {
trainDB->endLUW(); // need to commit whatever was copied.
return; // user aborted!
}
QTreeWidgetItem *current = files->invisibleRootItem()->child(i);
@@ -180,7 +187,6 @@ ErgDBDownloadDialog::downloadFiles()
// open success?
if (p->isValid()) {
delete p; // free memory!
if (QFile(filename).exists()) {
@@ -188,6 +194,7 @@ ErgDBDownloadDialog::downloadFiles()
// skip existing files
current->setText(5, "Exists already"); QApplication::processEvents();
fails++;
delete p; // free memory!
continue;
} else {
@@ -210,12 +217,15 @@ ErgDBDownloadDialog::downloadFiles()
downloads++;
current->setText(5, "Saved"); QApplication::processEvents();
trainDB->importWorkout(filename, p); // add to library
} else {
fails++;
current->setText(5, "Write failed"); QApplication::processEvents();
}
delete p; // free memory!
// couldn't parse
} else {
@@ -228,4 +238,6 @@ ErgDBDownloadDialog::downloadFiles()
}
}
// for library updating, transactional for 10x performance
trainDB->endLUW();
}

View File

@@ -375,6 +375,9 @@ LibrarySearchDialog::removeDirectory()
void
LibrarySearchDialog::updateDB()
{
// wipe away all user data before updating
trainDB->rebuildDB();
trainDB->startLUW();
// workouts
@@ -435,6 +438,24 @@ LibrarySearch::run()
// is a workout?
if (findWorkout && ErgFile::isWorkout(name)) emit foundWorkout(name);
}
// Now check and re-add references, if there are any
// these are files which were drag-n-dropped into the
// GC train window, but which were referenced not
// copied into the workout directory.
Library *l = Library::findLibrary("Media Library");
if (l) {
foreach(QString r, l->refs) {
if (!QFile(r).exists()) continue;
// is a video?
if (findMedia && helper.isMedia(r)) emit foundVideo(r);
// is a workout?
if (findWorkout && ErgFile::isWorkout(r)) emit foundWorkout(r);
}
}
emit done();
};

View File

@@ -34,6 +34,7 @@ class Library
public:
QString name; // e.g. Media Library
QList<QString> paths; // array of search paths for files in this library
QList<QString> refs; // array of drag-n-dropped files referenced not copied
static void initialise(QDir); // init
static Library *findLibrary(QString);

View File

@@ -40,6 +40,11 @@ bool LibraryParser::endElement( const QString&, const QString&, const QString &q
if (qName == "path") {
library->paths.append(buffer.trimmed());
}
// a reference
if (qName == "ref") {
library->refs.append(buffer.trimmed());
}
return TRUE;
}
@@ -87,6 +92,10 @@ LibraryParser::serialize(QDir home)
foreach(QString p, l->paths)
out << QString("\t<path>%1</path>\n").arg(p);
// paths...
foreach(QString r, l->refs)
out << QString("\t<ref>%1</ref>\n").arg(r);
// end document
out << "</library>\n";
}

View File

@@ -806,9 +806,10 @@ MainWindow::MainWindow(const QDir &home) :
optionsMenu->addAction(tr("Get &Zeo Data..."), this,
SLOT (downloadMeasuresFromZeo()));
optionsMenu->addSeparator();
optionsMenu->addAction(tr("Workout Wizard"), this, SLOT(showWorkoutWizard()));
optionsMenu->addAction(tr("Get Workouts from ErgDB"), this, SLOT(downloadErgDB()));
optionsMenu->addAction(tr("Manage Media/Workout Library"), this, SLOT(manageLibrary()));
optionsMenu->addAction(tr("Create a new workout..."), this, SLOT(showWorkoutWizard()));
optionsMenu->addAction(tr("Download workouts from ErgDB..."), this, SLOT(downloadErgDB()));
optionsMenu->addAction(tr("Import workouts or videos..."), this, SLOT(importWorkout()));
optionsMenu->addAction(tr("Scan disk for videos and workouts..."), this, SLOT(manageLibrary()));
#ifdef GC_HAVE_ICAL
optionsMenu->addSeparator();
@@ -1556,9 +1557,13 @@ MainWindow::dropEvent(QDropEvent *event)
QList<QUrl> urls = event->mimeData()->urls();
if (urls.isEmpty()) return;
// We have something to process then
RideImportWizard *dialog = new RideImportWizard (&urls, home, this);
dialog->process(); // do it!
if (currentWindow != trainWindow) {
// We have something to process then
RideImportWizard *dialog = new RideImportWizard (&urls, home, this);
dialog->process(); // do it!
} else {
//XXX hook for workout importer HERE
}
return;
}
@@ -1959,6 +1964,36 @@ MainWindow::uploadTtb()
}
}
/*----------------------------------------------------------------------
* Import Workout from Disk
*--------------------------------------------------------------------*/
void
MainWindow::importWorkout()
{
// go look at last place we imported workouts from...
QVariant lastDirVar = appsettings->value(this, GC_SETTINGS_LAST_WORKOUT_PATH);
QString lastDir = (lastDirVar != QVariant())
? lastDirVar.toString() : QDir::homePath();
// anything for now, we could add filters later
QStringList allFormats;
allFormats << "All files (*.*)";
QStringList fileNames = QFileDialog::getOpenFileNames(this, tr("Import from File"), lastDir,
allFormats.join(";;"));
// lets process them
if (!fileNames.isEmpty()) {
// save away last place we looked
lastDir = QFileInfo(fileNames.front()).absolutePath();
appsettings->setValue(GC_SETTINGS_LAST_WORKOUT_PATH, lastDir);
QStringList fileNamesCopy = fileNames; // QT doc says iterate over a copy
// import them via the workoutimporter
//XXX hook for workout importer HERE
}
}
/*----------------------------------------------------------------------
* ErgDB
*--------------------------------------------------------------------*/

View File

@@ -272,6 +272,7 @@ class MainWindow : public QMainWindow
void uploadCalendar(); // upload ride to calendar
#endif
void importFile();
void importWorkout();
void findBestIntervals();
void addIntervals();
void addIntervalForPowerPeaksForSecs(RideFile *ride, int windowSizeSecs, QString name);

View File

@@ -55,6 +55,7 @@
#define GC_BIO "bio"
#define GC_AVATAR "avatar"
#define GC_SETTINGS_LAST_IMPORT_PATH "mainwindow/lastImportPath"
#define GC_SETTINGS_LAST_WORKOUT_PATH "mainwindow/lastWorkoutPath"
#define GC_LAST_DOWNLOAD_DEVICE "mainwindow/lastDownloadDevice"
#define GC_LAST_DOWNLOAD_PORT "mainwindow/lastDownloadPort"
#define GC_CRANKLENGTH "crankLength"

View File

@@ -70,6 +70,18 @@ TrainDB::initDatabase(QDir home)
}
}
// rebuild effectively drops and recreates all tables
// but not the version table, since its about deleting
// user data (e.g. when rescanning their hard disk)
void
TrainDB::rebuildDB()
{
dropWorkoutTable();
createWorkoutTable();
dropVideoTable();
createVideoTable();
}
bool TrainDB::createVideoTable()
{
QSqlQuery query(dbconn);
@@ -149,12 +161,14 @@ bool TrainDB::createWorkoutTable()
rc = query.exec(createMetricTable);
// adding a space at the front of string to make manual mode always
// appear first in a sorted list is a bit of a hack, but works ok
QString manualErg = QString("INSERT INTO workouts (filepath, filename) values (\"//1\", \"%1\");")
.arg(tr("Manual Erg Mode"));
.arg(tr(" Manual Erg Mode"));
rc = query.exec(manualErg);
QString manualCrs = QString("INSERT INTO workouts (filepath, filename) values (\"//2\", \"%1\");")
.arg(tr("Manual Slope Mode"));
.arg(tr(" Manual Slope Mode"));
rc = query.exec(manualCrs);
// add row to version database

View File

@@ -52,6 +52,9 @@ class TrainDB : public QObject
bool importWorkout(QString pathname, ErgFile *ergFile);
bool importVideo(QString pathname); //XXX simple for now
// drop and recreate tables
void rebuildDB();
signals:
void dataChanged();

View File

@@ -86,8 +86,13 @@ TrainTool::TrainTool(MainWindow *parent, const QDir &home) : GcWindow(parent), h
videoModel->select();
while (videoModel->canFetchMore(QModelIndex())) videoModel->fetchMore(QModelIndex());
vsortModel = new QSortFilterProxyModel(this);
vsortModel->setSourceModel(videoModel);
vsortModel->setDynamicSortFilter(true);
vsortModel->sort(1, Qt::AscendingOrder); //filename order
mediaTree = new QTreeView;
mediaTree->setModel(videoModel);
mediaTree->setModel(vsortModel);
// hide unwanted columns and header
for(int i=0; i<mediaTree->header()->count(); i++)
@@ -95,7 +100,7 @@ TrainTool::TrainTool(MainWindow *parent, const QDir &home) : GcWindow(parent), h
mediaTree->setColumnHidden(1, false); // show filename
mediaTree->header()->hide();
mediaTree->setSortingEnabled(true);
mediaTree->setSortingEnabled(false);
mediaTree->setAlternatingRowColors(false);
mediaTree->setEditTriggers(QAbstractItemView::NoEditTriggers); // read-only
mediaTree->expandAll();
@@ -132,8 +137,13 @@ TrainTool::TrainTool(MainWindow *parent, const QDir &home) : GcWindow(parent), h
workoutModel->select();
while (workoutModel->canFetchMore(QModelIndex())) workoutModel->fetchMore(QModelIndex());
sortModel = new QSortFilterProxyModel(this);
sortModel->setSourceModel(workoutModel);
sortModel->setDynamicSortFilter(true);
sortModel->sort(1, Qt::AscendingOrder); //filename order
workoutTree = new QTreeView;
workoutTree->setModel(workoutModel);
workoutTree->setModel(sortModel);
// hide unwanted columns and header
for(int i=0; i<workoutTree->header()->count(); i++)
@@ -503,7 +513,8 @@ void
TrainTool::workoutTreeWidgetSelectionChanged()
{
QModelIndex current = workoutTree->currentIndex();
QString filename = workoutModel->data(workoutModel->index(current.row(), 0), Qt::DisplayRole).toString();
QModelIndex target = sortModel->mapToSource(current);
QString filename = workoutModel->data(workoutModel->index(target.row(), 0), Qt::DisplayRole).toString();
// wip away the current selected workout
if (ergFile) {
@@ -511,8 +522,13 @@ TrainTool::workoutTreeWidgetSelectionChanged()
ergFile = NULL;
}
if (filename == "") {
main->notifyErgFileSelected(NULL);
return;
}
// is it the auto mode?
int index = current.row();
int index = target.row();
if (index == 0) {
// ergo mode
main->notifyErgFileSelected(NULL);
@@ -585,8 +601,10 @@ TrainTool::listWorkoutFiles(const QDir &dir) const
void
TrainTool::mediaTreeWidgetSelectionChanged()
{
QModelIndex current = mediaTree->currentIndex();
QString filename = videoModel->data(videoModel->index(current.row(), 0), Qt::DisplayRole).toString();
QModelIndex target = vsortModel->mapToSource(current);
QString filename = videoModel->data(videoModel->index(target.row(), 0), Qt::DisplayRole).toString();
main->notifyMediaSelected(filename);
}

View File

@@ -182,6 +182,8 @@ class TrainTool : public GcWindow
QTreeWidget *deviceTree;
QTreeWidget *serverTree;
QTreeView *workoutTree;
QSortFilterProxyModel *sortModel; // sorting workout list
QSortFilterProxyModel *vsortModel; // sorting video list
QTreeView *mediaTree;
QTreeWidgetItem *allServers;