summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjasplin <qt-info@nokia.com>2010-05-12 07:10:58 +0200
committerjasplin <qt-info@nokia.com>2010-05-12 07:10:58 +0200
commit7acbe7afc40eabd9a88d500abb377b1e320cc1b4 (patch)
treecece6eaa8a5cbd1be61a8eacb88c885ea4a64abe
parent89893576179000aab90d683a43fabf2b2fb2c3b2 (diff)
Improved the result history details of the ASF page.
The result history details page now shows more relevant information, like the 'from' and 'to' times, the actual samples to be picked, and the outliers.
-rw-r--r--src/bm/bmrequest.cpp447
-rw-r--r--src/bm/bmrequest.h29
-rw-r--r--src/bm/plotter.cpp71
-rw-r--r--src/bm/plotter.h6
-rw-r--r--src/bmclient/main.cpp113
5 files changed, 587 insertions, 79 deletions
diff --git a/src/bm/bmrequest.cpp b/src/bm/bmrequest.cpp
index 33769a5..52bacdb 100644
--- a/src/bm/bmrequest.cpp
+++ b/src/bm/bmrequest.cpp
@@ -110,6 +110,8 @@ BMRequest * BMRequest::create(const QByteArray &data, const QString &msgType)
request = new BMRequest_GetIXHistories(doc);
else if (type == "ASFStatsGetValues")
request = new BMRequest_ASFStatsGetValues(doc);
+ else if (type == "ASFStatsGetValues2")
+ request = new BMRequest_ASFStatsGetValues2(doc);
#ifdef BMDEBUG
else
qDebug() << "invalid request type:" << type;
@@ -918,7 +920,7 @@ static bool lastValueIsStable(
}
// ### 2 B DOCUMENTED!
-bool BMRequest::appendBranchHistoryToReply(
+bool BMRequest::appendResultHistoryToReply(
QString *reply, int bmcontextId, const QString &suffix, const QString &timestamp1,
const QString &timestamp2, qreal diffTolerance, int stabTolerance, int maxSize) const
{
@@ -1811,10 +1813,10 @@ QByteArray BMRequest_GetHistory::toRequestBuffer(QString *)
"testCase=\"%2\" testFunction=\"%3\" dataTag=\"%4\" metric=\"%5\" platform=\"%6\" "
"host=\"%7\" gitRepo=\"%8\" gitBranch=\"%9\" timestamp1=\"%10\" "
"timestamp2=\"%11\" diffTolerance=\"%12\" stabTolerance=\"%13\" "
- "maxSize=\"%14\" explicitBMContextId=\"%15\" /></request>")
+ "maxSize=\"%14\" /></request>")
.arg(name()).arg(testCase).arg(testFunction).arg(dataTag).arg(metric).arg(platform)
.arg(host).arg(gitRepo).arg(gitBranch).arg(timestamp1).arg(timestamp2)
- .arg(diffTolerance).arg(stabTolerance).arg(maxSize).arg(explicitBMContextId);
+ .arg(diffTolerance).arg(stabTolerance).arg(maxSize);
return xmlConvert(request);
}
@@ -1838,34 +1840,18 @@ QByteArray BMRequest_GetHistory::toReplyBuffer()
Q_ASSERT(ok);
maxSize = contextElem.attributeNode("maxSize").value().toInt(&ok);
Q_ASSERT(ok);
- explicitBMContextId = contextElem.attributeNode("explicitBMContextId").value().toInt(&ok);
- Q_ASSERT(ok);
-
- if (explicitBMContextId >= 0) {
-
- QString error;
- if (!extractBMAndContextFromRH(
- explicitBMContextId, name(), database, &testCase, &testFunction, &dataTag,
- &metric, &platform, &host, &gitRepo, &gitBranch, &error)) {
- return xmlConvert(error);
- }
- }
QString reply = QString("<reply type=\"%1\">").arg(name());
+ int benchmarkId;
+ if (!getBenchmarkId(&reply, &benchmarkId, testCase, testFunction, dataTag))
+ return xmlConvert(reply);
int bmcontextId;
- if (explicitBMContextId >= 0) {
- bmcontextId = explicitBMContextId;
- } else {
- int benchmarkId;
- if (!getBenchmarkId(&reply, &benchmarkId, testCase, testFunction, dataTag))
- return xmlConvert(reply);
- if (!getBMContextId(
- &reply, &bmcontextId, benchmarkId, metric, platform, host, gitRepo, gitBranch))
- return xmlConvert(reply);
- }
+ if (!getBMContextId(
+ &reply, &bmcontextId, benchmarkId, metric, platform, host, gitRepo, gitBranch))
+ return xmlConvert(reply);
- if (!appendBranchHistoryToReply(
+ if (!appendResultHistoryToReply(
&reply, bmcontextId, "", timestamp1, timestamp2, diffTolerance, stabTolerance, maxSize))
return xmlConvert(reply);
@@ -2664,7 +2650,7 @@ QByteArray BMRequest_GetHistory2::toReplyBuffer()
if (!getBMContextId(
&reply, &bmcontextId1, benchmarkId, metric, platform, host, gitRepo1, gitBranch1))
return xmlConvert(reply);
- if (!appendBranchHistoryToReply(
+ if (!appendResultHistoryToReply(
&reply, bmcontextId1, "1", timestamp1, timestamp2, diffTolerance, stabTolerance,
maxSize))
return xmlConvert(reply);
@@ -2673,7 +2659,7 @@ QByteArray BMRequest_GetHistory2::toReplyBuffer()
if (!getBMContextId(
&reply, &bmcontextId2, benchmarkId, metric, platform, host, gitRepo2, gitBranch2))
return xmlConvert(reply);
- if (!appendBranchHistoryToReply(
+ if (!appendResultHistoryToReply(
&reply, bmcontextId2, "2", timestamp1, timestamp2, diffTolerance, stabTolerance,
maxSize))
return xmlConvert(reply);
@@ -7223,7 +7209,7 @@ void BMRequest_GetIXHistories::handleReply_Image(const QStringList &args) const
QString error_;
Plotter *plotter =
new HistoriesPlotter(
- rhInfos, true, evalTimestamp, &basePos, &extraPos, &descr, false, true);
+ rhInfos, true, evalTimestamp, &basePos, &extraPos, 0, &descr, false, true);
const QImage image = plotter->createImage(&error_);
// Free dynamic memory ...
@@ -7495,20 +7481,20 @@ struct RHStats {
// ### 2 B DOCUMENTED!
static QString createRHDetailsLink(
- const QString &webServer, const QString &server, const QString &styleSheet, int bmcontextId)
+ const QString &webServer, const QString &server, const QString &styleSheet, int bmcontextId,
+ int medianWinSize, int fromTime, int toTime)
{
QString url;
url = QString("%1/cgi-bin/bmclientwrapper").arg(webServer);
url += QString("?command=-server %1").arg(server);
- url +=
- " get detailspage dummyTestCase dummyTestFunction dummyDataTag dummyMetric dummyPlatform"
- " dummyHost dummyGitRepo dummyGitBranch"; // ### this is a bit silly, so fix it by making
- // the bmclient independent of argument positions
- QDateTime dateTime = QDateTime::currentDateTime();
- url += QString(" -timerange first last 0.00 0 -1 %1 %2 -bmcontextid %3")
- .arg(styleSheet)
- .arg(dateTime.toTime_t())
- .arg(bmcontextId);
+ url += QString(
+ " asfstats get detailspage2 -bmcontextid %1 -medianwinsize %2 -fromtime %3 -totime %4"
+ " -stylesheet %5")
+ .arg(bmcontextId)
+ .arg(medianWinSize)
+ .arg(fromTime)
+ .arg(toTime)
+ .arg(styleSheet);
return QString("<a href=\"%1\">details</a>").arg(url);
}
@@ -7561,8 +7547,7 @@ void BMRequest_ASFStatsGetValues::handleReply_HTML(const QStringList &args) cons
reply += "<br /><br /><table><tr><td style=\"background-color:#eeeeee\">";
- reply += "This page shows statistics suitable for presentation on the ASF Scorecard ";
- reply += "<span style=\"color:red\"> (More documentation to follow soon!)</span>.";
+ reply += "This page shows statistics suitable for presentation on the ASF Scorecard.";
reply += "</td></tr></table>\n";
@@ -7790,7 +7775,9 @@ void BMRequest_ASFStatsGetValues::handleReply_HTML(const QStringList &args) cons
"<td>%10</td>"
"</tr>\n")
.arg(i)
- .arg(createRHDetailsLink(webServer, server, styleSheet, rhStats.at(i)->id))
+ .arg(createRHDetailsLink(
+ webServer, server, styleSheet, rhStats.at(i)->id, medianWinSize,
+ fromTimestamp, toTimestamp))
.arg(rhStats.at(i)->rg ? "RG" : "")
.arg(rhStats.at(i)->uc ? "UC" : "")
.arg(rhStats.at(i)->im ? "IM" : "")
@@ -7808,3 +7795,379 @@ void BMRequest_ASFStatsGetValues::handleReply_HTML(const QStringList &args) cons
BMMisc::printHTMLOutput(reply);
}
+
+
+// --- ASFStatsGetValues2 ---
+QByteArray BMRequest_ASFStatsGetValues2::toRequestBuffer(QString *)
+{
+ const QString request =
+ QString("<request type=\"%1\"><args bmcontextId=\"%2\" cacheKey=\"%3\" /></request>")
+ .arg(name())
+ .arg(bmcontextId)
+ .arg(cacheKey);
+
+ return xmlConvert(request);
+}
+
+QByteArray BMRequest_ASFStatsGetValues2::toReplyBuffer()
+{
+ QString error;
+ QString reply;
+ bool ok;
+
+ QDomElement argsElem = doc.elementsByTagName("args").at(0).toElement();
+
+ // Check if a cached reply can be returned ...
+ int cacheKey_ = argsElem.attributeNode("cacheKey").value().toInt(&ok);
+ if (ok) {
+ reply = Cache::instance()->get(cacheKey_);
+ if (!reply.isEmpty())
+ return xmlConvert(reply);
+ }
+
+ // Compute from scratch ...
+
+ bmcontextId = argsElem.attributeNode("bmcontextId").value().toInt(&ok);
+ Q_ASSERT(ok);
+
+ QString testCase;
+ QString testFunction;
+ QString dataTag;
+ QString metric;
+ QString platform;
+ QString host;
+ QString gitRepo;
+ QString gitBranch;
+
+ if (!extractBMAndContextFromRH(
+ bmcontextId, name(), database, &testCase, &testFunction, &dataTag,
+ &metric, &platform, &host, &gitRepo, &gitBranch, &error)) {
+ return xmlConvert(error);
+ }
+
+ reply = QString("<reply type=\"%1\">").arg(name());
+
+ const qreal diffTolerance = 0.0; // 4 NOW
+ const qreal stabTolerance = 0; // 4 NOW
+ if (!appendResultHistoryToReply(
+ &reply, bmcontextId, "", "first", "last", diffTolerance, stabTolerance, -1))
+ return xmlConvert(reply);
+
+ reply += QString(
+ "<bmcontext testCase=\"%1\" testFunction=\"%2\" dataTag=\"%3\" metric=\"%4\" "
+ "platform=\"%5\" host=\"%6\" gitRepo=\"%7\" gitBranch=\"%8\" />")
+ .arg(testCase).arg(testFunction).arg(dataTag).arg(metric).arg(platform)
+ .arg(host).arg(gitRepo).arg(gitBranch);
+
+ // Finalize the reply and cache it ...
+ cacheKey_ = Cache::instance()->nextId();
+ reply += QString("<args cacheKey=\"%1\" /></reply>").arg(cacheKey_);
+ Cache::instance()->put(cacheKey_, reply);
+
+ return xmlConvert(reply);
+}
+
+void BMRequest_ASFStatsGetValues2::handleReply_HTML(const QStringList &args) const
+{
+ QString error;
+
+ error = doc.elementsByTagName("reply").at(0).toElement().attributeNode("error").value();
+ if (!error.isEmpty()) {
+ BMMisc::printHTMLErrorPage(
+ QString(
+ "failed to create details page for RH contributing to ASF stats: %1").arg(error));
+ return;
+ }
+
+ QStringList optValues;
+
+ // Get style sheet ...
+ QString styleSheet;
+ if (BMMisc::getOption(args, "-stylesheet", &optValues, 1, 0)) {
+ styleSheet = optValues.first().trimmed();
+ } else {
+ BMMisc::printHTMLErrorPage("-stylesheet option not found");
+ return;
+ }
+
+ // Get median window size ...
+ int medianWinSize = -1;
+ if (BMMisc::getOption(args, "-medianwinsize", &optValues, 1, 0)) {
+ bool ok;
+ medianWinSize = optValues.first().toInt(&ok);
+ if ((!ok) || (medianWinSize < 1)) {
+ BMMisc::printHTMLErrorPage("-medianwinsize not a positive integer");
+ return;
+ }
+ } else {
+ BMMisc::printHTMLErrorPage("-medianwinsize option not found");
+ return;
+ }
+
+ // Get 'from' timestamp ...
+ int fromTimestamp = -1;
+ if (BMMisc::getOption(args, "-fromtime", &optValues, 1, 0)) {
+ bool ok;
+ fromTimestamp = optValues.first().toInt(&ok);
+ if ((!ok) || (fromTimestamp < 0)) {
+ BMMisc::printHTMLErrorPage("-fromtime not a non-negative integer");
+ return;
+ }
+ } else {
+ BMMisc::printHTMLErrorPage("-fromtime option not found");
+ return;
+ }
+
+ // Get 'to' timestamp ...
+ int toTimestamp = -1;
+ if (BMMisc::getOption(args, "-totime", &optValues, 1, 0)) {
+ bool ok;
+ toTimestamp = optValues.first().toInt(&ok);
+ if ((!ok) || (toTimestamp < fromTimestamp)) {
+ BMMisc::printHTMLErrorPage(QString("-totime not >= %1").arg(fromTimestamp));
+ return;
+ }
+ } else {
+ BMMisc::printHTMLErrorPage("-totime option not found");
+ return;
+ }
+
+ // *** Header ***
+ QString reply = QString("<html>\n<head>\n");
+ reply += QString("<link rel=\"stylesheet\" type=\"text/css\" href=\"%1\" />\n").arg(styleSheet);
+ reply += QString("</head>\n<body>\n");
+
+ reply += "<span style=\"font-size:18\">ASF stats contributor</span>";
+
+ reply += "<br /><br /><table><tr><td style=\"background-color:#eeeeee\">";
+
+ reply += "This page shows a result history that contributed to the ASF stats.";
+
+ reply += "<br /><br />";
+
+ reply += QString(
+ "Normal data points are indicated in black, whereas potential outliers "
+ "(filtered out by median of %1 smoothing) are indicated in red.")
+ .arg(medianWinSize);
+
+ reply += "<br /><br />";
+
+ reply +=
+ "The 'from' and 'to' times are indicated by dashed and solid brown vertical lines "
+ "respectively. The data points to be used for sampling the result history at "
+ "these times are indicated in the graph as an orange square and an orange circle "
+ "respectively. If the result history is classified as <i>stable</i>, the two samples "
+ "serve to further classify it as <i>regressed</i>, <i>unchanged</i>, or <i>improved</i>. "
+ "Otherwise the two samples are just ignored.";
+
+ reply += "</td></tr></table>\n";
+
+
+ // *** Plot ***
+
+ // reply += "<br />args:<br />";
+ // for (int i = 0; i < args.size(); ++i)
+ // reply += QString("arg %1: &gt;%2&lt;<br />").arg(i).arg(args.at(i));
+ // reply += "<br />";
+
+ QDomElement argsElem = doc.elementsByTagName("args").at(0).toElement();
+
+ reply += "<br />\n<img src=\"bmclientwrapper?command=";
+ for (int i = args.indexOf("-server"); i < args.size(); ++i)
+ reply += QString("%1 ")
+ .arg((args.at(i) == "detailspage2") ? QLatin1String("plot2") : args.at(i));
+ reply += QString("-cachekey %1 ").arg(argsElem.attributeNode("cacheKey").value());
+ reply += "-httpheader ";
+ reply += "\" />\n";
+
+
+ // *** Table of values ***
+
+ reply += "<br />\n<br />\n";
+
+ reply += "<table style=\"text-align:right\">";
+
+ reply += QString(
+ "<tr>"
+ "<th class=\"details_table_head\">n</th>"
+ "<th class=\"details_table_head\">Timestamp</th>"
+ "<th class=\"details_table_head\">SHA-1</th>"
+ "<th class=\"details_table_head\">Value</th>"
+ "</tr>\n");
+
+ // Get the time series ...
+ QDomNodeList resultNodes = doc.elementsByTagName("result");
+ QList<int> timestamps;
+ QList<qreal> values;
+ QStringList sha1s;
+ for (int i = 0; i < resultNodes.size(); ++i) {
+ bool ok;
+ QDomElement resultElem = resultNodes.at(i).toElement();
+ timestamps += resultElem.attributeNode("timestamp").value().toInt(&ok);
+ Q_ASSERT(ok);
+ values += resultElem.attributeNode("value").value().toDouble(&ok);
+ Q_ASSERT(ok);
+ sha1s += resultElem.attributeNode("sha1").value();
+ }
+
+ ResultHistoryInfo rhInfo(-1, timestamps, values, medianWinSize, "");
+
+ // Get the actual data positions used for sampling at 'from' and 'to' timestamps ...
+ int fromPos = -1;
+ rhInfo.findSmoothPos(fromTimestamp, &fromPos);
+ int toPos = -1;
+ rhInfo.findSmoothPos(toTimestamp, &toPos);
+
+ for (int i = 0; i < timestamps.size(); ++i) {
+
+ const int i_ = resultNodes.size() - 1 - i; // reverse chronological order
+ reply += QString(
+ "<tr>"
+ "<td>%1</td>"
+ "<td>%2</td>"
+ "<td>%3</td>"
+ "<td%4>%5</td>"
+ "</tr>\n"
+ )
+ .arg(i_ + 1)
+ .arg(timestamps.at(i_))
+ .arg(sha1s.at(i_))
+ .arg(rhInfo.isOutlier(i_)
+ ? " style=\"color:red\""
+ : (((i_ == fromPos) || (i_ == toPos))
+ ? " style=\"background-color:#ff944d\""
+ : ""
+ ))
+ .arg(values.at(i_));
+ }
+
+ reply += "</table>\n";
+
+ reply += "</body></html>";
+
+ BMMisc::printHTMLOutput(reply);
+}
+
+void BMRequest_ASFStatsGetValues2::handleReply_Image(const QStringList &args) const
+{
+ const QString error =
+ doc.elementsByTagName("reply").at(0).toElement().attributeNode("error").value();
+
+ if (!error.isEmpty()) {
+ BMMisc::printHTMLErrorPage(
+ QString(
+ "failed to create plot for ASF stats contributor: %1").arg(error));
+ return;
+ }
+
+ bool ok;
+ QStringList optValues;
+
+
+ // Get median window size ...
+ int medianWinSize = -1;
+ if (BMMisc::getOption(args, "-medianwinsize", &optValues, 1, 0)) {
+ medianWinSize = optValues.first().toInt(&ok);
+ if ((!ok) || (medianWinSize < 1)) {
+ BMMisc::printHTMLErrorPage("-medianwinsize not a positive integer");
+ return;
+ }
+ } else {
+ BMMisc::printHTMLErrorPage("-medianwinsize option not found");
+ return;
+ }
+
+ // Get 'from' timestamp ...
+ int fromTimestamp = -1;
+ if (BMMisc::getOption(args, "-fromtime", &optValues, 1, 0)) {
+ fromTimestamp = optValues.first().toInt(&ok);
+ if ((!ok) || (fromTimestamp < 0)) {
+ BMMisc::printHTMLErrorPage("-fromtime not a non-negative integer");
+ return;
+ }
+ } else {
+ BMMisc::printHTMLErrorPage("-fromtime option not found");
+ return;
+ }
+
+ // Get 'to' timestamp ...
+ int toTimestamp = -1;
+ if (BMMisc::getOption(args, "-totime", &optValues, 1, 0)) {
+ toTimestamp = optValues.first().toInt(&ok);
+ if ((!ok) || (toTimestamp < fromTimestamp)) {
+ BMMisc::printHTMLErrorPage(QString("-totime not >= %1").arg(fromTimestamp));
+ return;
+ }
+ } else {
+ BMMisc::printHTMLErrorPage("-totime option not found");
+ return;
+ }
+
+
+ QDomElement argsElem = doc.elementsByTagName("args").at(0).toElement();
+
+
+ // Get the BM context ...
+ QDomNodeList bmcontextNodes = doc.elementsByTagName("bmcontext");
+ QDomElement bmcontextElem = bmcontextNodes.at(0).toElement();
+ const QString testCase = bmcontextElem.attributeNode("testCase").value();
+ const QString testFunction = bmcontextElem.attributeNode("testFunction").value();
+ const QString dataTag = bmcontextElem.attributeNode("dataTag").value();
+ const QString metric = bmcontextElem.attributeNode("metric").value();
+ const QString platform = bmcontextElem.attributeNode("platform").value();
+ const QString host = bmcontextElem.attributeNode("host").value();
+ const QString gitRepo = bmcontextElem.attributeNode("gitRepo").value();
+ const QString gitBranch = bmcontextElem.attributeNode("gitBranch").value();
+
+ // Get the time series ...
+ QDomNodeList resultNodes = doc.elementsByTagName("result");
+ QList<int> timestamps;
+ QList<qreal> values;
+ for (int i = 0; i < resultNodes.size(); ++i) {
+ bool ok;
+ QDomElement resultElem = resultNodes.at(i).toElement();
+ timestamps += resultElem.attributeNode("timestamp").value().toInt(&ok);
+ Q_ASSERT(ok);
+ values += resultElem.attributeNode("value").value().toDouble(&ok);
+ Q_ASSERT(ok);
+ }
+
+ QList<ResultHistoryInfo *> rhInfos;
+ ResultHistoryInfo *rhInfo = new ResultHistoryInfo(
+ -1, timestamps, values, medianWinSize, metric, platform, host, gitRepo, gitBranch,
+ testCase, testFunction, dataTag);
+ rhInfos.append(rhInfo);
+
+ // Get the actual data positions used for sampling at 'from' and 'to' timestamps ...
+ int fromPos = -1;
+ rhInfo->findSmoothPos(fromTimestamp, &fromPos);
+ int toPos = -1;
+ rhInfo->findSmoothPos(toTimestamp, &toPos);
+ QList<QList<int> > extraPos;
+ extraPos.append(QList<int>() << fromPos << toPos);
+ const QList<int> extraTimestamps = QList<int>() << fromTimestamp << toTimestamp;
+
+ const QStringList descr = QStringList() << "<no description>";
+
+
+ // Create image ...
+ QString error_;
+ Plotter *plotter =
+ new HistoriesPlotter(
+ rhInfos, true, -1, 0, &extraPos, &extraTimestamps, &descr, false, true);
+ const QImage image = plotter->createImage(&error_);
+
+ // Free dynamic memory ...
+ for (int i = 0; i < rhInfos.size(); ++i)
+ delete rhInfos.at(i);
+
+ // Output image ...
+ if (!image.isNull()) {
+ BMMisc::printImageOutput(
+ image, "png", QString(), BMMisc::hasOption(args, "-httpheader"));
+ } else {
+ BMMisc::printHTMLErrorPage(
+ QString(
+ "failed to create plot for main index contributors: %1").arg(error_));
+ }
+}
diff --git a/src/bm/bmrequest.h b/src/bm/bmrequest.h
index 12d61a1..f7a5a2b 100644
--- a/src/bm/bmrequest.h
+++ b/src/bm/bmrequest.h
@@ -113,7 +113,7 @@ protected:
const QString &testCaseFilter, const QString &testFunctionFilter,
const QString &dataTagFilter, const bool leadingAnd,
const QString &prefix = QString()) const;
- bool appendBranchHistoryToReply(
+ bool appendResultHistoryToReply(
QString *reply, int bmcontextId, const QString &suffix, const QString &timestamp1,
const QString &timestamp2, qreal diffTolerance, int stabTolerance, int maxSize) const;
@@ -308,13 +308,13 @@ public:
const QString &gitRepo, const QString &gitBranch,
const QString &timestamp1, const QString &timestamp2,
const qreal diffTolerance, const int stabTolerance, const int maxSize,
- const QString &styleSheet, const int currTimestamp, const int explicitBMContextId = -1)
+ const QString &styleSheet, const int currTimestamp)
: testCase(testCase), testFunction(testFunction), dataTag(dataTag), metric(metric)
, platform(platform), host(host), gitRepo(gitRepo), gitBranch(gitBranch)
, timestamp1(timestamp1), timestamp2(timestamp2)
, diffTolerance(diffTolerance), stabTolerance(stabTolerance)
, maxSize(maxSize), styleSheet(styleSheet), currTimestamp(currTimestamp)
- , explicitBMContextId(explicitBMContextId) {}
+ {}
BMRequest_GetHistory(const QDomDocument &doc) : BMRequest(doc) {}
private:
mutable QString testCase;
@@ -332,7 +332,6 @@ private:
mutable int maxSize;
mutable QString styleSheet;
mutable int currTimestamp;
- mutable int explicitBMContextId;
QString name() const { return "GetHistory"; }
QByteArray toRequestBuffer(QString *error);
@@ -787,4 +786,26 @@ private:
void handleReply_HTML(const QStringList &args) const;
};
+// ### 2 B DOCUMENTED (in bmclient.html and bmproto.html)
+class BMRequest_ASFStatsGetValues2 : public BMRequest
+{
+public:
+ BMRequest_ASFStatsGetValues2(
+ const int bmcontextId, const QString &cacheKey)
+ : bmcontextId(bmcontextId), cacheKey(cacheKey) {}
+ BMRequest_ASFStatsGetValues2(const QDomDocument &doc) : BMRequest(doc) {}
+
+private:
+ int bmcontextId;
+ QString cacheKey;
+
+ QString name() const { return "ASFStatsGetValues2"; }
+ QByteArray toRequestBuffer(QString *error);
+ QByteArray toReplyBuffer();
+ void handleReply_Raw(const QStringList &args) const { Q_UNUSED(args); /* 2 B DONE! */ };
+ void handleReply_JSON(const QStringList &args) const { Q_UNUSED(args); /* 2 B DONE! */ }
+ void handleReply_HTML(const QStringList &args) const;
+ void handleReply_Image(const QStringList &args) const;
+};
+
#endif // BMREQUEST_H
diff --git a/src/bm/plotter.cpp b/src/bm/plotter.cpp
index 8d3592e..8488772 100644
--- a/src/bm/plotter.cpp
+++ b/src/bm/plotter.cpp
@@ -696,13 +696,15 @@ bool IndexPlotter::drawScenes(
HistoriesPlotter::HistoriesPlotter(
const QList<ResultHistoryInfo *> &rhInfos, const bool showBenchmark, const int baseTimestamp,
- const QList<int> *basePos, const QList<QList<int> > *extraPos, const QStringList *extraDescr,
+ const QList<int> *basePos, const QList<QList<int> > *extraPos,
+ const QList<int> *extraTimestamps, const QStringList *extraDescr,
const bool commonValueRange, const bool showOutliers)
: rhInfos(rhInfos)
, showBenchmark(showBenchmark)
, baseTimestamp(baseTimestamp)
, basePos(basePos)
, extraPos(extraPos)
+ , extraTimestamps(extraTimestamps)
, extraDescr(extraDescr)
, commonValueRange(commonValueRange)
, showOutliers(showOutliers)
@@ -912,6 +914,25 @@ bool HistoriesPlotter::drawScenes(
scene_far->addLine(btx, ymin, btx, ymax, QPen(color, 2));
}
+ if (extraTimestamps) {
+ // Draw lines indicating extra timestamps ...
+
+ for (int i = 0; i < extraTimestamps->size(); ++i) {
+
+ const int t = extraTimestamps->at(i);
+
+ if ((t < loTimestamp) || (t > hiTimestamp))
+ continue;
+
+ QColor color("#803300");
+ color.setAlpha(200);
+ const qreal tx = xmin + (t - loTimestamp) * xfact;
+ scene_far->addLine(
+ tx, ymin, tx, ymax,
+ QPen(color, 2, (i == 0) ? Qt::DashLine : Qt::SolidLine));
+ }
+ }
+
const qreal dpSize = 4;
// Draw history curve between data points ...
@@ -974,17 +995,22 @@ bool HistoriesPlotter::drawScenes(
const qreal dpSize_large_2 = dpSize_large / 2;
for (int i = 0; i < exPos.size(); ++i) {
+
+ const int pos = exPos.at(i);
+ if ((pos < 0) || (pos >= x.size()))
+ continue;
+
QPainterPath path;
if (i == 0) {
path.addRect(
- x.at(exPos.at(i)) - dpSize_large_2,
- y.at(exPos.at(i)) - dpSize_large_2,
+ x.at(pos) - dpSize_large_2,
+ y.at(pos) - dpSize_large_2,
dpSize_large, dpSize_large);
} else {
path.addEllipse(
- x.at(exPos.at(i)) - dpSize_large_2,
- y.at(exPos.at(i)) - dpSize_large_2,
+ x.at(pos) - dpSize_large_2,
+ y.at(pos) - dpSize_large_2,
dpSize_large, dpSize_large);
}
@@ -1123,5 +1149,40 @@ bool HistoriesPlotter::drawScenes(
}
+ if (extraTimestamps) {
+
+ for (int i = 0; i < extraTimestamps->size(); ++i) {
+
+ const int t = extraTimestamps->at(i);
+
+ if ((t < loTimestamp) || (t > hiTimestamp))
+ continue;
+
+ const QString xLabel = BMMisc::compactDateString(t);
+ font.setPointSize(12);
+
+ const qreal xpos = xmin + (t - loTimestamp) * xfact;
+ const QColor color("#803300");
+
+ // ... top x axis label ...
+ {
+ QGraphicsSimpleTextItem *text = scene_near->addSimpleText(xLabel, font);
+ text->setPos(
+ xpos - text->boundingRect().width() / 2,
+ ymin - text->boundingRect().height() + labelPad / 2);
+ text->setBrush(color);
+ }
+
+ // ... bottom x axis label ...
+ {
+ QGraphicsSimpleTextItem *text = scene_near->addSimpleText(xLabel, font);
+ text->setPos(
+ xpos - text->boundingRect().width() / 2,
+ ymax + labelPad / 4);
+ text->setBrush(color);
+ }
+ }
+ }
+
return true;
}
diff --git a/src/bm/plotter.h b/src/bm/plotter.h
index 8bf5056..32b194a 100644
--- a/src/bm/plotter.h
+++ b/src/bm/plotter.h
@@ -102,8 +102,9 @@ public:
HistoriesPlotter(
const QList<ResultHistoryInfo *> &rhInfos, const bool showBenchmark = false,
const int baseTimestamp = -1, const QList<int> *basePos = 0,
- const QList<QList<int> > *extraPos = 0, const QStringList *extraDescr = 0,
- const bool commonValueRange = true, const bool showOutliers = false);
+ const QList<QList<int> > *extraPos = 0, const QList<int> *extraTimestamps = 0,
+ const QStringList *extraDescr = 0, const bool commonValueRange = true,
+ const bool showOutliers = false);
private:
QList<ResultHistoryInfo *> rhInfos;
@@ -111,6 +112,7 @@ private:
int baseTimestamp;
const QList<int> *basePos;
const QList<QList<int> > *extraPos;
+ const QList<int> *extraTimestamps;
const QStringList *extraDescr;
bool commonValueRange;
bool showOutliers;
diff --git a/src/bmclient/main.cpp b/src/bmclient/main.cpp
index 0823d20..8c36a86 100644
--- a/src/bmclient/main.cpp
+++ b/src/bmclient/main.cpp
@@ -119,6 +119,9 @@ private:
BMRequest * createASFStatsGetValuesRequest(
const QStringList &args, QString *error,
const QString &command = "asfstats get values") const;
+ BMRequest * createASFStatsGetValues2Request(
+ const QStringList &args, QString *error,
+ const QString &command = "asfstats get values2") const;
mutable BMRequest::OutputFormat explicitOutputFormat;
mutable bool useExplicitOutputFormat;
BMRequest::OutputFormat outputFormat() const;
@@ -798,15 +801,6 @@ BMRequest * Executor::createRequest(const QStringList &args, QString *error) con
return createASFStatsGetValuesRequest(args, error);
- // } else if (
- // (args.size() >= 3) && (args.at(0) == "asfstats") && (args.at(1) == "get")
- // && (args.at(2) == "plot")) {
- // // --- 'asfstats get plot' command ---
-
- // BMRequest *request = createASFStatsGetValuesRequest(args, error, "asfstats get plot");
- // setOutputFormat(BMRequest::Image);
- // return request;
-
} else if (
(args.size() >= 3) && (args.at(0) == "asfstats") && (args.at(1) == "get")
&& (args.at(2) == "detailspage")) {
@@ -817,6 +811,33 @@ BMRequest * Executor::createRequest(const QStringList &args, QString *error) con
setOutputFormat(BMRequest::HTML);
return request;
+ } else if (
+ (args.size() >= 3) && (args.at(0) == "asfstats") && (args.at(1) == "get")
+ && (args.at(2) == "values2")) {
+ // --- 'asfstats get values2' command ---
+
+ return createASFStatsGetValues2Request(args, error);
+
+ } else if (
+ (args.size() >= 3) && (args.at(0) == "asfstats") && (args.at(1) == "get")
+ && (args.at(2) == "detailspage2")) {
+ // --- 'asfstats get detailspage2' command ---
+
+ BMRequest *request =
+ createASFStatsGetValues2Request(args, error, "asfstats get detailspage2");
+ setOutputFormat(BMRequest::HTML);
+ return request;
+
+ } else if (
+ (args.size() >= 3) && (args.at(0) == "asfstats") && (args.at(1) == "get")
+ && (args.at(2) == "plot2")) {
+ // --- 'asfstats get plot2' command ---
+
+ BMRequest *request =
+ createASFStatsGetValues2Request(args, error, "asfstats get plot2");
+ setOutputFormat(BMRequest::Image);
+ return request;
+
} else if ((args.size() >= 2) && (args.at(0) == "get") && (args.at(1) == "detailspage")) {
// --- 'get detailspage' command ---
@@ -915,21 +936,10 @@ BMRequest * Executor::createGetHistoryRequest(const QStringList &args, QString *
currTimestamp = -1;
}
- // Extract BM context ID (which overrides other values if specified) ...
- int explicitBMContextId = -1;
- if (BMMisc::getOption(args, "-bmcontextid", &values, 1, 0, error)) {
- bool ok;
- explicitBMContextId = values.at(0).toInt(&ok);
- if ((!ok) || (explicitBMContextId < 0)) {
- *error = "failed to extract stability tolerance as non-negative integer";
- return 0;
- }
- }
-
return new BMRequest_GetHistory(
args.at(2), args.at(3), args.at(4), args.at(5), args.at(6), args.at(7),
args.at(8), args.at(9), timeRange.first, timeRange.second, diffTolerance,
- stabTolerance, maxSize, args.at(16), currTimestamp, explicitBMContextId);
+ stabTolerance, maxSize, args.at(16), currTimestamp);
}
BMRequest * Executor::createGetHistory2Request(const QStringList &args, QString *error) const
@@ -1425,6 +1435,45 @@ BMRequest * Executor::createASFStatsGetValuesRequest(
branchFilter, fromTimestamp, toTimestamp, diffTol, stabTol, sfTol, lfTol, maxLDTol);
}
+BMRequest * Executor::createASFStatsGetValues2Request(
+ const QStringList &args, QString *error, const QString &command) const
+{
+ Q_UNUSED(command);
+
+ QStringList values;
+
+ // Extract BM context ID ...
+ int bmcontextId = -1;
+ if (BMMisc::getOption(args, "-bmcontextid", &values, 1, 0, error)) {
+ bool ok;
+ bmcontextId = values.at(0).toInt(&ok);
+ if ((!ok) || (bmcontextId < 0)) {
+ *error = "failed to extract BM context ID as a non-negative integer";
+ return 0;
+ }
+ } else {
+ if (error->isEmpty())
+ *error = "-bmcontextid option not found";
+ return 0;
+ }
+
+ // Get cache key ...
+ QString cacheKey;
+ if (BMMisc::getOption(args, "-cachekey", &values, 1, 0, error)) {
+ cacheKey = values.at(0).trimmed();
+ bool ok;
+ cacheKey.toInt(&ok);
+ if (!ok) {
+ *error = "failed to extract cache key as an integer";
+ return 0;
+ }
+ } else if (!error->isEmpty()) {
+ return 0;
+ }
+
+ return new BMRequest_ASFStatsGetValues2(bmcontextId, cacheKey);
+}
+
// ### 2 B DOCUMENTED!
static void splitQuotedArgs(const QString &arg_s, QStringList *args)
{
@@ -1730,7 +1779,7 @@ class DirectExecutor : public Executor
<<
"get history <test case> <test function> <data tag> <metric> "
- "<platform> <host> <git repo> <git branch> \\\n"
+ " <platform> <host> <git repo> <git branch> \\\n"
" -timerange <from> <to> \\\n"
" <difference tolerance> <stability tolerance> <max size> \\\n"
" <style sheet> <current Unix timestamp>\n"
@@ -1795,7 +1844,7 @@ class DirectExecutor : public Executor
<<
"index get plot <SAME AS 'index get values' except that the optional \\\n"
- " -httpHeader and -cachekey options are recognized>\n"
+ " -httpheader and -cachekey options are recognized>\n"
<<
"index get detailspage <SAME AS 'index get plot'> except that the mandatory \\\n"
@@ -1822,7 +1871,7 @@ class DirectExecutor : public Executor
<<
"get histories plot <SAME AS 'get histories' except that the optional \\\n"
- " -httpHeader and -cachekey options are recognized>\n"
+ " -httpheader and -cachekey options are recognized>\n"
<<
"get histories detailspage <SAME AS 'get histories'> except that the mandatory \\\n"
@@ -1838,7 +1887,7 @@ class DirectExecutor : public Executor
<<
"get ixhistories plot <SAME AS 'get ixhistories' except that the optional \\\n"
- " -httpHeader and -cachekey options are recognized>\n"
+ " -httpheader and -cachekey options are recognized>\n"
<<
"get ixhistories detailspage <SAME AS 'get ixhistories'> except that the mandatory \\\n"
@@ -1854,9 +1903,21 @@ class DirectExecutor : public Executor
<<
"asfstats get detailspage <SAME AS 'asfstats get values'> except that the \\\n"
- " optional -httpHeader and -cachekey options, and the mandatory \\\n"
+ " optional -httpheader and -cachekey options, and the mandatory \\\n"
" -stylesheet option are recognized\n"
+ <<
+ "asfstats get values2 -bmcontextid <...>\n"
+
+ <<
+ "asfstats get plot2 <SAME AS 'asfstats get detailspage2' except that the optional \\\n"
+ " -httpheader and -cachekey options are recognized>\n"
+
+ <<
+ "asfstats get detailspage2 <SAME AS 'asfstats get values2'> except that the \\\n"
+ " mandatory -medianwinsize, -fromtime, -totime, and -stylesheet options are \\\n"
+ " recognized\n"
+
;
qDebug() << "\nNote: the -server option may be replaced by a BMSERVER=<host>:<port> "