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 <orgads@gmail.com>
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@digia.com>
This commit is contained in:
Friedemann Kleint
2014-06-16 13:50:06 +02:00
committed by Orgad Shaneh
parent 3f25ed3bbf
commit ded93b43d2
3 changed files with 112 additions and 30 deletions

View File

@@ -300,7 +300,7 @@ void GerritDialog::slotCurrentChanged()
const bool valid = current.isValid(); const bool valid = current.isValid();
if (valid) { if (valid) {
const int row = m_filterModel->mapToSource(current).row(); const int row = m_filterModel->mapToSource(current).row();
m_detailsBrowser->setText(m_model->change(row)->toHtml()); m_detailsBrowser->setText(m_model->toHtml(row));
} else { } else {
m_detailsBrowser->setText(QString()); m_detailsBrowser->setText(QString());
} }

View File

@@ -187,26 +187,6 @@ int GerritPatchSet::approvalLevel() const
return value; return value;
} }
QString GerritChange::toHtml() const
{
// Keep in sync with list model headers.
static const QString format = GerritModel::tr(
"<html><head/><body><table>"
"<tr><td>Subject</td><td>%1</td></tr>"
"<tr><td>Number</td><td><a href=\"%11\">%2</a></td></tr>"
"<tr><td>Owner</td><td>%3 <a href=\"mailto:%4\">%4</a></td></tr>"
"<tr><td>Project</td><td>%5 (%6)</td></tr>"
"<tr><td>Status</td><td>%7, %8</td></tr>"
"<tr><td>Patch set</td><td>%9</td></tr>"
"%10"
"<tr><td>URL</td><td><a href=\"%11\">%11</a></td></tr>"
"</table></body></html>");
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 QString GerritChange::filterString() const
{ {
const QChar blank = QLatin1Char(' '); const QChar blank = QLatin1Char(' ');
@@ -300,7 +280,8 @@ QueryContext::QueryContext(const QStringList &queries,
m_progress.setProgressRange(0, m_queries.size()); m_progress.setProgressRange(0, m_queries.size());
// Determine binary and common command line arguments. // 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"); << QLatin1String("--format=JSON");
m_binary = m_baseArguments.front(); m_binary = m_baseArguments.front();
m_baseArguments.pop_front(); m_baseArguments.pop_front();
@@ -431,20 +412,75 @@ GerritModel::~GerritModel()
{ {
} }
static inline GerritChangePtr changeFromItem(const QStandardItem *item)
{
return qvariant_cast<GerritChangePtr>(item->data(GerritModel::GerritChangeRole));
}
GerritChangePtr GerritModel::change(int row) const GerritChangePtr GerritModel::change(int row) const
{ {
if (row >= 0 && row < rowCount()) if (row >= 0 && row < rowCount())
return qvariant_cast<GerritChangePtr>(item(row, 0)->data(GerritChangeRole)); return changeFromItem(item(row, 0));
return GerritChangePtr(new GerritChange); 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 << "<tr><td>" << header << "</td><td><a href="
<< serverPrefix << "r/" << changeId << '>' << changeId << "</a>";
if (const QStandardItem *item = itemForId(changeId))
str << " (" << changeFromItem(item)->title << ')';
str << "</td></tr>";
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 << "<html><head/><body><table>"
<< "<tr><td>" << subjectHeader << "</td><td>" << c->title << "</td></tr>"
<< "<tr><td>" << numberHeader << "</td><td><a href=\"" << c->url << "\">" << c->number << "</a></td></tr>"
<< "<tr><td>" << ownerHeader << "</td><td>" << c->owner << ' '
<< "<a href=\"mailto:" << c->email << "\">" << c->email << "</a></td></tr>"
<< "<tr><td>" << projectHeader << "</td><td>" << c->project << " (" << c->branch << ")</td></tr>"
<< dependencyHtml(dependsOnHeader, c->dependsOnId, serverPrefix)
<< dependencyHtml(neededByHeader, c->neededById, serverPrefix)
<< "<tr><td>" << statusHeader << "</td><td>" << c->status
<< ", " << c->lastUpdated.toString(Qt::DefaultLocaleShortDate) << "</td></tr>"
<< "<tr><td>" << patchSetHeader << "</td><td>" << "</td></tr>" << c->currentPatchSet.patchSetNumber << "</td></tr>"
<< c->currentPatchSet.approvalsToHtml()
<< "<tr><td>" << urlHeader << "</td><td><a href=\"" << c->url << "\">" << c->url << "</a></td></tr>"
<< "</table></body></html>";
return result;
}
QStandardItem *GerritModel::itemForId(const QString &id) const
{ {
const int numRows = rowCount(); const int numRows = rowCount();
for (int r = 0; r < numRows; ++r) for (int r = 0; r < numRows; ++r)
if (change(r)->number == gerritNumber) if (change(r)->id == id)
return r; return item(r, 0);
return -1; return 0;
} }
void GerritModel::refresh(const QString &query) void GerritModel::refresh(const QString &query)
@@ -515,6 +551,8 @@ static bool parseOutput(const QSharedPointer<GerritParameters> &parameters,
// The output consists of separate lines containing a document each // The output consists of separate lines containing a document each
const QString typeKey = QLatin1String("type"); const QString typeKey = QLatin1String("type");
const QString idKey = QLatin1String("id"); const QString idKey = QLatin1String("id");
const QString dependsOnKey = QLatin1String("dependsOn");
const QString neededByKey = QLatin1String("neededBy");
const QString branchKey = QLatin1String("branch"); const QString branchKey = QLatin1String("branch");
const QString numberKey = QLatin1String("number"); const QString numberKey = QLatin1String("number");
const QString ownerKey = QLatin1String("owner"); const QString ownerKey = QLatin1String("owner");
@@ -601,6 +639,26 @@ static bool parseOutput(const QSharedPointer<GerritParameters> &parameters,
.arg(QString::fromLocal8Bit(line))); .arg(QString::fromLocal8Bit(line)));
res = false; 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; return res;
} }
@@ -641,6 +699,8 @@ static bool parseOutput(const QSharedPointer<GerritParameters> &parameters,
const QList<QByteArray> lines = output.split('\n'); const QList<QByteArray> lines = output.split('\n');
const QString typeKey = QLatin1String("type"); const QString typeKey = QLatin1String("type");
const QString idKey = QLatin1String("id"); const QString idKey = QLatin1String("id");
const QString dependsOnKey = QLatin1String("dependsOn");
const QString neededByKey = QLatin1String("neededBy");
const QString branchKey = QLatin1String("branch"); const QString branchKey = QLatin1String("branch");
const QString numberKey = QLatin1String("number"); const QString numberKey = QLatin1String("number");
const QString ownerKey = QLatin1String("owner"); const QString ownerKey = QLatin1String("owner");
@@ -746,6 +806,24 @@ static bool parseOutput(const QSharedPointer<GerritParameters> &parameters,
qWarning("%s: Parse error in line '%s'.", Q_FUNC_INFO, line.constData()); qWarning("%s: Parse error in line '%s'.", Q_FUNC_INFO, line.constData());
res = false; 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) { if (debug) {
qDebug() << __FUNCTION__; qDebug() << __FUNCTION__;
@@ -771,7 +849,7 @@ void GerritModel::queryFinished(const QByteArray &output)
foreach (const GerritChangePtr &c, changes) { foreach (const GerritChangePtr &c, changes) {
// Avoid duplicate entries for example in the (unlikely) // Avoid duplicate entries for example in the (unlikely)
// case people do self-reviews. // 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. // Determine the verbose user name from the owner of the first query.
// It used for marking the changes pending for review in bold. // It used for marking the changes pending for review in bold.
if (m_userName.isEmpty() && !m_query->currentQuery()) if (m_userName.isEmpty() && !m_query->currentQuery())

View File

@@ -73,13 +73,14 @@ public:
GerritChange() : number(0) {} GerritChange() : number(0) {}
bool isValid() const { return number && !url.isEmpty() && !project.isEmpty(); } bool isValid() const { return number && !url.isEmpty() && !project.isEmpty(); }
QString toHtml() const;
QString filterString() const; QString filterString() const;
QStringList gitFetchArguments(const QSharedPointer<GerritParameters> &p) const; QStringList gitFetchArguments(const QSharedPointer<GerritParameters> &p) const;
QString url; QString url;
int number; int number;
QString id; QString id;
QString dependsOnId;
QString neededById;
QString title; QString title;
QString owner; QString owner;
QString email; QString email;
@@ -115,8 +116,9 @@ public:
~GerritModel(); ~GerritModel();
GerritChangePtr change(int row) const; GerritChangePtr change(int row) const;
QString toHtml(int row) const;
int indexOf(int gerritNumber) const; QStandardItem *itemForId(const QString &id) const;
public slots: public slots:
void refresh(const QString &query); void refresh(const QString &query);
@@ -132,6 +134,8 @@ private slots:
private: private:
inline bool evaluateQuery(QString *errorMessage); inline bool evaluateQuery(QString *errorMessage);
QString dependencyHtml(const QString &header, const QString &changeId,
const QString &serverPrefix) const;
const QSharedPointer<GerritParameters> m_parameters; const QSharedPointer<GerritParameters> m_parameters;
QueryContext *m_query; QueryContext *m_query;