summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjasplin <qt-info@nokia.com>2010-04-09 12:21:42 +0200
committerjasplin <qt-info@nokia.com>2010-04-09 12:21:42 +0200
commita62277ff1c05b222a42d0dd670443f92640df655 (patch)
treef5faca2ccdb3286cfde2f41ac167e5349c745bca
parente4adf9f130eeca2f43350ddc006f3659827dd72d (diff)
Added save/load feature for index configs.
It is now possible to save, load, and delete a performance index configuration (i.e. all the settings in the 'Index' section in the BM web app).
-rw-r--r--doc/er-diagram.diabin4088 -> 5901 bytes
-rw-r--r--src/bm/bmrequest.cpp596
-rw-r--r--src/bm/bmrequest.h91
-rw-r--r--src/bm/plotter.cpp1
-rw-r--r--src/bmclient/main.cpp180
-rw-r--r--src/bmserver/main.cpp75
-rw-r--r--src/bmweb/global.js16
-rw-r--r--src/bmweb/indexsection.js421
8 files changed, 1309 insertions, 71 deletions
diff --git a/doc/er-diagram.dia b/doc/er-diagram.dia
index e187403..6db08a4 100644
--- a/doc/er-diagram.dia
+++ b/doc/er-diagram.dia
Binary files differ
diff --git a/src/bm/bmrequest.cpp b/src/bm/bmrequest.cpp
index dbbdf4d..fffcb34 100644
--- a/src/bm/bmrequest.cpp
+++ b/src/bm/bmrequest.cpp
@@ -92,6 +92,14 @@ BMRequest * BMRequest::create(const QByteArray &data, const QString &msgType)
request = new BMRequest_GetResult(doc);
else if (type == "IndexGetValues")
request = new BMRequest_IndexGetValues(doc);
+ else if (type == "IndexGetConfigs")
+ request = new BMRequest_IndexGetConfigs(doc);
+ else if (type == "IndexGetConfig")
+ request = new BMRequest_IndexGetConfig(doc);
+ else if (type == "IndexPutConfig")
+ request = new BMRequest_IndexPutConfig(doc);
+ else if (type == "IndexDeleteConfig")
+ request = new BMRequest_IndexDeleteConfig(doc);
#ifdef BMDEBUG
else
qDebug() << "invalid request type:" << type;
@@ -278,7 +286,7 @@ bool BMRequest::getId(
// ... and get its id ...
if (!BMMisc::getLastInsertId(query, id)) {
- Q_ASSERT(false); // ### Backup code (involving an separate query) to go here!
+ Q_ASSERT(false); // ### Backup code (involving a separate query) to go here!
deleteQuery(query);
*reply = errorReply(*query, name(), "failed to get last inserted ID (in getId())");
return false;
@@ -433,7 +441,7 @@ bool BMRequest::getOrInsertContextId(
// ... and get its ID ...
if (!BMMisc::getLastInsertId(query, id)) {
- Q_ASSERT(false); // ### Backup code (involving an separate query) to go here!
+ Q_ASSERT(false); // ### Backup code (involving a separate query) to go here!
deleteQuery(query);
*reply = errorReply(*query, name(), "failed to get ID of last context inserted");
return false;
@@ -547,7 +555,7 @@ bool BMRequest::getOrInsertBMContextId(
// ... and get its ID ...
if (!BMMisc::getLastInsertId(query, id)) {
- Q_ASSERT(false); // ### Backup code (involving an separate query) to go here!
+ Q_ASSERT(false); // ### Backup code (involving a separate query) to go here!
deleteQuery(query);
*reply = errorReply(*query, name(), "failed to get ID of last bmcontext inserted");
return false;
@@ -596,8 +604,6 @@ bool BMRequest::insertOrReplaceResult(
return false;
}
- deleteQuery(query);
-
} else {
// ... it doesn't, so insert the value ...
@@ -613,12 +619,10 @@ bool BMRequest::insertOrReplaceResult(
deleteQuery(query, Rollback);
return false;
}
-
- database->commit();
-
- deleteQuery(query);
}
+ deleteQuery(query, Commit);
+
return true;
}
@@ -5785,3 +5789,577 @@ void BMRequest_IndexGetValues::handleReply_Image(const QStringList &args) const
BMMisc::printHTMLErrorPage(QString("failed to compute index values: %1").arg(error));
}
}
+
+
+// --- IndexGetConfigs ---
+QByteArray BMRequest_IndexGetConfigs::toRequestBuffer(QString *)
+{
+ const QString request = QString("<request type=\"%1\" />").arg(name());
+ return xmlConvert(request);
+}
+
+QByteArray BMRequest_IndexGetConfigs::toReplyBuffer()
+{
+ QString reply = QString("<reply type=\"%1\">").arg(name());
+ QSqlQuery *query;
+
+ query = createQuery();
+ if (!query->exec("SELECT name FROM indexConfig ORDER BY name ASC;")) {
+ reply = errorReply(*query, name(), QString("(exec() failed)"));
+ deleteQuery(query);
+ return xmlConvert(reply);
+ }
+ while (query->next())
+ reply += QString("<indexConfig name=\"%1\" />").arg(query->value(0).toString());
+ deleteQuery(query);
+
+ reply += "</reply>";
+
+ return xmlConvert(reply);
+}
+
+void BMRequest_IndexGetConfigs::handleReply_Raw(const QStringList &args) const
+{
+ printf("NOTE: raw output not supported yet; printing JSON output for now:\n\n");
+ handleReply_JSON(args);
+}
+
+void BMRequest_IndexGetConfigs::handleReply_JSON(const QStringList &args) const
+{
+ Q_UNUSED(args);
+
+ const QString error =
+ doc.elementsByTagName("reply").at(0).toElement().attributeNode("error").value();
+
+ QString reply = QString("{");
+
+ if (error.isEmpty()) {
+
+ reply += "\n\"names\": [";
+
+ QDomNodeList indexConfigNodes = doc.elementsByTagName("indexConfig");
+ for (int i = 0; i < indexConfigNodes.size(); ++i) {
+ QDomElement indexConfigElem = indexConfigNodes.at(i).toElement();
+ reply += QString("%1\n\"%2\"")
+ .arg((i == 0) ? "" : ", ")
+ .arg(indexConfigElem.attributeNode("name").value());
+ }
+
+ reply += "\n]\n}";
+
+ } else {
+ reply = QString("{\"error\": \"error getting index configurations: %1\"}").arg(error);
+ }
+
+ BMMisc::printJSONOutput(reply);
+}
+
+
+// --- IndexGetConfig ---
+QByteArray BMRequest_IndexGetConfig::toRequestBuffer(QString *)
+{
+ const QString request =
+ QString("<request type=\"%1\"><args configName=\"%2\" /></request>")
+ .arg(name()).arg(configName);
+ return xmlConvert(request);
+}
+
+QByteArray BMRequest_IndexGetConfig::toReplyBuffer()
+{
+ // Get config name ...
+ QDomElement argsElem = doc.elementsByTagName("args").at(0).toElement();
+ configName = argsElem.attributeNode("configName").value();
+
+ QString reply = QString("<reply type=\"%1\">").arg(name());
+ QSqlQuery *query;
+
+ // Get config ID and non-filter attributes ...
+ query = createQuery();
+ if (!query->exec(
+ QString(
+ "SELECT id, baseTimestamp, loEvalTimestamp, hiEvalTimestamp, evalTimestep, "
+ "medianWinSize FROM indexConfig WHERE name='%1';").arg(configName))) {
+ reply = errorReply(*query, name(), QString("failed to get config (exec() failed (1))"));
+ deleteQuery(query);
+ return xmlConvert(reply);
+ }
+ int configId;
+ int baseTimestamp;
+ int evalLoTime;
+ int evalHiTime;
+ int evalStep;
+ int medianWinSize;
+ if (query->next()) {
+ configId = query->value(0).toInt();
+ baseTimestamp = query->value(1).toInt();
+ evalLoTime = query->value(2).toInt();
+ evalHiTime = query->value(3).toInt();
+ evalStep = query->value(4).toInt();
+ medianWinSize = query->value(5).toInt();
+ } else {
+ reply = errorReply(*query, name(), QString("index config '%1' not found").arg(configName));
+ deleteQuery(query);
+ return xmlConvert(reply);
+ }
+ deleteQuery(query);
+
+
+ // Get filter attributes (### note the refactoring potential here) ...
+
+ // ... test case ...
+ query = createQuery();
+ if (!query->exec(
+ QString("SELECT name FROM icTestCase WHERE indexConfigId=%1 ORDER BY name ASC;")
+ .arg(configId))) {
+ reply = errorReply(*query, name(), QString("failed to get config (exec() failed (2))"));
+ deleteQuery(query);
+ return xmlConvert(reply);
+ }
+ QStringList testCaseFilter;
+ while (query->next())
+ testCaseFilter.append(query->value(0).toString());
+ deleteQuery(query);
+
+ // ... metric ...
+ query = createQuery();
+ if (!query->exec(
+ QString("SELECT name FROM icMetric WHERE indexConfigId=%1 ORDER BY name ASC;")
+ .arg(configId))) {
+ reply = errorReply(*query, name(), QString("failed to get config (exec() failed (2))"));
+ deleteQuery(query);
+ return xmlConvert(reply);
+ }
+ QStringList metricFilter;
+ while (query->next())
+ metricFilter.append(query->value(0).toString());
+ deleteQuery(query);
+
+ // ... platform ...
+ query = createQuery();
+ if (!query->exec(
+ QString("SELECT name FROM icPlatform WHERE indexConfigId=%1 ORDER BY name ASC;")
+ .arg(configId))) {
+ reply = errorReply(*query, name(), QString("failed to get config (exec() failed (2))"));
+ deleteQuery(query);
+ return xmlConvert(reply);
+ }
+ QStringList platformFilter;
+ while (query->next())
+ platformFilter.append(query->value(0).toString());
+ deleteQuery(query);
+
+ // ... host ...
+ query = createQuery();
+ if (!query->exec(
+ QString("SELECT name FROM icHost WHERE indexConfigId=%1 ORDER BY name ASC;")
+ .arg(configId))) {
+ reply = errorReply(*query, name(), QString("failed to get config (exec() failed (2))"));
+ deleteQuery(query);
+ return xmlConvert(reply);
+ }
+ QStringList hostFilter;
+ while (query->next())
+ hostFilter.append(query->value(0).toString());
+ deleteQuery(query);
+
+ // ... branch ...
+ query = createQuery();
+ if (!query->exec(
+ QString("SELECT name FROM icBranch WHERE indexConfigId=%1 ORDER BY name ASC;")
+ .arg(configId))) {
+ reply = errorReply(*query, name(), "failed to get config (exec() failed (2))");
+ deleteQuery(query);
+ return xmlConvert(reply);
+ }
+ QStringList branchFilter;
+ while (query->next())
+ branchFilter.append(query->value(0).toString());
+ deleteQuery(query);
+
+
+ //--------------------------------------
+
+ reply += QString(
+ "<args baseTimestamp=\"%1\" evalLoTime=\"%2\" evalHiTime=\"%3\" evalStep=\"%4\" "
+ "medianWinSize=\"%5\" />")
+ .arg(baseTimestamp)
+ .arg(evalLoTime)
+ .arg(evalHiTime)
+ .arg(evalStep)
+ .arg(medianWinSize);
+
+ for (int i = 0; i < testCaseFilter.size(); ++i)
+ reply += QString("<testCase name=\"%1\" />").arg(testCaseFilter.at(i));
+ for (int i = 0; i < metricFilter.size(); ++i)
+ reply += QString("<metric name=\"%1\" />").arg(metricFilter.at(i));
+ for (int i = 0; i < platformFilter.size(); ++i)
+ reply += QString("<platform name=\"%1\" />").arg(platformFilter.at(i));
+ for (int i = 0; i < hostFilter.size(); ++i)
+ reply += QString("<host name=\"%1\" />").arg(hostFilter.at(i));
+ for (int i = 0; i < branchFilter.size(); ++i)
+ reply += QString("<branch name=\"%1\" />").arg(branchFilter.at(i));
+
+ reply += "</reply>";
+
+ return xmlConvert(reply);
+}
+
+void BMRequest_IndexGetConfig::handleReply_Raw(const QStringList &args) const
+{
+ printf("NOTE: raw output not supported yet; printing JSON output for now:\n\n");
+ handleReply_JSON(args);
+}
+
+void BMRequest_IndexGetConfig::handleReply_JSON(const QStringList &args) const
+{
+ Q_UNUSED(args);
+
+ const QString error =
+ doc.elementsByTagName("reply").at(0).toElement().attributeNode("error").value();
+
+ QString reply = QString("{");
+
+ if (error.isEmpty()) {
+
+ QDomElement argsElem = doc.elementsByTagName("args").at(0).toElement();
+ bool ok;
+
+ // Non-filter values ...
+
+ reply += QString("\n\"baseTimestamp\": %1")
+ .arg(argsElem.attributeNode("baseTimestamp").value().toInt(&ok));
+ Q_ASSERT(ok);
+
+ reply += QString(",\n\"evalLoTime\": %1")
+ .arg(argsElem.attributeNode("evalLoTime").value().toInt(&ok));
+ Q_ASSERT(ok);
+
+ reply += QString(",\n\"evalHiTime\": %1")
+ .arg(argsElem.attributeNode("evalHiTime").value().toInt(&ok));
+ Q_ASSERT(ok);
+
+ reply += QString(",\n\"evalStep\": %1")
+ .arg(argsElem.attributeNode("evalStep").value().toInt(&ok));
+ Q_ASSERT(ok);
+
+ reply += QString(",\n\"medianWinSize\": %1")
+ .arg(argsElem.attributeNode("medianWinSize").value().toInt(&ok));
+ Q_ASSERT(ok);
+
+ // Filter values ...
+
+ const QStringList filterNames =
+ QStringList() << "testCase" << "metric" << "platform" << "host" << "branch";
+ for (int i = 0; i < filterNames.size(); ++i) {
+ reply += QString(",\n\"%1Filter\": [").arg(filterNames.at(i));
+ QDomNodeList filterNodes = doc.elementsByTagName(filterNames.at(i));
+ for (int j = 0; j < filterNodes.size(); ++j) {
+ QDomElement filterElem = filterNodes.at(j).toElement();
+ reply += QString("%1\"%2\"")
+ .arg((j == 0) ? "" : ", ")
+ .arg(filterElem.attributeNode("name").value());
+ }
+ reply += "]";
+ }
+
+ reply += "\n}";
+
+ } else {
+ reply = QString("{\"error\": \"error getting index configuration: %1\"}").arg(error);
+ }
+
+ BMMisc::printJSONOutput(reply);
+}
+
+
+// --- IndexPutConfig ---
+QByteArray BMRequest_IndexPutConfig::toRequestBuffer(QString *)
+{
+ QString request = QString("<request type=\"%1\">").arg(name());
+
+ // Non-filter values ...
+ request += QString(
+ "<args configName=\"%1\" baseTimestamp=\"%2\" evalLoTime=\"%3\" "
+ "evalHiTime=\"%4\" evalStep=\"%5\" medianWinSize=\"%6\" />")
+ .arg(configName)
+ .arg(baseTimestamp)
+ .arg(evalLoTime)
+ .arg(evalHiTime)
+ .arg(evalStep)
+ .arg(medianWinSize);
+
+ // Filter values ...
+ for (int i = 0; i < testCaseFilter.size(); ++i)
+ request += QString("<testCase name=\"%1\" />").arg(testCaseFilter.at(i));
+ for (int i = 0; i < metricFilter.size(); ++i)
+ request += QString("<metric name=\"%1\" />").arg(metricFilter.at(i));
+ for (int i = 0; i < platformFilter.size(); ++i)
+ request += QString("<platform name=\"%1\" />").arg(platformFilter.at(i));
+ for (int i = 0; i < hostFilter.size(); ++i)
+ request += QString("<host name=\"%1\" />").arg(hostFilter.at(i));
+ for (int i = 0; i < branchFilter.size(); ++i)
+ request += QString("<branch name=\"%1\" />").arg(branchFilter.at(i));
+
+ request += "</request>";
+
+ return xmlConvert(request);
+}
+
+// ### 2 B DOCUMENTED!
+static bool deleteIndexConfig(
+ QSqlDatabase *database, const QString &type, int configId, const QStringList &filterTables,
+ QString *reply)
+{
+ database->transaction();
+
+ QSqlQuery query(*database);
+
+ // Delete from filter tables ...
+ for (int i = 0; i < filterTables.size(); ++i) {
+ if (!query.exec(
+ QString("DELETE FROM %1 WHERE indexConfigId=%2;")
+ .arg(filterTables.at(i)).arg(configId))) {
+ *reply = errorReply(
+ query, type, QString("failed to delete from %1").arg(filterTables.at(i)));
+ database->rollback();
+ return false;
+ }
+ }
+
+ // Delete from main table ...
+ if (!query.exec(
+ QString("DELETE FROM indexConfig WHERE id=%1;").arg(configId))) {
+ *reply = errorReply(query, type, "failed to delete from indexConfig");
+ database->rollback();
+ return false;
+ }
+
+ database->commit();
+ return true;
+}
+
+QByteArray BMRequest_IndexPutConfig::toReplyBuffer()
+{
+ QDomElement argsElem = doc.elementsByTagName("args").at(0).toElement();
+
+ // Get config name ...
+ configName = argsElem.attributeNode("configName").value();
+
+ // Get non-filter values ...
+ bool ok;
+
+ baseTimestamp = argsElem.attributeNode("baseTimestamp").value().toInt(&ok);
+ Q_ASSERT(ok);
+
+ evalLoTime = argsElem.attributeNode("evalLoTime").value().toInt(&ok);
+ Q_ASSERT(ok);
+
+ evalHiTime = argsElem.attributeNode("evalHiTime").value().toInt(&ok);
+ Q_ASSERT(ok);
+
+ evalStep = argsElem.attributeNode("evalStep").value().toInt(&ok);
+ Q_ASSERT(ok);
+
+ medianWinSize = argsElem.attributeNode("medianWinSize").value().toInt(&ok);
+ Q_ASSERT(ok);
+
+ // Get filter values ...
+
+ QDomNodeList testCaseNodes = doc.elementsByTagName("testCase");
+ for (int i = 0; i < testCaseNodes.size(); ++i)
+ testCaseFilter.append(testCaseNodes.at(i).toElement().attributeNode("name").value());
+
+ QDomNodeList metricNodes = doc.elementsByTagName("metric");
+ for (int i = 0; i < metricNodes.size(); ++i)
+ metricFilter.append(metricNodes.at(i).toElement().attributeNode("name").value());
+
+ QDomNodeList platformNodes = doc.elementsByTagName("platform");
+ for (int i = 0; i < platformNodes.size(); ++i)
+ platformFilter.append(platformNodes.at(i).toElement().attributeNode("name").value());
+
+ QDomNodeList hostNodes = doc.elementsByTagName("host");
+ for (int i = 0; i < hostNodes.size(); ++i)
+ hostFilter.append(hostNodes.at(i).toElement().attributeNode("name").value());
+
+ QDomNodeList branchNodes = doc.elementsByTagName("branch");
+ for (int i = 0; i < branchNodes.size(); ++i)
+ branchFilter.append(branchNodes.at(i).toElement().attributeNode("name").value());
+
+
+ //------------------------------------
+
+ // Insert config (overwriting any existing one) ...
+
+ QString reply;
+ QSqlQuery *query = createQuery();
+ const QStringList filterTables =
+ QStringList() << "icTestCase" << "icMetric" << "icPlatform" << "icHost" << "icBranch";
+ QMap<QString, QStringList> filters;
+ filters.insert("icTestCase", testCaseFilter);
+ filters.insert("icMetric", metricFilter);
+ filters.insert("icPlatform", platformFilter);
+ filters.insert("icHost", hostFilter);
+ filters.insert("icBranch", branchFilter);
+ int configId;
+
+ // Check if the config already exists ...
+ if (!query->exec(QString("SELECT id FROM indexConfig WHERE name='%1';").arg(configName))) {
+ reply = errorReply(*query, name(), "failed to save index config (exec() failed (1))");
+ deleteQuery(query);
+ return xmlConvert(reply);
+ }
+
+ if (query->next()) {
+ // ... it does, so delete it ...
+
+ configId = query->value(0).toInt();
+ deleteQuery(query);
+ if (!deleteIndexConfig(database, name(), configId, filterTables, &reply))
+ return xmlConvert(reply);
+ } else {
+ deleteQuery(query);
+ }
+
+
+ // Insert the new config ...
+
+ query = createQuery(Transaction);
+
+ // Insert into main table ...
+ if (!query->exec(
+ QString(
+ "INSERT INTO indexConfig (name, baseTimestamp, loEvalTimestamp, hiEvalTimestamp, "
+ "evalTimestep, medianWinSize) "
+ "VALUES ('%1', %2, %3, %4, %5, %6);")
+ .arg(configName)
+ .arg(baseTimestamp)
+ .arg(evalLoTime)
+ .arg(evalHiTime)
+ .arg(evalStep)
+ .arg(medianWinSize))) {
+ reply = errorReply(*query, name(), "failed to insert into indexConfig");
+ deleteQuery(query, Rollback);
+ return xmlConvert(reply);
+ }
+
+ // Get last insert ID ...
+ if (!BMMisc::getLastInsertId(query, &configId)) {
+ Q_ASSERT(false); // ### Backup code (involving a separate query) to go here!
+ reply = errorReply(*query, name(), "failed to get last ID inserted in indexConfig");
+ deleteQuery(query, Rollback);
+ return xmlConvert(reply);
+ }
+
+ // Insert into filter tables ...
+ for (int i = 0; i < filterTables.size(); ++i) {
+ QStringList filter = filters.value(filterTables.at(i));
+ for (int j = 0; j < filter.size(); ++j) {
+ if (!query->exec(
+ QString("INSERT INTO %1 (indexConfigId, name) VALUES (%2, '%3');")
+ .arg(filterTables.at(i)).arg(configId).arg(filter.at(j)))) {
+ reply = errorReply(
+ *query, name(),
+ QString("failed to insert value '%1' into filter table %1")
+ .arg(filter.at(j)).arg(filterTables.at(i)));
+ deleteQuery(query, Rollback);
+ return xmlConvert(reply);
+ }
+ }
+ }
+
+ deleteQuery(query, Commit);
+
+
+ reply = QString("<reply type=\"%1\" />").arg(name());
+ return xmlConvert(reply);
+}
+
+void BMRequest_IndexPutConfig::handleReply_Raw(const QStringList &args) const
+{
+ printf("NOTE: raw output not supported yet; printing JSON output for now:\n\n");
+ handleReply_JSON(args);
+}
+
+void BMRequest_IndexPutConfig::handleReply_JSON(const QStringList &args) const
+{
+ Q_UNUSED(args);
+
+ QString reply;
+
+ const QString error =
+ doc.elementsByTagName("reply").at(0).toElement().attributeNode("error").value();
+
+ if (error.isEmpty())
+ reply = "{}";
+ else
+ reply = QString("{\"error\": \"error saving index configuration: %1\"}").arg(error);
+
+ BMMisc::printJSONOutput(reply);
+}
+
+
+// --- IndexDeleteConfig ---
+QByteArray BMRequest_IndexDeleteConfig::toRequestBuffer(QString *)
+{
+ const QString request =
+ QString("<request type=\"%1\"><args configName=\"%2\" /></request>")
+ .arg(name()).arg(configName);
+ return xmlConvert(request);
+}
+
+QByteArray BMRequest_IndexDeleteConfig::toReplyBuffer()
+{
+ QDomElement argsElem = doc.elementsByTagName("args").at(0).toElement();
+
+ // Get config name ...
+ configName = argsElem.attributeNode("configName").value();
+
+ QString reply;
+ QSqlQuery *query = createQuery();
+ const QStringList filterTables =
+ QStringList() << "icTestCase" << "icMetric" << "icPlatform" << "icHost" << "icBranch";
+ int configId;
+
+ // Check if the config already exists ...
+ if (!query->exec(QString("SELECT id FROM indexConfig WHERE name='%1';").arg(configName))) {
+ reply = errorReply(*query, name(), "failed to save index config (exec() failed (1))");
+ deleteQuery(query);
+ return xmlConvert(reply);
+ }
+
+ if (query->next()) {
+ // ... it does, so delete it ...
+ configId = query->value(0).toInt();
+ deleteQuery(query);
+ if (!deleteIndexConfig(database, name(), configId, filterTables, &reply))
+ return xmlConvert(reply);
+ } else {
+ deleteQuery(query);
+ }
+
+ reply = QString("<reply type=\"%1\" />").arg(name());
+ return xmlConvert(reply);
+}
+
+void BMRequest_IndexDeleteConfig::handleReply_Raw(const QStringList &args) const
+{
+ printf("NOTE: raw output not supported yet; printing JSON output for now:\n\n");
+ handleReply_JSON(args);
+}
+
+void BMRequest_IndexDeleteConfig::handleReply_JSON(const QStringList &args) const
+{
+ Q_UNUSED(args);
+
+ QString reply;
+
+ const QString error =
+ doc.elementsByTagName("reply").at(0).toElement().attributeNode("error").value();
+
+ if (error.isEmpty())
+ reply = "{}";
+ else
+ reply = QString("{\"error\": \"error deleting index configuration: %1\"}").arg(error);
+
+ BMMisc::printJSONOutput(reply);
+}
diff --git a/src/bm/bmrequest.h b/src/bm/bmrequest.h
index 83986d0..1d2c40f 100644
--- a/src/bm/bmrequest.h
+++ b/src/bm/bmrequest.h
@@ -566,9 +566,9 @@ public:
BMRequest_IndexGetValues(
const int evalLoTime, const int evalHiTime, const int evalStep,
const QList<int> &evalTimestamps, const int baseTimestamp, const int medianWinSize,
- const QString &cacheKey, const QStringList &metricFilter, const QStringList &platformFilter,
- const QStringList &hostFilter, const QStringList &branchFilter,
- const QStringList &testCaseFilter)
+ const QString &cacheKey, const QStringList &testCaseFilter,
+ const QStringList &metricFilter, const QStringList &platformFilter,
+ const QStringList &hostFilter, const QStringList &branchFilter)
: evalLoTime(evalLoTime), evalHiTime(evalHiTime), evalStep(evalStep)
, evalTimestamps(evalTimestamps), baseTimestamp(baseTimestamp)
, medianWinSize(medianWinSize), cacheKey(cacheKey), testCaseFilter(testCaseFilter)
@@ -607,4 +607,89 @@ private:
const QStringList &filter, const QString &filterName, QString *reply);
};
+// ### 2 B DOCUMENTED (in bmclient.html and bmproto.html)
+class BMRequest_IndexGetConfigs : public BMRequest
+{
+public:
+ BMRequest_IndexGetConfigs() {}
+ BMRequest_IndexGetConfigs(const QDomDocument &doc) : BMRequest(doc) {}
+private:
+ QString name() const { return "IndexGetConfigs"; }
+ QByteArray toRequestBuffer(QString *error);
+ QByteArray toReplyBuffer();
+ void handleReply_Raw(const QStringList &args) const;
+ void handleReply_JSON(const QStringList &args) const;
+};
+
+// ### 2 B DOCUMENTED (in bmclient.html and bmproto.html)
+class BMRequest_IndexGetConfig : public BMRequest
+{
+public:
+ BMRequest_IndexGetConfig(const QString &configName)
+ : configName(configName) {}
+ BMRequest_IndexGetConfig(const QDomDocument &doc) : BMRequest(doc) {}
+private:
+ QString configName;
+
+ QString name() const { return "IndexGetConfig"; }
+ QByteArray toRequestBuffer(QString *error);
+ QByteArray toReplyBuffer();
+ void handleReply_Raw(const QStringList &args) const;
+ void handleReply_JSON(const QStringList &args) const;
+};
+
+// ### 2 B DOCUMENTED (in bmclient.html and bmproto.html)
+class BMRequest_IndexPutConfig : public BMRequest
+{
+public:
+ BMRequest_IndexPutConfig(
+ const QString &configName, const int baseTimestamp, const int evalLoTime,
+ const int evalHiTime, const int evalStep, const int medianWinSize,
+ const QStringList &testCaseFilter, const QStringList &metricFilter,
+ const QStringList &platformFilter, const QStringList &hostFilter,
+ const QStringList &branchFilter)
+ : configName(configName), baseTimestamp(baseTimestamp), evalLoTime(evalLoTime)
+ , evalHiTime(evalHiTime), evalStep(evalStep), medianWinSize(medianWinSize)
+ , testCaseFilter(testCaseFilter), metricFilter(metricFilter)
+ , platformFilter(platformFilter), hostFilter(hostFilter), branchFilter(branchFilter)
+ {}
+
+ BMRequest_IndexPutConfig(const QDomDocument &doc) : BMRequest(doc) {}
+private:
+ QString configName;
+ int baseTimestamp;
+ int evalLoTime;
+ int evalHiTime;
+ int evalStep;
+ int medianWinSize;
+ QStringList testCaseFilter;
+ QStringList metricFilter;
+ QStringList platformFilter;
+ QStringList hostFilter;
+ QStringList branchFilter;
+
+ QString name() const { return "IndexPutConfig"; }
+ QByteArray toRequestBuffer(QString *error);
+ QByteArray toReplyBuffer();
+ void handleReply_Raw(const QStringList &args) const;
+ void handleReply_JSON(const QStringList &args) const;
+};
+
+// ### 2 B DOCUMENTED (in bmclient.html and bmproto.html)
+class BMRequest_IndexDeleteConfig : public BMRequest
+{
+public:
+ BMRequest_IndexDeleteConfig(const QString &configName)
+ : configName(configName) {}
+ BMRequest_IndexDeleteConfig(const QDomDocument &doc) : BMRequest(doc) {}
+private:
+ QString configName;
+
+ QString name() const { return "IndexDeleteConfig"; }
+ QByteArray toRequestBuffer(QString *error);
+ QByteArray toReplyBuffer();
+ void handleReply_Raw(const QStringList &args) const;
+ void handleReply_JSON(const QStringList &args) const;
+};
+
#endif // BMREQUEST_H
diff --git a/src/bm/plotter.cpp b/src/bm/plotter.cpp
index e709fb5..180827a 100644
--- a/src/bm/plotter.cpp
+++ b/src/bm/plotter.cpp
@@ -26,6 +26,7 @@
#include <QGraphicsScene>
#include <QGraphicsSimpleTextItem>
#include <QPainter>
+#include <QDebug>
// ### Note: There is a potential for refactoring in order to avoid code duplication
// in this file.
diff --git a/src/bmclient/main.cpp b/src/bmclient/main.cpp
index ecbb6e9..63e18bc 100644
--- a/src/bmclient/main.cpp
+++ b/src/bmclient/main.cpp
@@ -113,6 +113,7 @@ private:
BMRequest * createIndexGetValuesRequest(
const QStringList &args, QString *error,
const QString &command = "index get values") const;
+ BMRequest * createIndexPutConfigRequest(const QStringList &args, QString *error) const;
mutable BMRequest::OutputFormat explicitOutputFormat;
mutable bool useExplicitOutputFormat;
BMRequest::OutputFormat outputFormat() const;
@@ -674,6 +675,62 @@ BMRequest * Executor::createRequest(const QStringList &args, QString *error) con
setOutputFormat(BMRequest::HTML);
return request;
+ } else if(
+ (args.size() >= 3) && (args.at(0) == "index") && (args.at(1) == "get")
+ && (args.at(2) == "configs")) {
+ // --- 'index get configs' command ---
+
+ return new BMRequest_IndexGetConfigs();
+
+ } else if(
+ (args.size() >= 3) && (args.at(0) == "index") && (args.at(1) == "get")
+ && (args.at(2) == "config")) {
+ // --- 'index get config' command ---
+
+ QStringList values;
+
+ // Get name ...
+ if (!BMMisc::getOption(args, "-name", &values, 1, 0, error)) {
+ if (error->isEmpty())
+ *error = "-name option not found";
+ return 0;
+ }
+ const QString configName = values.first().trimmed();
+ if (configName.isEmpty()) {
+ *error = "empty config name";
+ return 0;
+ }
+
+ return new BMRequest_IndexGetConfig(configName);
+
+ } else if(
+ (args.size() >= 3) && (args.at(0) == "index") && (args.at(1) == "put")
+ && (args.at(2) == "config")) {
+ // --- 'index put config' command ---
+
+ return createIndexPutConfigRequest(args, error);
+
+ } else if(
+ (args.size() >= 3) && (args.at(0) == "index") && (args.at(1) == "delete")
+ && (args.at(2) == "config")) {
+ // --- 'index delete config' command ---
+
+ QStringList values;
+
+ // Get name ...
+ if (!BMMisc::getOption(args, "-name", &values, 1, 0, error)) {
+ if (error->isEmpty())
+ *error = "-name option not found";
+ return 0;
+ }
+ const QString configName = values.first().trimmed();
+ if (configName.isEmpty()) {
+ *error = "empty config name";
+ return 0;
+ }
+
+ return new BMRequest_IndexDeleteConfig(configName);
+
} else if ((args.size() >= 2) && (args.at(0) == "get") && (args.at(1) == "detailspage")) {
// --- 'get detailspage' command ---
@@ -864,7 +921,7 @@ BMRequest * Executor::createIndexGetValuesRequest(
QStringList values;
- // Get explicit eval timestamps (default is current time) or time range ...
+ // Get explicit eval timestamps or time range ...
int evalLoTime = -1;
int evalHiTime = -1;
int evalStep = -1;
@@ -915,7 +972,7 @@ BMRequest * Executor::createIndexGetValuesRequest(
evalStep = values.at(2).toInt(&ok);
if ((!ok) || (evalStep < 1)) {
*error =
- QString("eval time range step not a positve integer: %1").arg(values.at(2));
+ QString("eval time range step not a positive integer: %1").arg(values.at(2));
return 0;
}
@@ -975,6 +1032,9 @@ BMRequest * Executor::createIndexGetValuesRequest(
}
// Get filters ...
+ QStringList testCaseFilter;
+ if (!BMMisc::getMultiOption(args, "-testcase", &testCaseFilter, error))
+ return 0;
QStringList metricFilter;
if (!BMMisc::getMultiOption(args, "-metric", &metricFilter, error))
return 0;
@@ -987,13 +1047,100 @@ BMRequest * Executor::createIndexGetValuesRequest(
QStringList branchFilter;
if (!BMMisc::getMultiOption(args, "-branch", &branchFilter, error))
return 0;
+
+ return new BMRequest_IndexGetValues(
+ evalLoTime, evalHiTime, evalStep, evalTimestamps, baseTimestamp, medianWinSize, cacheKey,
+ testCaseFilter, metricFilter, platformFilter, hostFilter, branchFilter);
+}
+
+BMRequest * Executor::createIndexPutConfigRequest(const QStringList &args, QString *error) const
+{
+ QStringList values;
+
+ // Get name ...
+ if (!BMMisc::getOption(args, "-name", &values, 1, 0, error)) {
+ if (error->isEmpty())
+ *error = "-name option not found";
+ return 0;
+ }
+ const QString configName = values.first().trimmed();
+ if (configName.isEmpty()) {
+ *error = "empty config name";
+ return 0;
+ }
+
+ // Get base timestamp ...
+ if (!BMMisc::getOption(args, "-basetimestamp", &values, 1, 0, error)) {
+ if (error->isEmpty())
+ *error = "-basetimestamp option not found";
+ return 0;
+ }
+ bool ok;
+ int baseTimestamp = values.at(0).toInt(&ok);
+ if (!ok) {
+ *error = "failed to extract base timestamp as an integer";
+ return 0;
+ }
+ baseTimestamp = qMax(baseTimestamp, -1);
+
+ // Get evaluation time range ...
+ if (!BMMisc::getOption(args, "-evaltimerange", &values, 3, 0, error)) {
+ if (error->isEmpty())
+ *error = "-evaltimerange option not found";
+ return 0;
+ }
+ const int evalLoTime = values.at(0).toInt(&ok);
+ if (!ok) {
+ *error = QString("eval time range start not an integer: %1").arg(values.at(0));
+ return 0;
+ }
+ const int evalHiTime = values.at(1).toInt(&ok);
+ if (!ok) {
+ *error = QString("eval time range end not an integer: %1").arg(values.at(1));
+ return 0;
+ }
+ const int evalStep = values.at(2).toInt(&ok);
+ if ((!ok) || (evalStep < 1)) {
+ *error = QString("eval time range step not a positive integer: %1").arg(values.at(2));
+ return 0;
+ }
+ if ((evalLoTime < 0) || ((evalLoTime > evalHiTime) && (evalHiTime != -1))) {
+ *error = QString("invalid time range: %1..%2").arg(evalLoTime).arg(evalHiTime);
+ return 0;
+ }
+
+ // Get median window size ...
+ if (!BMMisc::getOption(args, "-medianwinsize", &values, 1, 0, error)) {
+ if (error->isEmpty())
+ *error = "-medianwinsize option not found";
+ return 0;
+ }
+ const int medianWinSize = values.at(0).toInt(&ok);
+ if ((!ok) || (medianWinSize < 1)) {
+ *error = "failed to extract median window size as a positive integer";
+ return 0;
+ }
+
+ // Get filters ...
QStringList testCaseFilter;
if (!BMMisc::getMultiOption(args, "-testcase", &testCaseFilter, error))
return 0;
+ QStringList metricFilter;
+ if (!BMMisc::getMultiOption(args, "-metric", &metricFilter, error))
+ return 0;
+ QStringList platformFilter;
+ if (!BMMisc::getMultiOption(args, "-platform", &platformFilter, error))
+ return 0;
+ QStringList hostFilter;
+ if (!BMMisc::getMultiOption(args, "-host", &hostFilter, error))
+ return 0;
+ QStringList branchFilter;
+ if (!BMMisc::getMultiOption(args, "-branch", &branchFilter, error))
+ return 0;
- return new BMRequest_IndexGetValues(
- evalLoTime, evalHiTime, evalStep, evalTimestamps, baseTimestamp, medianWinSize, cacheKey,
- metricFilter, platformFilter, hostFilter, branchFilter, testCaseFilter);
+ return new BMRequest_IndexPutConfig(
+ configName, baseTimestamp, evalLoTime, evalHiTime, evalStep, medianWinSize, testCaseFilter,
+ metricFilter, platformFilter, hostFilter, branchFilter);
}
// ### 2 B DOCUMENTED!
@@ -1360,9 +1507,9 @@ class DirectExecutor : public Executor
"index get values \\\n"
" {-evaltimestamp <...> ...} | {-evaltimerange <first> <last> <step>} \\\n"
" -basetimestamp <...> [-medianwinsize <...> (default = 8)] \\\n"
- " [-metric <...> -metric <...> ...] [-platform <...> -platform <...> ...] \\\n"
- " [-host <...> -host <...> ...] [-branch <...> -branch <...> ...] \\\n"
- " [-testcase <...> -testcase <...> ...]\n"
+ " [-testcase <...> -testcase <...> ...] [-metric <...> -metric <...> ...] \\\n"
+ " [-platform <...> -platform <...> ...] [-host <...> -host <...> ...] \\\n"
+ " [-branch <...> -branch <...> ...]\n"
<<
"index get plot <SAME AS 'index get values' except that the optional \\\n"
@@ -1372,6 +1519,23 @@ class DirectExecutor : public Executor
"index get detailspage <SAME AS 'index get plot'> except that the mandatory \\\n"
" -stylesheet option is recognized\n"
+ <<
+ "index get configs\n"
+
+ <<
+ "index get config -name <...>\n"
+
+ <<
+ "index put config -name <...> \\\n"
+ " -basetimestamp <...> -evaltimerange <first> <last> <step> \\\n"
+ " -medianwinsize <...> \\\n"
+ " [-testcase <...> -testcase <...> ...] [-metric <...> -metric <...> ...] \\\n"
+ " [-platform <...> -platform <...> ...] [-host <...> -host <...> ...] \\\n"
+ " [-branch <...> -branch <...> ...]\n"
+
+ <<
+ "index delete config -name <...>\n"
+
;
qDebug() << "\nNote: the -server option may be replaced by a BMSERVER=<host>:<port> "
diff --git a/src/bmserver/main.cpp b/src/bmserver/main.cpp
index dbc4d59..9ee80f9 100644
--- a/src/bmserver/main.cpp
+++ b/src/bmserver/main.cpp
@@ -117,6 +117,58 @@ static bool initDatabase(const QString &dbfile, QString *error)
", UNIQUE(bmcontextId, snapshotId));");
Q_ASSERT(ok);
+ // indexConfig
+ ok = query.exec(
+ "CREATE TABLE indexConfig(id INTEGER PRIMARY KEY AUTOINCREMENT"
+ ", name TEXT NOT NULL"
+ ", baseTimestamp INTEGER NOT NULL"
+ ", loEvalTimestamp INTEGER NOT NULL"
+ ", hiEvalTimestamp INTEGER NOT NULL"
+ ", evalTimestep INTEGER NOT NULL"
+ ", medianWinSize INTEGER NOT NULL"
+ ", UNIQUE(name));");
+ Q_ASSERT(ok);
+
+ // icTestCase
+ ok = query.exec(
+ "CREATE TABLE icTestCase(id INTEGER PRIMARY KEY AUTOINCREMENT"
+ ", indexConfigId INTEGER REFERENCES indexConfig(id)"
+ ", name TEXT NOT NULL"
+ ", UNIQUE(indexConfigId, name));");
+ Q_ASSERT(ok);
+
+ // icMetric
+ ok = query.exec(
+ "CREATE TABLE icMetric(id INTEGER PRIMARY KEY AUTOINCREMENT"
+ ", indexConfigId INTEGER REFERENCES indexConfig(id)"
+ ", name TEXT NOT NULL"
+ ", UNIQUE(indexConfigId, name));");
+ Q_ASSERT(ok);
+
+ // icPlatform
+ ok = query.exec(
+ "CREATE TABLE icPlatform(id INTEGER PRIMARY KEY AUTOINCREMENT"
+ ", indexConfigId INTEGER REFERENCES indexConfig(id)"
+ ", name TEXT NOT NULL"
+ ", UNIQUE(indexConfigId, name));");
+ Q_ASSERT(ok);
+
+ // icHost
+ ok = query.exec(
+ "CREATE TABLE icHost(id INTEGER PRIMARY KEY AUTOINCREMENT"
+ ", indexConfigId INTEGER REFERENCES indexConfig(id)"
+ ", name TEXT NOT NULL"
+ ", UNIQUE(indexConfigId, name));");
+ Q_ASSERT(ok);
+
+ // icBranch
+ ok = query.exec(
+ "CREATE TABLE icBranch(id INTEGER PRIMARY KEY AUTOINCREMENT"
+ ", indexConfigId INTEGER REFERENCES indexConfig(id)"
+ ", name TEXT NOT NULL"
+ ", UNIQUE(indexConfigId, name));");
+ Q_ASSERT(ok);
+
// *** Create indexes ***
ok = query.exec("CREATE INDEX index_metric_name ON metric(name);");
@@ -165,6 +217,29 @@ static bool initDatabase(const QString &dbfile, QString *error)
"CREATE INDEX index_result_bmcontextId_snapshotId ON result(bmcontextId, snapshotId);");
Q_ASSERT(ok);
+ ok = query.exec("CREATE INDEX index_indexConfig_name ON indexConfig(name);");
+ Q_ASSERT(ok);
+
+ ok = query.exec(
+ "CREATE INDEX index_icTestCase_indexConfigId_name ON icTestCase(indexConfigId, name);");
+ Q_ASSERT(ok);
+
+ ok = query.exec(
+ "CREATE INDEX index_icMetric_indexConfigId_name ON icMetric(indexConfigId, name);");
+ Q_ASSERT(ok);
+
+ ok = query.exec(
+ "CREATE INDEX index_icPlatform_indexConfigId_name ON icPlatform(indexConfigId, name);");
+ Q_ASSERT(ok);
+
+ ok = query.exec(
+ "CREATE INDEX index_icHost_indexConfigId_name ON icHost(indexConfigId, name);");
+ Q_ASSERT(ok);
+
+ ok = query.exec(
+ "CREATE INDEX index_icBranch_indexConfigId_name ON icBranch(indexConfigId, name);");
+ Q_ASSERT(ok);
+
return true;
}
diff --git a/src/bmweb/global.js b/src/bmweb/global.js
index e435e9d..e6b80d6 100644
--- a/src/bmweb/global.js
+++ b/src/bmweb/global.js
@@ -396,6 +396,22 @@ function selectedOptions(id)
return result;
}
+function sortOptions(id)
+{
+ var select = document.getElementById(id);
+ if (!select)
+ return;
+ var optionValues = [];
+ for (i = 0; i < select.options.length; ++i)
+ optionValues[optionValues.length] = select.options[i].value;
+ optionValues.sort(function(val1, val2) {
+ return val1 > val2;
+ });
+ select.options.length = 0;
+ for (i = 0; i < optionValues.length; ++i)
+ select.options[select.options.length] = new Option(optionValues[i]);
+}
+
function setDiffToleranceOptions(select)
{
select.options[0] = new Option("0.00", "0.00");
diff --git a/src/bmweb/indexsection.js b/src/bmweb/indexsection.js
index bddccd5..93f3630 100644
--- a/src/bmweb/indexsection.js
+++ b/src/bmweb/indexsection.js
@@ -41,18 +41,18 @@ function updateEvalLink(
// Add evaluation timestamps ...
- evalTimeLo = getIntFromTextInput("ixEvalTimeLo", defaultEvalTimeLo);
- document.getElementById("ixEvalTimeLo").value = evalTimeLo;
+ evalTimeLo = getIntFromTextInput("ixEvalLoTime", defaultEvalTimeLo);
+ document.getElementById("ixEvalLoTime").value = evalTimeLo;
- evalTimeHi = getIntFromTextInput("ixEvalTimeHi", defaultEvalTimeHi);
- document.getElementById("ixEvalTimeHi").value = evalTimeHi;
+ evalTimeHi = getIntFromTextInput("ixEvalHiTime", defaultEvalTimeHi);
+ document.getElementById("ixEvalHiTime").value = evalTimeHi;
- evalTimeStep = getIntFromTextInput("ixEvalTimeStep", defaultEvalTimeStep);
+ evalTimeStep = getIntFromTextInput("ixEvalStep", defaultEvalTimeStep);
// Make sure the time step is at least 24 hours to prevent unnecessary
// server load (results are currently not uploaded to the server more
// than approximately once a day anyway) ...
evalTimeStep = Math.max(evalTimeStep, 86400);
- document.getElementById("ixEvalTimeStep").value = evalTimeStep;
+ document.getElementById("ixEvalStep").value = evalTimeStep;
url += " -evaltimerange " + evalTimeLo + " " + evalTimeHi + " " + evalTimeStep;
@@ -114,16 +114,37 @@ function IndexSection()
this.name = function() { return "ixSection"; }
- this.resetFilters = function(handleDone)
+ this.reset = function(handleDone)
{
- this.finalize_all = function()
+ this.finalize_reset = function()
{
setMessage("");
- document.getElementById("resetIX").disabled = false;
+ document.getElementById("ixReset").disabled = false;
if (handleDone)
handleDone();
}
+ this.handleReply_resetConfigs = function(reply)
+ {
+ if (reply.error) {
+ alert("reset() failed when fetching configs (2): " + reply.error);
+ } else {
+ // Populate <select> tag ...
+ select = document.getElementById("ixConfigs");
+ select.options.length = 0;
+ for (i = 0; i < reply.names.length; ++i)
+ select.options[select.options.length] = new Option(reply.names[i]);
+ }
+
+ this.finalize_reset();
+ }
+
+ this.handleError_resetConfigs = function(error)
+ {
+ alert("reset() failed when fetching configs (1): " + error);
+ this.finalize_resetConfigs();
+ }
+
this.populateFilterTable = function(tableId, values, skipValue)
{
// alert("populating " + tableId + " filter table; values (" + values.length
@@ -151,6 +172,7 @@ function IndexSection()
td = tr.insertCell(1);
td.setAttribute("style", "border:0px; padding:0px");
+ // ### Just display the concatenation of multi-component values for now:
value = "";
for (j = 0; j < values[i].length; ++j)
value += values[i][j];
@@ -160,11 +182,13 @@ function IndexSection()
}
}
- this.handleReply_tccontexts = function(reply)
+ this.handleReply_resetFilters = function(reply)
{
if (reply.error) {
- alert(
- "resetFilters() failed when fetching test cases and contexts: " + reply.error);
+ alert("reset() failed when fetching test cases and contexts (2): " + reply.error);
+
+ this.finalize_reset();
+
} else {
this.populateFilterTable("ixTestCaseFilter", reply.values[0]);
@@ -203,19 +227,25 @@ function IndexSection()
this.updateMatches();
}
- this.finalize_all();
+ // Fetch index configurations ...
+ setMessage("fetching configurations ...");
+ var args = ["index", "get", "configs"];
+ sendRequest(
+ args,
+ function(reply) { thisSection.handleReply_resetConfigs(reply); },
+ function(error) { thisSection.handleError_resetConfigs(error); });
}
- this.handleError_all = function(error)
+ this.handleError_resetFilters = function(error)
{
- alert("resetFilters() failed: " + error);
- this.finalize_all();
+ alert("reset() failed when fetching test cases and contexts (1): " + error);
+ this.finalize_reset();
}
var thisSection = this; // ### hack?
- document.getElementById("resetIX").disabled = true;
+ document.getElementById("ixReset").disabled = true;
// Clear tables ...
removeElementChildren(document.getElementById("ixTestCaseFilter"));
@@ -229,23 +259,19 @@ function IndexSection()
var args = ["get", "tccontexts"];
sendRequest(
args,
- function(reply) { thisSection.handleReply_tccontexts(reply); },
- function(error) { thisSection.handleError_all(error); });
- }
-
- this.reset = function(handleDone)
- {
- this.resetFilters(handleDone);
+ function(reply) { thisSection.handleReply_resetFilters(reply); },
+ function(error) { thisSection.handleError_resetFilters(error); });
}
- this.clearFilter = function(tableId) {
+ this.clearFilter = function(tableId, skipUpdate) {
table = document.getElementById(tableId);
for (i = 0; i < table.rows.length; ++i) {
input = document.getElementById(tableId + ":" + i);
input.checked = false;
}
- this.updateMatches();
+ if (!skipUpdate)
+ this.updateMatches();
}
this.updateMatches = function() {
@@ -309,6 +335,211 @@ function IndexSection()
}
}
+ this.loadConfig = function()
+ {
+ this.finalize_loadConfig = function()
+ {
+ setMessage("");
+ }
+
+ this.handleReply_loadConfig = function(reply)
+ {
+ if (reply.error) {
+ alert("loadConfig() failed (2): " + reply.error);
+
+ } else {
+
+ // Load non-filter values ...
+ document.getElementById("ixBaseTime").value = reply.baseTimestamp;
+ document.getElementById("ixEvalLoTime").value = reply.evalLoTime;
+ document.getElementById("ixEvalHiTime").value = reply.evalHiTime;
+ document.getElementById("ixEvalStep").value = reply.evalStep;
+ document.getElementById("ixMedianWinSize").value = reply.medianWinSize;
+
+ // Load filter values ...
+ function loadFilterValues(tableId, filter) {
+ thisSection.clearFilter(tableId, true);
+ table = document.getElementById(tableId);
+ for (i = 0; i < table.rows.length; ++i) {
+ name = table.rows[i].cells[1].innerHTML;
+ if (filter.indexOf(name) != -1)
+ table.rows[i].cells[0].childNodes[0].checked = true;
+ }
+ }
+ loadFilterValues("ixTestCaseFilter", reply.testCaseFilter);
+ loadFilterValues("ixMetricFilter", reply.metricFilter);
+ loadFilterValues("ixPlatformFilter", reply.platformFilter);
+ loadFilterValues("ixHostFilter", reply.hostFilter);
+ loadFilterValues("ixBranchFilter", reply.branchFilter);
+
+ this.updateMatches();
+ }
+
+
+ this.finalize_loadConfig();
+ }
+
+ this.handleError_loadConfig = function(error)
+ {
+ alert("loadConfig() failed (1): " + error);
+ this.finalize_loadConfig();
+ }
+
+
+ var thisSection = this; // ### hack?
+
+ // Get current config name ...
+ configName = selectedOptions("ixConfigs")[0]
+
+ // Fetch config values ...
+ setMessage("fetching config values ...");
+ var args = ["index", "get", "config", "-name", "'" + configName + "'"];
+ sendRequest(
+ args,
+ function(reply) { thisSection.handleReply_loadConfig(reply); },
+ function(error) { thisSection.handleError_loadConfig(error); });
+ }
+
+ this.deleteConfig = function()
+ {
+ this.finalize_deleteConfig = function()
+ {
+ setMessage("");
+ }
+
+ this.handleReply_deleteConfig = function(reply)
+ {
+ if (reply.error) {
+ alert("deleteConfig() failed (2): " + reply.error);
+ } else {
+ select = document.getElementById("ixConfigs");
+ pos = -1;
+ for (i = 0; i < select.options.length; ++i)
+ if (select.options[i].value == configName) {
+ pos = i;
+ break;
+ }
+ // assert(pos >= 0);
+ select.remove(pos);
+
+// alert("configuration successfully deleted");
+ }
+
+ this.finalize_deleteConfig();
+ }
+
+ this.handleError_deleteConfig = function(error)
+ {
+ alert("deleteConfig() failed (1): " + error);
+ this.finalize_deleteConfig();
+ }
+
+ var thisSection = this; // ### hack?
+
+ // Get current config name ...
+ configName = selectedOptions("ixConfigs")[0]
+
+ if (!confirm("Really delete configuration '" + configName + "' ?"))
+ return;
+
+ setMessage("deleting config values ...");
+ var args = ["index", "delete", "config", "-name", "'" + configName + "'"];
+ sendRequest(
+ args,
+ function(reply) { thisSection.handleReply_deleteConfig(reply); },
+ function(error) { thisSection.handleError_deleteConfig(error); });
+ }
+
+ this.saveConfig = function()
+ {
+ this.finalize_saveConfig = function()
+ {
+ setMessage("");
+ }
+
+ this.handleReply_saveConfig = function(reply)
+ {
+ if (reply.error) {
+ alert("saveConfig() failed (2): " + reply.error);
+ } else {
+
+ select = document.getElementById("ixConfigs");
+
+ pos = -1;
+ for (i = 0; i < select.options.length; ++i)
+ if (select.options[i].value == configName) {
+ pos = i;
+ break;
+ }
+ if (pos == -1) {
+ select.appendChild(new Option(configName));
+ sortOptions("ixConfigs");
+ }
+
+// alert("configuration successfully saved");
+ }
+
+ this.finalize_saveConfig();
+ }
+
+ this.handleError_saveConfig = function(error)
+ {
+ alert("saveConfig() failed (1): " + error);
+ this.finalize_saveConfig();
+ }
+
+ var thisSection = this; // ### hack?
+
+ // Get new config name ...
+ configName = trim(document.getElementById("ixNewConfig").value);
+ if (configName.length == 0) {
+ alert("please enter a non-empty name!");
+ return;
+ }
+
+ if (!confirm("Really save configuration '" + configName + "' ?"))
+ return;
+
+ // Save config values ...
+ setMessage("saving config values ...");
+ var args = [
+ "index", "put", "config", "-name", "'" + configName + "'",
+ "-basetimestamp", document.getElementById("ixBaseTime").value,
+ "-evaltimerange",
+ document.getElementById("ixEvalLoTime").value,
+ document.getElementById("ixEvalHiTime").value,
+ document.getElementById("ixEvalStep").value,
+ "-medianwinsize", document.getElementById("ixMedianWinSize").value
+ ];
+
+ function filterSelection(tableId, option)
+ {
+ var sel = [];
+
+ table = document.getElementById(tableId);
+ for (i = 0; i < table.rows.length; ++i) {
+ if (table.rows[i].cells[0].childNodes[0].checked) {
+ sel[sel.length] = option;
+ sel[sel.length] = table.rows[i].cells[1].innerHTML;
+
+ }
+ }
+
+ return sel;
+ }
+
+ args = args.concat(filterSelection("ixTestCaseFilter", "-testcase"));
+ args = args.concat(filterSelection("ixMetricFilter", "-metric"));
+ args = args.concat(filterSelection("ixPlatformFilter", "-platform"));
+ args = args.concat(filterSelection("ixHostFilter", "-host"));
+ args = args.concat(filterSelection("ixBranchFilter", "-branch"));
+
+ sendRequest(
+ args,
+ function(reply) { thisSection.handleReply_saveConfig(reply); },
+ function(error) { thisSection.handleError_saveConfig(error); });
+ }
+
this.appendAll = function(section)
{
var thisSection = this; // ### hack?
@@ -334,7 +565,7 @@ function IndexSection()
);
- // Create settings section ...
+ // Create 'Configuration' section ...
section.appendChild(document.createElement("br"));
section.appendChild(document.createElement("br"));
table = section.appendChild(document.createElement("table"));
@@ -342,12 +573,36 @@ function IndexSection()
tr = table.insertRow(0);
td = tr.insertCell(0);
td.setAttribute("style", "border:0px; padding:0px");
- fieldset = td.appendChild(document.createElement("fieldset"));
+ configFieldset = td.appendChild(document.createElement("fieldset"));
- legend = fieldset.appendChild(document.createElement("legend"));
- legend.appendChild(document.createTextNode("Settings"));
+ legend = configFieldset.appendChild(document.createElement("legend"));
+ legend.appendChild(document.createTextNode("Configuration"));
- table = fieldset.appendChild(document.createElement("table"));
+ // ... 'Reset' button ...
+ input = configFieldset.appendChild(document.createElement("input"));
+ input.setAttribute("type", "button");
+ input.setAttribute("id", "ixReset");
+ input.setAttribute("value", "Reset");
+ input.onclick = function() {
+ try { thisSection.reset(); }
+ catch (ex) { alert("thisSection.reset() failed: " + ex); }
+ }
+
+ configFieldset.appendChild(document.createElement("br"));
+ configFieldset.appendChild(document.createElement("br"));
+
+ table = configFieldset.appendChild(document.createElement("table"));
+ table.setAttribute("style", "border:0px; padding:0px");
+ tr = table.insertRow(0);
+ topLeftCell = tr.insertCell(0);
+ topLeftCell.setAttribute("style", "border:0px; padding:0px");
+ topMidCell = tr.insertCell(1);
+ topMidCell.setAttribute("style", "border:0px; padding:10px");
+ topRightCell = tr.insertCell(2);
+ topRightCell.setAttribute("style", "border:0px; padding:0px");
+
+ // Create "miscellaneous settings" section ...
+ table = topLeftCell.appendChild(document.createElement("table"));
table.setAttribute("style", "border:0px; padding:0px");
// ... base timestamp ...
@@ -367,7 +622,7 @@ function IndexSection()
td = tr.insertCell(1);
input = td.appendChild(document.createElement("input"));
input.setAttribute("type", "text");
- input.setAttribute("id", "ixEvalTimeLo");
+ input.setAttribute("id", "ixEvalLoTime");
input.setAttribute("value", defaultEvalTimeLo);
// ... high evaluation timestamp ...
@@ -377,7 +632,7 @@ function IndexSection()
td = tr.insertCell(1);
input = td.appendChild(document.createElement("input"));
input.setAttribute("type", "text");
- input.setAttribute("id", "ixEvalTimeHi");
+ input.setAttribute("id", "ixEvalHiTime");
input.setAttribute("value", defaultEvalTimeHi);
// ... evaluation time step ...
@@ -387,7 +642,7 @@ function IndexSection()
td = tr.insertCell(1);
input = td.appendChild(document.createElement("input"));
input.setAttribute("type", "text");
- input.setAttribute("id", "ixEvalTimeStep");
+ input.setAttribute("id", "ixEvalStep");
input.setAttribute("value", defaultEvalTimeStep);
// ... median window size ...
@@ -401,32 +656,96 @@ function IndexSection()
input.setAttribute("value", defaultMedianWinSize);
- // Create filter section ...
- section.appendChild(document.createElement("br"));
- table = section.appendChild(document.createElement("table"));
+ // Create "load/save" section ...
+ loadSaveTable = topRightCell.appendChild(document.createElement("table"));
+
+ // ... available configurations + operations 'load' and 'delete' ...
+ tr = loadSaveTable.insertRow(loadSaveTable.rows.length);
+ td = tr.insertCell(0);
+ select = td.appendChild(document.createElement("select"));
+ select.setAttribute("id", "ixConfigs");
+ select.setAttribute("size", "4");
+ select.onchange = function() {
+ select = document.getElementById("ixConfigs");
+ document.getElementById("ixNewConfig").value =
+ select.options[select.selectedIndex].value;
+ }
+
+ td = tr.insertCell(1);
+ loadDeleteTable = td.appendChild(document.createElement("table"));
+ loadDeleteTable.setAttribute("style", "border:0px; padding:0px");
+
+ tr = loadDeleteTable.insertRow(loadDeleteTable.rows.length);
+ td = tr.insertCell(0);
+ td.setAttribute("style", "border:0px; padding:2px");
+ input = td.appendChild(document.createElement("input"));
+ input.setAttribute("type", "button");
+ input.setAttribute("id", "ixLoadConfig");
+ input.setAttribute("value", "Load");
+ input.onclick = function() {
+ try { thisSection.loadConfig(); }
+ catch (ex) { alert("thisSection.loadConfig() failed: " + ex); }
+ }
+
+ tr = loadDeleteTable.insertRow(loadDeleteTable.rows.length);
+ td = tr.insertCell(0);
+ td.setAttribute("style", "border:0px; padding:2px");
+ input = td.appendChild(document.createElement("input"));
+ input.setAttribute("type", "button");
+ input.setAttribute("id", "ixDeleteConfig");
+ input.setAttribute("value", "Delete");
+ input.onclick = function() {
+ try { thisSection.deleteConfig(); }
+ catch (ex) { alert("thisSection.deleteConfig() failed: " + ex); }
+ }
+
+
+ // ... new configuration + operation 'save' ...
+ tr = loadSaveTable.insertRow(loadSaveTable.rows.length);
+ td = tr.insertCell(0);
+ input = td.appendChild(document.createElement("input"));
+ input.setAttribute("type", "text");
+ input.setAttribute("id", "ixNewConfig");
+// input.setAttribute("value", "<enter new name here>");
+ td = tr.insertCell(1);
+ td.setAttribute("colspan", "2");
+ input = td.appendChild(document.createElement("input"));
+ input.setAttribute("type", "button");
+ input.setAttribute("id", "ixSaveConfig");
+ input.setAttribute("value", "Save");
+ input.onclick = function() {
+ try { thisSection.saveConfig(); }
+ catch (ex) { alert("thisSection.saveConfig() failed: " + ex); }
+ }
+
+
+ // Create 'Filters' section ...
+ configFieldset.appendChild(document.createElement("br"));
+ table = configFieldset.appendChild(document.createElement("table"));
table.setAttribute("style", "border:0px; padding:0px");
tr = table.insertRow(0);
td = tr.insertCell(0);
td.setAttribute("style", "border:0px; padding:0px");
- fieldset = td.appendChild(document.createElement("fieldset"));
+ filtersFieldset = td.appendChild(document.createElement("fieldset"));
- legend = fieldset.appendChild(document.createElement("legend"));
+ legend = filtersFieldset.appendChild(document.createElement("legend"));
legend.appendChild(document.createTextNode("Filters"));
- // ... 'Reset' button ...
- input = fieldset.appendChild(document.createElement("input"));
- input.setAttribute("type", "button");
- input.setAttribute("id", "resetIX");
- input.setAttribute("value", "Reset");
- input.onclick = function() {
- try { thisSection.resetFilters(); }
- catch (ex) { alert("thisSection.resetFilters() failed: " + ex); }
- }
+ // ... a bit of documentation ...
+ table = filtersFieldset.appendChild(document.createElement("table"));
+ table.setAttribute("style", "border:0px; padding:0px");
+ tr = table.insertRow(0);
+ td = tr.insertCell(0);
+ td.setAttribute("style", "background-color:#eeeeee");
+ td.innerHTML =
+ "<b>Note</b>: The green backgrounds in the table below indicate matching combinations. "
+ + "<br /><b>Note</b>: A filter with no explicitly selected values is considered "
+ + "disabled/open, i.e. it behaves as if all values were selected.";
+
+ filtersFieldset.appendChild(document.createElement("br"));
// ... main table ...
- fieldset.appendChild(document.createElement("br"));
- fieldset.appendChild(document.createElement("br"));
- table = fieldset.appendChild(document.createElement("table"));
+ table = filtersFieldset.appendChild(document.createElement("table"));
table.setAttribute("id", "ixFilters");
table.insertRow(0); // Header row