From ded93b43d2de61441b3bc30898908c2a6c07bd9a Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Mon, 16 Jun 2014 13:50:06 +0200 Subject: [PATCH] Gerrit: Read out dependency information. Pass on the command line argument --dependencies to the client, read out dependent/needed by changes and display them in the detailed text view. Move the toHtml() method from GerritChange to GerritModel to be able to display titles of dependent changes. Change-Id: I1d0b67a995d45a9fe5b7bdb3ff51cc905d58478f Reviewed-by: Orgad Shaneh Reviewed-by: Friedemann Kleint --- src/plugins/git/gerrit/gerritdialog.cpp | 2 +- src/plugins/git/gerrit/gerritmodel.cpp | 132 +++++++++++++++++++----- src/plugins/git/gerrit/gerritmodel.h | 8 +- 3 files changed, 112 insertions(+), 30 deletions(-) diff --git a/src/plugins/git/gerrit/gerritdialog.cpp b/src/plugins/git/gerrit/gerritdialog.cpp index 70fb173a287..b9b80a55842 100644 --- a/src/plugins/git/gerrit/gerritdialog.cpp +++ b/src/plugins/git/gerrit/gerritdialog.cpp @@ -300,7 +300,7 @@ void GerritDialog::slotCurrentChanged() const bool valid = current.isValid(); if (valid) { const int row = m_filterModel->mapToSource(current).row(); - m_detailsBrowser->setText(m_model->change(row)->toHtml()); + m_detailsBrowser->setText(m_model->toHtml(row)); } else { m_detailsBrowser->setText(QString()); } diff --git a/src/plugins/git/gerrit/gerritmodel.cpp b/src/plugins/git/gerrit/gerritmodel.cpp index dd5dcfcf755..000140c5766 100644 --- a/src/plugins/git/gerrit/gerritmodel.cpp +++ b/src/plugins/git/gerrit/gerritmodel.cpp @@ -187,26 +187,6 @@ int GerritPatchSet::approvalLevel() const return value; } -QString GerritChange::toHtml() const -{ - // Keep in sync with list model headers. - static const QString format = GerritModel::tr( - "" - "" - "" - "" - "" - "" - "" - "%10" - "" - "
Subject%1
Number%2
Owner%3 %4
Project%5 (%6)
Status%7, %8
Patch set%9
URL%11
"); - return format.arg(title).arg(number).arg(owner, email, project, branch) - .arg(status, lastUpdated.toString(Qt::DefaultLocaleShortDate)) - .arg(currentPatchSet.patchSetNumber) - .arg(currentPatchSet.approvalsToHtml(), url); -} - QString GerritChange::filterString() const { const QChar blank = QLatin1Char(' '); @@ -300,7 +280,8 @@ QueryContext::QueryContext(const QStringList &queries, m_progress.setProgressRange(0, m_queries.size()); // Determine binary and common command line arguments. - m_baseArguments << QLatin1String("query") << QLatin1String("--current-patch-set") + m_baseArguments << QLatin1String("query") << QLatin1String("--dependencies") + << QLatin1String("--current-patch-set") << QLatin1String("--format=JSON"); m_binary = m_baseArguments.front(); m_baseArguments.pop_front(); @@ -431,20 +412,75 @@ GerritModel::~GerritModel() { } +static inline GerritChangePtr changeFromItem(const QStandardItem *item) +{ + return qvariant_cast(item->data(GerritModel::GerritChangeRole)); +} + GerritChangePtr GerritModel::change(int row) const { if (row >= 0 && row < rowCount()) - return qvariant_cast(item(row, 0)->data(GerritChangeRole)); + return changeFromItem(item(row, 0)); return GerritChangePtr(new GerritChange); } -int GerritModel::indexOf(int gerritNumber) const +QString GerritModel::dependencyHtml(const QString &header, const QString &changeId, + const QString &serverPrefix) const +{ + QString res; + if (changeId.isEmpty()) + return res; + QTextStream str(&res); + str << "" << header << "' << changeId << ""; + if (const QStandardItem *item = itemForId(changeId)) + str << " (" << changeFromItem(item)->title << ')'; + str << ""; + return res; +} + +QString GerritModel::toHtml(int row) const +{ + static const QString subjectHeader = GerritModel::tr("Subject"); + static const QString numberHeader = GerritModel::tr("Number"); + static const QString ownerHeader = GerritModel::tr("Owner"); + static const QString projectHeader = GerritModel::tr("Project"); + static const QString statusHeader = GerritModel::tr("Status"); + static const QString patchSetHeader = GerritModel::tr("Patch set"); + static const QString urlHeader = GerritModel::tr("URL"); + static const QString dependsOnHeader = GerritModel::tr("Depends on"); + static const QString neededByHeader = GerritModel::tr("Needed by"); + + if (row < 0 || row >= rowCount()) + return QString(); + const GerritChangePtr c = change(row); + const QString serverPrefix = c->url.left(c->url.lastIndexOf(QLatin1Char('/')) + 1); + QString result; + QTextStream str(&result); + str << "" + << "" + << "" + << "" + << "" + << dependencyHtml(dependsOnHeader, c->dependsOnId, serverPrefix) + << dependencyHtml(neededByHeader, c->neededById, serverPrefix) + << "" + << "" << c->currentPatchSet.patchSetNumber << "" + << c->currentPatchSet.approvalsToHtml() + << "" + << "
" << subjectHeader << "" << c->title << "
" << numberHeader << "url << "\">" << c->number << "
" << ownerHeader << "" << c->owner << ' ' + << "email << "\">" << c->email << "
" << projectHeader << "" << c->project << " (" << c->branch << ")
" << statusHeader << "" << c->status + << ", " << c->lastUpdated.toString(Qt::DefaultLocaleShortDate) << "
" << patchSetHeader << "" << "
" << urlHeader << "url << "\">" << c->url << "
"; + return result; +} + +QStandardItem *GerritModel::itemForId(const QString &id) const { const int numRows = rowCount(); for (int r = 0; r < numRows; ++r) - if (change(r)->number == gerritNumber) - return r; - return -1; + if (change(r)->id == id) + return item(r, 0); + return 0; } void GerritModel::refresh(const QString &query) @@ -515,6 +551,8 @@ static bool parseOutput(const QSharedPointer ¶meters, // The output consists of separate lines containing a document each const QString typeKey = QLatin1String("type"); const QString idKey = QLatin1String("id"); + const QString dependsOnKey = QLatin1String("dependsOn"); + const QString neededByKey = QLatin1String("neededBy"); const QString branchKey = QLatin1String("branch"); const QString numberKey = QLatin1String("number"); const QString ownerKey = QLatin1String("owner"); @@ -601,6 +639,26 @@ static bool parseOutput(const QSharedPointer ¶meters, .arg(QString::fromLocal8Bit(line))); res = false; } + // Read out dependencies + const QJsonValue dependsOnValue = object.value(dependsOnKey); + if (dependsOnValue.isArray()) { + const QJsonArray dependsOnArray = dependsOnValue.toArray(); + if (!dependsOnArray.isEmpty()) { + const QJsonValue first = dependsOnArray.at(0); + if (first.isObject()) + change->dependsOnId = first.toObject()[idKey].toString(); + } + } + // Read out needed by + const QJsonValue neededByValue = object.value(neededByKey); + if (neededByValue.isArray()) { + const QJsonArray neededByArray = neededByValue.toArray(); + if (!neededByArray.isEmpty()) { + const QJsonValue first = neededByArray.at(0); + if (first.isObject()) + change->neededById = first.toObject()[idKey].toString(); + } + } } return res; } @@ -641,6 +699,8 @@ static bool parseOutput(const QSharedPointer ¶meters, const QList lines = output.split('\n'); const QString typeKey = QLatin1String("type"); const QString idKey = QLatin1String("id"); + const QString dependsOnKey = QLatin1String("dependsOn"); + const QString neededByKey = QLatin1String("neededBy"); const QString branchKey = QLatin1String("branch"); const QString numberKey = QLatin1String("number"); const QString ownerKey = QLatin1String("owner"); @@ -746,6 +806,24 @@ static bool parseOutput(const QSharedPointer ¶meters, qWarning("%s: Parse error in line '%s'.", Q_FUNC_INFO, line.constData()); res = false; } + // Read out dependencies + if (Utils::JsonValue *dependsOnValue = object->member(dependsOnKey)) { + if (Utils::JsonArrayValue *dependsOnArray = dependsOnValue->toArray()) { + if (dependsOnArray->size()) { + if (Utils::JsonObjectValue *dependsOnObject = dependsOnArray->elements().first()->toObject()) + change->dependsOnId = jsonStringMember(dependsOnObject, idKey); + } + } + } + // Read out needed by + if (Utils::JsonValue *neededByValue = object->member(neededByKey)) { + if (Utils::JsonArrayValue *neededByArray = neededByValue->toArray()) { + if (neededByArray->size()) { + if (Utils::JsonObjectValue *neededByObject = neededByArray->elements().first()->toObject()) + change->neededById = jsonStringMember(neededByObject, idKey); + } + } + } } if (debug) { qDebug() << __FUNCTION__; @@ -771,7 +849,7 @@ void GerritModel::queryFinished(const QByteArray &output) foreach (const GerritChangePtr &c, changes) { // Avoid duplicate entries for example in the (unlikely) // case people do self-reviews. - if (indexOf(c->number) == -1) { + if (!itemForId(c->id)) { // Determine the verbose user name from the owner of the first query. // It used for marking the changes pending for review in bold. if (m_userName.isEmpty() && !m_query->currentQuery()) diff --git a/src/plugins/git/gerrit/gerritmodel.h b/src/plugins/git/gerrit/gerritmodel.h index 61a526ccf0c..d4bfcc774ba 100644 --- a/src/plugins/git/gerrit/gerritmodel.h +++ b/src/plugins/git/gerrit/gerritmodel.h @@ -73,13 +73,14 @@ public: GerritChange() : number(0) {} bool isValid() const { return number && !url.isEmpty() && !project.isEmpty(); } - QString toHtml() const; QString filterString() const; QStringList gitFetchArguments(const QSharedPointer &p) const; QString url; int number; QString id; + QString dependsOnId; + QString neededById; QString title; QString owner; QString email; @@ -115,8 +116,9 @@ public: ~GerritModel(); GerritChangePtr change(int row) const; + QString toHtml(int row) const; - int indexOf(int gerritNumber) const; + QStandardItem *itemForId(const QString &id) const; public slots: void refresh(const QString &query); @@ -132,6 +134,8 @@ private slots: private: inline bool evaluateQuery(QString *errorMessage); + QString dependencyHtml(const QString &header, const QString &changeId, + const QString &serverPrefix) const; const QSharedPointer m_parameters; QueryContext *m_query;