mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-14 16:39:57 +00:00
... create folder structure for existing and new athletes ... upgrade existing athlete to new structure when opening / incl. logging and error handling ... convert any "Downloaded" file to .JSON directly - store source file in /downloads ... convert any "Imported" file to .JSON directly - store source file in /imports
391 lines
12 KiB
C++
391 lines
12 KiB
C++
/*
|
|
* Copyright (c) 2012 Mark Liversedge (liversedge@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 "TrainDB.h"
|
|
#include "ErgFile.h"
|
|
|
|
// DB Schema Version - YOU MUST UPDATE THIS IF THE TRAIN DB SCHEMA CHANGES
|
|
|
|
// Revision History
|
|
// Rev Date Who What Changed
|
|
// 01 21 Dec 2012 Mark Liversedge Initial Build
|
|
|
|
static int TrainDBSchemaVersion = 1;
|
|
TrainDB *trainDB;
|
|
|
|
TrainDB::TrainDB(QDir home) : home(home)
|
|
{
|
|
// we live above the rider directory
|
|
initDatabase(home);
|
|
}
|
|
|
|
void TrainDB::closeConnection()
|
|
{
|
|
db->database(sessionid).close();
|
|
}
|
|
|
|
TrainDB::~TrainDB()
|
|
{
|
|
if (db) {
|
|
db->close();
|
|
delete db;
|
|
QSqlDatabase::removeDatabase(sessionid);
|
|
}
|
|
}
|
|
|
|
void
|
|
TrainDB::initDatabase(QDir home)
|
|
{
|
|
|
|
|
|
if(db->database(sessionid).isOpen()) return;
|
|
|
|
// get a connection
|
|
sessionid = "train";
|
|
db = new QSqlDatabase(QSqlDatabase::addDatabase("QSQLITE", sessionid));
|
|
db->setDatabaseName(home.canonicalPath() + "/trainDB");
|
|
|
|
if (!db->database(sessionid).isOpen()) {
|
|
QMessageBox::critical(0, qApp->translate("TrainDB","Cannot open database"),
|
|
qApp->translate("TrainDB","Unable to establish a database connection.\n"
|
|
"This feature requires SQLite support. Please read "
|
|
"the Qt SQL driver documentation for information how "
|
|
"to build it.\n\n"
|
|
"Click Cancel to exit."), QMessageBox::Cancel);
|
|
} else {
|
|
|
|
// create database - does nothing if its already there
|
|
createDatabase();
|
|
}
|
|
}
|
|
|
|
// 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(db->database(sessionid));
|
|
bool rc;
|
|
bool createTables = true;
|
|
|
|
// does the table exist?
|
|
rc = query.exec("SELECT name FROM sqlite_master WHERE type='table' ORDER BY name;");
|
|
if (rc) {
|
|
while (query.next()) {
|
|
|
|
QString table = query.value(0).toString();
|
|
if (table == "videos") {
|
|
createTables = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// we need to create it!
|
|
if (rc && createTables) {
|
|
|
|
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
|
|
// need to resolve DVD playback in v3.1, there is an open feature request for this.
|
|
// rc = query.exec("INSERT INTO videos (filepath, filename) values (\"\", \"DVD\");");
|
|
|
|
// add row to version database
|
|
query.exec("DELETE FROM version where table_name = \"videos\"");
|
|
|
|
// insert into table
|
|
query.prepare("INSERT INTO version (table_name, schema_version, creation_date) values (?,?,?);");
|
|
query.addBindValue("videos");
|
|
query.addBindValue(TrainDBSchemaVersion);
|
|
query.addBindValue(QDateTime::currentDateTime().toTime_t());
|
|
rc = query.exec();
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
bool TrainDB::createWorkoutTable()
|
|
{
|
|
QSqlQuery query(db->database(sessionid));
|
|
bool rc;
|
|
bool createTables = true;
|
|
|
|
// does the table exist?
|
|
rc = query.exec("SELECT name FROM sqlite_master WHERE type='table' ORDER BY name;");
|
|
if (rc) {
|
|
while (query.next()) {
|
|
|
|
QString table = query.value(0).toString();
|
|
if (table == "workouts") {
|
|
createTables = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// we need to create it!
|
|
if (rc && createTables) {
|
|
|
|
QString createMetricTable = "create table workouts (filepath varchar primary key,"
|
|
"filename,"
|
|
"timestamp integer,"
|
|
"description varchar,"
|
|
"source varchar,"
|
|
"ftp integer,"
|
|
"length integer,"
|
|
"coggan_tss integer,"
|
|
"coggan_if integer,"
|
|
"elevation integer,"
|
|
"grade double );";
|
|
|
|
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"));
|
|
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\"");
|
|
|
|
// insert into table
|
|
query.prepare("INSERT INTO version (table_name, schema_version, creation_date) values (?,?,?);");
|
|
query.addBindValue("workouts");
|
|
query.addBindValue(TrainDBSchemaVersion);
|
|
query.addBindValue(QDateTime::currentDateTime().toTime_t());
|
|
rc = query.exec();
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
bool TrainDB::dropVideoTable()
|
|
{
|
|
QSqlQuery query("DROP TABLE videos", db->database(sessionid));
|
|
bool rc = query.exec();
|
|
return rc;
|
|
}
|
|
|
|
bool TrainDB::dropWorkoutTable()
|
|
{
|
|
QSqlQuery query("DROP TABLE workouts", db->database(sessionid));
|
|
bool rc = query.exec();
|
|
return rc;
|
|
}
|
|
|
|
bool TrainDB::createDatabase()
|
|
{
|
|
// check schema version and if missing recreate database
|
|
checkDBVersion();
|
|
|
|
// Workouts
|
|
createWorkoutTable();
|
|
createVideoTable();
|
|
|
|
return true;
|
|
}
|
|
|
|
void TrainDB::checkDBVersion()
|
|
{
|
|
// can we get a version number?
|
|
QSqlQuery query("SELECT table_name, schema_version, creation_date from version;", db->database(sessionid));
|
|
|
|
bool rc = query.exec();
|
|
|
|
if (!rc) {
|
|
// we couldn't read the version table properly
|
|
// it must be out of date!!
|
|
|
|
QSqlQuery dropM("DROP TABLE version", db->database(sessionid));
|
|
dropM.exec();
|
|
|
|
// recreate version table and add one entry
|
|
QSqlQuery version("CREATE TABLE version ( table_name varchar primary key, schema_version integer, creation_date date );", db->database(sessionid));
|
|
version.exec();
|
|
|
|
// wipe away whatever (if anything is there)
|
|
dropWorkoutTable();
|
|
createWorkoutTable();
|
|
|
|
dropVideoTable();
|
|
createVideoTable();
|
|
return;
|
|
}
|
|
|
|
// ok we checked out ok, so lets adjust db schema to reflect
|
|
// tne current version / crc
|
|
bool dropWorkout = false;
|
|
bool dropVideo = false;
|
|
while (query.next()) {
|
|
|
|
QString table_name = query.value(0).toString();
|
|
int currentversion = query.value(1).toInt();
|
|
|
|
if (table_name == "workouts" && currentversion != TrainDBSchemaVersion) dropWorkout = true;
|
|
if (table_name == "videos" && currentversion != TrainDBSchemaVersion) dropVideo = true;
|
|
}
|
|
query.finish();
|
|
|
|
// "workouts" table, is it up-to-date?
|
|
if (dropWorkout) dropWorkoutTable();
|
|
if (dropVideo) dropVideoTable();
|
|
}
|
|
|
|
int TrainDB::getCount()
|
|
{
|
|
// how many workouts are there?
|
|
QSqlQuery query("SELECT count(*) from workouts;", db->database(sessionid));
|
|
bool rc = query.exec();
|
|
|
|
if (rc) {
|
|
while (query.next()) {
|
|
return query.value(0).toInt();
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int TrainDB::getDBVersion()
|
|
{
|
|
int schema_version = -1;
|
|
|
|
// can we get a version number?
|
|
QSqlQuery query("SELECT schema_version from version;", db->database(sessionid));
|
|
|
|
bool rc = query.exec();
|
|
|
|
if (rc) {
|
|
while (query.next()) {
|
|
if (query.value(0).toInt() > schema_version)
|
|
schema_version = query.value(0).toInt();
|
|
}
|
|
}
|
|
query.finish();
|
|
return schema_version;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
* CRUD routines
|
|
*----------------------------------------------------------------------*/
|
|
|
|
bool TrainDB::deleteWorkout(QString pathname)
|
|
{
|
|
QSqlQuery query(db->database(sessionid));
|
|
QDateTime timestamp = QDateTime::currentDateTime();
|
|
|
|
// zap the current row - if there is one
|
|
query.prepare("DELETE FROM workouts WHERE filepath = ?;");
|
|
query.addBindValue(pathname);
|
|
|
|
return query.exec();
|
|
}
|
|
|
|
bool TrainDB::importWorkout(QString pathname, ErgFile *ergFile)
|
|
{
|
|
QSqlQuery query(db->database(sessionid));
|
|
QDateTime timestamp = QDateTime::currentDateTime();
|
|
|
|
// zap the current row - if there is one
|
|
query.prepare("DELETE FROM workouts WHERE filepath = ?;");
|
|
query.addBindValue(pathname);
|
|
query.exec();
|
|
|
|
// construct an insert statement
|
|
QString insertStatement = "insert into workouts ( filepath, "
|
|
"filename,"
|
|
"timestamp,"
|
|
"description,"
|
|
"source,"
|
|
"ftp,"
|
|
"length,"
|
|
"coggan_tss,"
|
|
"coggan_if,"
|
|
"elevation,"
|
|
"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);
|
|
query.addBindValue(ergFile->Ftp);
|
|
query.addBindValue((int)ergFile->Duration);
|
|
query.addBindValue(ergFile->TSS);
|
|
query.addBindValue(ergFile->IF);
|
|
query.addBindValue(ergFile->ELE);
|
|
query.addBindValue(ergFile->GRADE);
|
|
|
|
// go do it!
|
|
bool rc = query.exec();
|
|
|
|
return rc;
|
|
}
|
|
|
|
bool TrainDB::deleteVideo(QString pathname)
|
|
{
|
|
QSqlQuery query(db->database(sessionid));
|
|
QDateTime timestamp = QDateTime::currentDateTime();
|
|
|
|
// zap the current row - if there is one
|
|
query.prepare("DELETE FROM videos WHERE filepath = ?;");
|
|
query.addBindValue(pathname);
|
|
return query.exec();
|
|
}
|
|
|
|
bool TrainDB::importVideo(QString pathname)
|
|
{
|
|
QSqlQuery query(db->database(sessionid));
|
|
QDateTime timestamp = QDateTime::currentDateTime();
|
|
|
|
// zap the current row - if there is one
|
|
query.prepare("DELETE FROM videos WHERE filepath = ?;");
|
|
query.addBindValue(pathname);
|
|
query.exec();
|
|
|
|
// construct an insert statement
|
|
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();
|
|
|
|
return rc;
|
|
}
|