diff options
Diffstat (limited to 'src/bm/bmrequest.cpp')
-rw-r--r-- | src/bm/bmrequest.cpp | 447 |
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 ×tamp1, const QString ×tamp2, 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: >%2<<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_)); + } +} |