From 3df75b674051c31b5b6799662e3b151e0ae1716f Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Fri, 19 Jan 2024 15:06:34 +0100 Subject: [PATCH] Axivion: Fetch issues Change-Id: Ib33c507c8036fe84291ac3ff3fb08eed46afb07c Reviewed-by: Jarek Kobus --- src/plugins/axivion/axivionoutputpane.cpp | 93 ++++++++++++++++++++++- src/plugins/axivion/axivionoutputpane.h | 7 ++ src/plugins/axivion/axivionplugin.cpp | 63 +++++++++++++++ src/plugins/axivion/axivionplugin.h | 16 ++++ 4 files changed, 176 insertions(+), 3 deletions(-) diff --git a/src/plugins/axivion/axivionoutputpane.cpp b/src/plugins/axivion/axivionoutputpane.cpp index 32d0509a1b7..41a445a4074 100644 --- a/src/plugins/axivion/axivionoutputpane.cpp +++ b/src/plugins/axivion/axivionoutputpane.cpp @@ -8,6 +8,8 @@ #include "dashboard/dto.h" #include +#include +#include #include #include @@ -176,6 +178,8 @@ class IssuesWidget : public QScrollArea public: explicit IssuesWidget(QWidget *parent = nullptr); void updateUi(); + void setTableDto(const Dto::TableInfoDto &dto); + void addIssues(const Dto::IssueTableDto &dto); private: void updateTableView(); @@ -190,6 +194,8 @@ private: QComboBox *m_versionStart = nullptr; QComboBox *m_versionEnd = nullptr; QLineEdit *m_pathGlobFilter = nullptr; // FancyLineEdit instead? + Utils::BaseTreeView *m_issuesView = nullptr; + Utils::TreeModel<> *m_issuesModel = nullptr; }; IssuesWidget::IssuesWidget(QWidget *parent) @@ -231,7 +237,13 @@ IssuesWidget::IssuesWidget(QWidget *parent) m_pathGlobFilter->setPlaceholderText(Tr::tr("Path globbing")); m_filtersLayout->addWidget(m_pathGlobFilter); layout->addLayout(m_filtersLayout); - // TODO the issues table + m_issuesView = new Utils::BaseTreeView(this); + m_issuesView->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); + m_issuesView->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + m_issuesView->enableColumnHiding(); + m_issuesModel = new Utils::TreeModel; + m_issuesView->setModel(m_issuesModel); + layout->addWidget(m_issuesView); layout->addStretch(1); setWidget(widget); setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); @@ -273,12 +285,75 @@ void IssuesWidget::updateUi() // TODO fill owners m_filtersLayout->setEnabled(true); + if (info.issueKinds.size()) + m_currentPrefix = info.issueKinds.front().prefix; + updateTableView(); +} + + +void IssuesWidget::setTableDto(const Dto::TableInfoDto &dto) +{ + m_currentTableInfo.emplace(dto); + + // update issues table layout - for now just simple approach + Utils::TreeModel<> *issuesModel = new Utils::TreeModel; + QStringList columnHeaders; + QStringList hiddenColumns; + for (const Dto::ColumnInfoDto &column : dto.columns) { + columnHeaders << column.key; + if (!column.showByDefault) + hiddenColumns << column.key; + } + + issuesModel->setHeader(columnHeaders); + + auto oldModel = m_issuesModel; + m_issuesModel = issuesModel; + m_issuesView->setModel(issuesModel); + delete oldModel; + int counter = 0; + for (const QString &header : std::as_const(columnHeaders)) + m_issuesView->setColumnHidden(counter++, hiddenColumns.contains(header)); + + // first time lookup... should we cache and maybe represent old data? + IssueListSearch search; + search.kind = m_currentPrefix; + fetchIssues(search); +} + +void IssuesWidget::addIssues(const Dto::IssueTableDto &dto) +{ + QTC_ASSERT(m_currentTableInfo.has_value(), return); + // handle added/removed/total ? + + const std::vector tableColumns = m_currentTableInfo->columns; + // const std::vector columns = dto.columns.value(); + const std::vector> rows = dto.rows; + for (auto row : rows) { + QStringList data; + for (auto column : tableColumns) { + auto it = row.find(column.key); + if (it != row.end()) { + if (it->second.isString()) + data << it->second.getString(); + else if (it->second.isDouble()) + data << QString::number(it->second.getDouble()); + else if (it->second.isBool()) + data << QString("%1").arg(it->second.getBool()); + else + data << "not yet"; + } + } + Utils::StaticTreeItem *it = new Utils::StaticTreeItem(data); + m_issuesModel->rootItem()->appendChild(it); + } } void IssuesWidget::updateTableView() { - // fetch table dto and apply - // on done fetch first data for the selected issues + QTC_ASSERT(!m_currentPrefix.isEmpty(), return); + // fetch table dto and apply, on done fetch first data for the selected issues + fetchIssueTableLayout(m_currentPrefix); } AxivionOutputPane::AxivionOutputPane(QObject *parent) @@ -387,6 +462,18 @@ void AxivionOutputPane::updateDashboard() } } +void AxivionOutputPane::setTableDto(const Dto::TableInfoDto &dto) +{ + if (auto issues = static_cast(m_outputWidget->widget(1))) + issues->setTableDto(dto); +} + +void AxivionOutputPane::addIssues(const Dto::IssueTableDto &dto) +{ + if (auto issues = static_cast(m_outputWidget->widget(1))) + issues->addIssues(dto); +} + void AxivionOutputPane::updateAndShowRule(const QString &ruleHtml) { if (auto browser = static_cast(m_outputWidget->widget(2))) { diff --git a/src/plugins/axivion/axivionoutputpane.h b/src/plugins/axivion/axivionoutputpane.h index 140d50b7235..93e7d8f62c5 100644 --- a/src/plugins/axivion/axivionoutputpane.h +++ b/src/plugins/axivion/axivionoutputpane.h @@ -11,6 +11,11 @@ QT_END_NAMESPACE namespace Axivion::Internal { +namespace Dto { +class IssueTableDto; +class TableInfoDto; +} + class AxivionOutputPane : public Core::IOutputPane { Q_OBJECT @@ -33,6 +38,8 @@ public: void updateDashboard(); void updateAndShowRule(const QString &ruleHtml); + void setTableDto(const Dto::TableInfoDto &dto); + void addIssues(const Dto::IssueTableDto &dto); private: QStackedWidget *m_outputWidget = nullptr; }; diff --git a/src/plugins/axivion/axivionplugin.cpp b/src/plugins/axivion/axivionplugin.cpp index 3b0feac5bdc..505536c8bbb 100644 --- a/src/plugins/axivion/axivionplugin.cpp +++ b/src/plugins/axivion/axivionplugin.cpp @@ -69,6 +69,16 @@ QIcon iconForIssue(const QString &prefix) return it.value(); } +QString IssueListSearch::toQuery() const +{ + if (kind.isEmpty()) + return {}; + QString result; + result.append(QString("?kind=%1&offset=%2&limit=%3").arg(kind).arg(offset).arg(limit)); + // TODO other params + return result; +} + class AxivionPluginPrivate : public QObject { public: @@ -76,6 +86,8 @@ public: void handleSslErrors(QNetworkReply *reply, const QList &errors); void onStartupProjectChanged(); void fetchProjectInfo(const QString &projectName); + void fetchIssueTableLayout(const QString &prefix); + void fetchIssues(const IssueListSearch &search); void handleOpenedDocs(ProjectExplorer::Project *project); void onDocumentOpened(Core::IDocument *doc); void onDocumentClosed(Core::IDocument * doc); @@ -127,6 +139,18 @@ void fetchProjectInfo(const QString &projectName) dd->fetchProjectInfo(projectName); } +void fetchIssueTableLayout(const QString &prefix) +{ + QTC_ASSERT(dd, return); + dd->fetchIssueTableLayout(prefix); +} + +void fetchIssues(const IssueListSearch &search) +{ + QTC_ASSERT(dd, return); + dd->fetchIssues(search); +} + std::optional projectInfo() { QTC_ASSERT(dd, return {}); @@ -332,6 +356,45 @@ void AxivionPluginPrivate::fetchProjectInfo(const QString &projectName) m_taskTreeRunner.start(fetchDataRecipe(url, handler)); } +void AxivionPluginPrivate::fetchIssueTableLayout(const QString &prefix) +{ + QTC_ASSERT(m_currentProjectInfo.has_value(), return); + if (m_taskTreeRunner.isRunning()) { + QTimer::singleShot(3000, this, [this, prefix] { fetchIssueTableLayout(prefix); }); + return; + } + const QUrl url = urlForProject(m_currentProjectInfo.value().name + '/') + .resolved(QString("issues_meta?kind=" + prefix)); + + const auto handler = [this](const Dto::TableInfoDto &data) { + m_axivionOutputPane.setTableDto(data); + }; + + m_taskTreeRunner.start(fetchDataRecipe(url, handler)); +} + +void AxivionPluginPrivate::fetchIssues(const IssueListSearch &search) +{ + QTC_ASSERT(m_currentProjectInfo.has_value(), return); + if (m_taskTreeRunner.isRunning()) { + QTimer::singleShot(3000, this, [this, search] { fetchIssues(search); }); + return; + } + + const QString query = search.toQuery(); + if (query.isEmpty()) + return; + + const QUrl url = urlForProject(m_currentProjectInfo.value().name + '/') + .resolved(QString("issues" + query)); + + const auto handler = [this](const Dto::IssueTableDto &data) { + m_axivionOutputPane.addIssues(data); + }; + + m_taskTreeRunner.start(fetchDataRecipe(url, handler)); +} + void AxivionPluginPrivate::fetchRuleInfo(const QString &id) { if (m_runningQuery) { diff --git a/src/plugins/axivion/axivionplugin.h b/src/plugins/axivion/axivionplugin.h index b7a4819a081..29b3c307323 100644 --- a/src/plugins/axivion/axivionplugin.h +++ b/src/plugins/axivion/axivionplugin.h @@ -15,8 +15,24 @@ namespace ProjectExplorer { class Project; } namespace Axivion::Internal { +struct IssueListSearch +{ + QString kind; + QString state; + QString versionStart; + QString versionEnd; + QString owner; + QString pathglob; + int offset = 0; + int limit = 30; + + QString toQuery() const; +}; + void fetchProjectInfo(const QString &projectName); std::optional projectInfo(); +void fetchIssueTableLayout(const QString &prefix); +void fetchIssues(const IssueListSearch &search); bool handleCertificateIssue(); QIcon iconForIssue(const QString &prefix);