summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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> "