Workout Library Part 3 of 3

Linked up the training db to the train tool to
allow users to select from the db.

Further enhancements to the list could be made,
but leaving this till we move to OpenTrainer.

Still need to add drag and drop to copy into
the library or add reference to the file.

Fixes #301.
This commit is contained in:
Mark Liversedge
2012-12-22 19:05:36 +00:00
parent cf23bd2e89
commit a603dd5b5b
5 changed files with 119 additions and 127 deletions

View File

@@ -375,7 +375,7 @@ LibrarySearchDialog::removeDirectory()
void
LibrarySearchDialog::updateDB()
{
trainDB->connection().transaction();
trainDB->startLUW();
// workouts
foreach(QString ergFile, workoutsFound) {
@@ -391,7 +391,7 @@ LibrarySearchDialog::updateDB()
trainDB->importVideo(video);
}
trainDB->connection().commit();
trainDB->endLUW();
}
//

View File

@@ -25,7 +25,7 @@
// Rev Date Who What Changed
// 01 21 Dec 2012 Mark Liversedge Initial Build
static int TrainDBSchemaVersion = 2;
static int TrainDBSchemaVersion = 1;
TrainDB *trainDB;
TrainDB::TrainDB(QDir home) : home(home)
@@ -91,12 +91,16 @@ bool TrainDB::createVideoTable()
// we need to create it!
if (rc && createTables) {
QString createVideoTable = "create table videos (filename varchar primary key,"
QString createVideoTable = "create table videos (filepath varchar primary key,"
"filename varchar,"
"timestamp integer,"
"length integer);";
rc = query.exec(createVideoTable);
// insert the 'DVD' record for playing currently loaded DVD
//XXX rc = query.exec("INSERT INTO videos (filepath, filename) values (\"\", \"DVD\");");
// add row to version database
query.exec("DELETE FROM version where table_name = \"videos\"");
@@ -131,7 +135,8 @@ bool TrainDB::createWorkoutTable()
// we need to create it!
if (rc && createTables) {
QString createMetricTable = "create table workouts (filename varchar primary key,"
QString createMetricTable = "create table workouts (filepath varchar primary key,"
"filename,"
"timestamp integer,"
"description varchar,"
"source varchar,"
@@ -144,6 +149,14 @@ bool TrainDB::createWorkoutTable()
rc = query.exec(createMetricTable);
QString manualErg = QString("INSERT INTO workouts (filepath, filename) values (\"//1\", \"%1\");")
.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"));
rc = query.exec(manualCrs);
// add row to version database
query.exec("DELETE FROM version where table_name = \"workouts\"");
@@ -257,12 +270,13 @@ bool TrainDB::importWorkout(QString pathname, ErgFile *ergFile)
QDateTime timestamp = QDateTime::currentDateTime();
// zap the current row - if there is one
query.prepare("DELETE FROM workouts WHERE filename = ?;");
query.prepare("DELETE FROM workouts WHERE filepath = ?;");
query.addBindValue(pathname);
query.exec();
// construct an insert statement
QString insertStatement = "insert into workouts ( filename, "
QString insertStatement = "insert into workouts ( filepath, "
"filename,"
"timestamp,"
"description,"
"source,"
@@ -271,11 +285,12 @@ bool TrainDB::importWorkout(QString pathname, ErgFile *ergFile)
"coggan_tss,"
"coggan_if,"
"elevation,"
"grade ) values ( ?,?,?,?,?,?,?,?,?,? );";
"grade ) values ( ?,?,?,?,?,?,?,?,?,?,? );";
query.prepare(insertStatement);
// filename, timestamp, ride date
query.addBindValue(pathname);
query.addBindValue(QFileInfo(pathname).fileName());
query.addBindValue(timestamp);
query.addBindValue(ergFile->Name);
query.addBindValue(ergFile->Source);
@@ -298,16 +313,17 @@ bool TrainDB::importVideo(QString pathname)
QDateTime timestamp = QDateTime::currentDateTime();
// zap the current row - if there is one
query.prepare("DELETE FROM videos WHERE filename = ?;");
query.prepare("DELETE FROM videos WHERE filepath = ?;");
query.addBindValue(pathname);
query.exec();
// construct an insert statement
QString insertStatement = "insert into videos ( filename ) values ( ? );";
QString insertStatement = "insert into videos ( filepath,filename ) values ( ?,? );";
query.prepare(insertStatement);
// filename, timestamp, ride date
query.addBindValue(pathname);
query.addBindValue(QFileInfo(pathname).fileName());
// go do it!
bool rc = query.exec();

View File

@@ -26,9 +26,11 @@
#include <QtSql>
class ErgFile;
class TrainDB
class TrainDB : public QObject
{
Q_OBJECT
public:
// get connection name
@@ -44,9 +46,15 @@ class TrainDB
TrainDB(QDir home);
~TrainDB();
void startLUW() { dbconn.transaction(); }
void endLUW() { dbconn.commit(); emit dataChanged(); }
bool importWorkout(QString pathname, ErgFile *ergFile);
bool importVideo(QString pathname); //XXX simple for now
signals:
void dataChanged();
private:
QDir home;
QSqlDatabase db;

View File

@@ -49,6 +49,7 @@
#endif
#include <math.h> // isnan and isinf
#include "TrainDB.h"
TrainTool::TrainTool(MainWindow *parent, const QDir &home) : GcWindow(parent), home(home), main(parent)
{
@@ -79,16 +80,35 @@ TrainTool::TrainTool(MainWindow *parent, const QDir &home) : GcWindow(parent), h
#endif
#if defined Q_OS_MAC || defined GC_HAVE_VLC
mediaTree = new QTreeWidget;
mediaTree->setFrameStyle(QFrame::NoFrame);
mediaTree->setColumnCount(1);
mediaTree->setSelectionMode(QAbstractItemView::SingleSelection);
videoModel = new QSqlTableModel(this, trainDB->connection());
videoModel->setTable("videos");
videoModel->setEditStrategy(QSqlTableModel::OnManualSubmit);
videoModel->select();
while (videoModel->canFetchMore(QModelIndex())) videoModel->fetchMore(QModelIndex());
mediaTree = new QTreeView;
mediaTree->setModel(videoModel);
// hide unwanted columns and header
for(int i=0; i<mediaTree->header()->count(); i++)
mediaTree->setColumnHidden(i, true);
mediaTree->setColumnHidden(1, false); // show filename
mediaTree->header()->hide();
mediaTree->setAlternatingRowColors (false);
mediaTree->setIndentation(5);
allMedia = new QTreeWidgetItem(mediaTree, HEAD_TYPE);
allMedia->setText(0, tr("Video / Media"));
mediaTree->expandItem(allMedia);
mediaTree->setSortingEnabled(true);
mediaTree->setAlternatingRowColors(false);
mediaTree->setEditTriggers(QAbstractItemView::NoEditTriggers); // read-only
mediaTree->expandAll();
mediaTree->header()->setCascadingSectionResizes(true); // easier to resize this way
mediaTree->setContextMenuPolicy(Qt::CustomContextMenu);
mediaTree->header()->setStretchLastSection(true);
mediaTree->header()->setMinimumSectionSize(0);
mediaTree->header()->setFocusPolicy(Qt::NoFocus);
mediaTree->setFrameStyle(QFrame::NoFrame);
#ifdef Q_OS_MAC
mediaTree->header()->setSortIndicatorShown(false); // blue looks nasty
mediaTree->setAttribute(Qt::WA_MacShowFocusRect, 0);
#endif
#endif
deviceTree = new QTreeWidget;
@@ -106,17 +126,34 @@ TrainTool::TrainTool(MainWindow *parent, const QDir &home) : GcWindow(parent), h
deviceTree->expandItem(allDevices);
deviceTree->setContextMenuPolicy(Qt::CustomContextMenu);
workoutTree = new QTreeWidget;
workoutTree->setFrameStyle(QFrame::NoFrame);
workoutTree->setColumnCount(1);
workoutTree->setSelectionMode(QAbstractItemView::SingleSelection);
workoutTree->header()->hide();
workoutTree->setAlternatingRowColors (false);
workoutTree->setIndentation(5);
workoutModel = new QSqlTableModel(this, trainDB->connection());
workoutModel->setTable("workouts");
workoutModel->setEditStrategy(QSqlTableModel::OnManualSubmit);
workoutModel->select();
while (workoutModel->canFetchMore(QModelIndex())) workoutModel->fetchMore(QModelIndex());
allWorkouts = new QTreeWidgetItem(workoutTree, HEAD_TYPE);
allWorkouts->setText(0, tr("Workout Library"));
workoutTree->expandItem(allWorkouts);
workoutTree = new QTreeView;
workoutTree->setModel(workoutModel);
// hide unwanted columns and header
for(int i=0; i<workoutTree->header()->count(); i++)
workoutTree->setColumnHidden(i, true);
workoutTree->setColumnHidden(1, false); // show filename
workoutTree->header()->hide();
workoutTree->setFrameStyle(QFrame::NoFrame);
workoutTree->setAlternatingRowColors(false);
workoutTree->setEditTriggers(QAbstractItemView::NoEditTriggers); // read-only
workoutTree->expandAll();
workoutTree->header()->setCascadingSectionResizes(true); // easier to resize this way
workoutTree->setContextMenuPolicy(Qt::CustomContextMenu);
workoutTree->header()->setStretchLastSection(true);
workoutTree->header()->setMinimumSectionSize(0);
workoutTree->header()->setFocusPolicy(Qt::NoFocus);
workoutTree->setFrameStyle(QFrame::NoFrame);
#ifdef Q_OS_MAC
workoutTree->header()->setSortIndicatorShown(false); // blue looks nasty
workoutTree->setAttribute(Qt::WA_MacShowFocusRect, 0);
#endif
// TOOLBAR BUTTONS ETC
QHBoxLayout *toolallbuttons=new QHBoxLayout; // on toolbar
@@ -274,21 +311,16 @@ TrainTool::TrainTool(MainWindow *parent, const QDir &home) : GcWindow(parent), h
//connect(serverTree,SIGNAL(itemSelectionChanged()), this, SLOT(serverTreeWidgetSelectionChanged()));
connect(deviceTree,SIGNAL(itemSelectionChanged()), this, SLOT(deviceTreeWidgetSelectionChanged()));
connect(deviceTree,SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(deviceTreeMenuPopup(const QPoint &)));
connect(workoutTree,SIGNAL(itemSelectionChanged()), this, SLOT(workoutTreeWidgetSelectionChanged()));
#if defined Q_OS_MAC || defined GC_HAVE_VLC
connect(mediaTree,SIGNAL(itemSelectionChanged()), this, SLOT(mediaTreeWidgetSelectionChanged()));
connect(mediaTree->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
this, SLOT(mediaTreeWidgetSelectionChanged()));
#endif
connect(main, SIGNAL(configChanged()), this, SLOT(configChanged()));
connect(trainDB, SIGNAL(dataChanged()), this, SLOT(refresh()));
connect(workoutTree->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)), this, SLOT(workoutTreeWidgetSelectionChanged()));
// add a watch on all directories
QVariant workoutDir = appsettings->value(NULL, GC_WORKOUTDIR);
#if 0 //XXX Performance issues with this
watcher = boost::shared_ptr<QFileSystemWatcher>(new QFileSystemWatcher());
watcher->addPaths(workoutDir.toStringList());
connect(&*watcher,SIGNAL(directoryChanged(QString)),this,SLOT(configChanged()));
connect(&*watcher,SIGNAL(fileChanged(QString)),this,SLOT(configChanged()));
#endif
// set home
main = parent;
@@ -337,6 +369,15 @@ TrainTool::TrainTool(MainWindow *parent, const QDir &home) : GcWindow(parent), h
}
void
TrainTool::refresh()
{
videoModel->select();
while (videoModel->canFetchMore(QModelIndex())) videoModel->fetchMore(QModelIndex());
workoutModel->select();
while (workoutModel->canFetchMore(QModelIndex())) workoutModel->fetchMore(QModelIndex());
}
void
TrainTool::configChanged()
{
@@ -394,46 +435,6 @@ TrainTool::configChanged()
deviceTree->setCurrentItem(allDevices->child(0));
}
// WORKOUTS
// zap whats there
QList<QTreeWidgetItem *> workouts = allWorkouts->takeChildren();
for (int i=0; i<workouts.count(); i++) delete workouts.at(i);
// standard workouts - ergo and slope
QTreeWidgetItem *ergomode = new QTreeWidgetItem(allWorkouts, WORKOUT_TYPE);
ergomode->setText(0, tr("Manual Ergo Mode"));
QTreeWidgetItem *slopemode = new QTreeWidgetItem(allWorkouts, WORKOUT_TYPE);
slopemode->setText(0, tr("Manual Slope Mode"));
// add all the workouts in the library
QVariant workoutDir = appsettings->value(this, GC_WORKOUTDIR);
QStringListIterator w(listWorkoutFiles(workoutDir.toString()));
while (w.hasNext()) {
QString name = w.next();
QTreeWidgetItem *work = new QTreeWidgetItem(allWorkouts, WORKOUT_TYPE);
work->setText(0, name);
}
#if defined Q_OS_MAC || defined GC_HAVE_VLC
// MEDIA
QList<QTreeWidgetItem *> media = allMedia->takeChildren();
for (int i=0; i<media.count(); i++) delete media.at(i);
#ifndef Q_OS_MAC
// add dvd playback via VLC
QTreeWidgetItem *dvd = new QTreeWidgetItem(allMedia, WORKOUT_TYPE);
dvd->setText(0, "DVD");
#endif
MediaHelper mediaHelper;
foreach(QString video, mediaHelper.listMedia(QDir(workoutDir.toString()))) {
// add a media line for the video (it might be a song though...)
QTreeWidgetItem *media = new QTreeWidgetItem(allMedia, WORKOUT_TYPE);
media->setText(0, video);
}
#endif
// Athlete
FTP=285; // default to 285 if zones are not set
int range = main->zones()->whichRange(QDate::currentDate());
@@ -501,18 +502,8 @@ TrainTool::devices()
void
TrainTool::workoutTreeWidgetSelectionChanged()
{
assert(workoutTree->selectedItems().size() <= 1);
if (workoutTree->selectedItems().isEmpty())
workout = NULL;
else {
QTreeWidgetItem *which = workoutTree->selectedItems().first();
if (which->type() != WORKOUT_TYPE)
workout = NULL;
else
workout = which;
}
//int mode;
QModelIndex current = workoutTree->currentIndex();
QString filename = workoutModel->data(workoutModel->index(current.row(), 0), Qt::DisplayRole).toString();
// wip away the current selected workout
if (ergFile) {
@@ -520,15 +511,8 @@ TrainTool::workoutTreeWidgetSelectionChanged()
ergFile = NULL;
}
// which one is selected?
if (currentWorkout() == NULL || currentWorkout()->type() != WORKOUT_TYPE) {
main->notifyErgFileSelected(NULL);
setLabels();
return;
}
// is it the auto mode?
int index = workoutItems()->indexOfChild((QTreeWidgetItem *)currentWorkout());
int index = current.row();
if (index == 0) {
// ergo mode
main->notifyErgFileSelected(NULL);
@@ -545,10 +529,7 @@ TrainTool::workoutTreeWidgetSelectionChanged()
//ergPlot->setVisible(false);
} else {
// workout mode
QVariant workoutDir = appsettings->value(this, GC_WORKOUTDIR);
QString fileName = workoutDir.toString() + "/" + currentWorkout()->text(0); // filename
ergFile = new ErgFile(fileName, mode, main);
ergFile = new ErgFile(filename, mode, main);
if (ergFile->isValid()) {
status |= RT_WORKOUT;
@@ -604,26 +585,9 @@ TrainTool::listWorkoutFiles(const QDir &dir) const
void
TrainTool::mediaTreeWidgetSelectionChanged()
{
assert(mediaTree->selectedItems().size() <= 1);
if (mediaTree->selectedItems().isEmpty())
media = NULL;
else {
QTreeWidgetItem *which = mediaTree->selectedItems().first();
if (which->type() != WORKOUT_TYPE)
media = NULL;
else
media = which;
}
// which one is selected?
if (currentMedia() == NULL || currentMedia()->type() != WORKOUT_TYPE) {
main->notifyMediaSelected("");
return;
}
QVariant workoutDir = appsettings->value(this, GC_WORKOUTDIR);
QString fileName = workoutDir.toString() + "/" + currentMedia()->text(0); // filename
main->notifyMediaSelected(fileName);
QModelIndex current = mediaTree->currentIndex();
QString filename = videoModel->data(videoModel->index(current.row(), 0), Qt::DisplayRole).toString();
main->notifyMediaSelected(filename);
}
/*--------------------------------------------------------------------------------

View File

@@ -35,6 +35,7 @@
#include "math.h" // for round()
#include "Units.h" // for MILES_PER_KM
#include <QDebug>
#include <QSqlTableModel>
// Status settings
#define RT_MODE_ERGO 0x0001 // load generation modes
@@ -84,7 +85,6 @@ class TrainTool : public GcWindow
const QTreeWidgetItem *currentWorkout() { return workout; }
const QTreeWidgetItem *currentMedia() { return media; }
const QTreeWidgetItem *workoutItems() { return allWorkouts; }
const QTreeWidgetItem *mediaItems() { return allMedia; }
const QTreeWidgetItem *currentServer() { return server; }
const QTreeWidgetItem *serverItems() { return allServers; }
@@ -133,6 +133,8 @@ class TrainTool : public GcWindow
void deviceTreeMenuPopup(const QPoint &);
void deleteDevice();
void refresh(); // when TrainDB is updated...
//XXX void workoutTreeMenuPopup(const QPoint &);
//XXX void mediaTreeMenuPopup(const QPoint &);
@@ -174,13 +176,15 @@ class TrainTool : public GcWindow
QWidget *toolbarButtons;
QTreeWidget *workoutTree;
QSqlTableModel *videoModel;
QSqlTableModel *workoutModel;
QTreeWidget *deviceTree;
QTreeWidget *serverTree;
QTreeWidget *mediaTree;
QTreeView *workoutTree;
QTreeView *mediaTree;
QTreeWidgetItem *allServers;
QTreeWidgetItem *allMedia;
QTreeWidgetItem *allDevices;
QTreeWidgetItem *server;
QTreeWidgetItem *allWorkouts;