summaryrefslogtreecommitdiffstats
path: root/src/bm/bmrequest.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/bm/bmrequest.cpp')
-rw-r--r--src/bm/bmrequest.cpp447
1 files changed, 405 insertions, 42 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_));
+ }
+}