From 39349a7e1e4fbb16e4a71d7f741b0725e73caeb7 Mon Sep 17 00:00:00 2001 From: Matias Linares Date: Sat, 5 Oct 2024 10:43:00 -0300 Subject: Add review changes functionality! --- kodereviewer/data.py | 8 +++++ kodereviewer/network_manager.py | 42 ++++++++++++++++++++-- kodereviewer/qml/PullRequestPage.qml | 43 +++++++++++----------- kodereviewer/qml/ReviewDialog.qml | 69 ++++++++++++++++++++++++++++++++++++ 4 files changed, 136 insertions(+), 26 deletions(-) create mode 100644 kodereviewer/qml/ReviewDialog.qml (limited to 'kodereviewer') diff --git a/kodereviewer/data.py b/kodereviewer/data.py index 0d1bef6..8c4a0e6 100644 --- a/kodereviewer/data.py +++ b/kodereviewer/data.py @@ -108,6 +108,8 @@ class PullRequest(QObject): _assignee: User | None _labels: list[Label] + _last_commit: str + _comments: list[Comment] _files: list[ChangedFile] @@ -134,6 +136,8 @@ class PullRequest(QObject): self._labels = [Label(label) for label in data['labels']] + self._last_commit = data['head']['sha'] + # At first this is empty until it's updated with a request self._comments = [] self._files = [] @@ -182,6 +186,10 @@ class PullRequest(QObject): def username(self) -> str: return self._user.username + @Property(str, constant=True) + def last_commit(self) -> str: + return self._last_commit + @Property(list, constant=True) def labels(self) -> list[Label]: return self._labels diff --git a/kodereviewer/network_manager.py b/kodereviewer/network_manager.py index ebc2797..3296153 100644 --- a/kodereviewer/network_manager.py +++ b/kodereviewer/network_manager.py @@ -1,4 +1,7 @@ +import json +import logging import re +from enum import StrEnum from typing import Optional from PySide6.QtCore import QObject, QSettings, Signal, Slot, Property, qDebug @@ -14,6 +17,15 @@ QML_IMPORT_MAJOR_VERSION = 1 PULL_REQUEST_LIST_URL = re.compile(r'/pulls$') COMMENT_LIST_URL = re.compile(r'/issues/(\d+)/comments') FILE_LIST_URL = re.compile(r'/pulls/(\d+)/files') +CREATE_REVIEW_URL = re.compile(r'/pulls/(\d+)/reviews') + +logger = logging.getLogger(__name__) + + +class ReviewEvent(StrEnum): + APPROVE = 'APPROVE' + REQUEST_CHANGES = 'REQUEST_CHANGES' + COMMENT = 'COMMENT' @QmlElement @@ -76,8 +88,10 @@ class NetworkManager(QObject): pull_request: Optional[PullRequest] = self._project.find_pull_request(pull_request_number) if pull_request is not None: pull_request.load_files(response_body) + elif (match := CREATE_REVIEW_URL.search(reply.url().toString())): + logger.info(f'Got review reply: {response_body}') else: - print(f"Can't handle {reply.url()}") + logger.info(f"Can't handle {reply.url()}") @Slot() def getPullRequests(self) -> None: @@ -85,8 +99,30 @@ class NetworkManager(QObject): @Slot(int) def getPullRequestComments(self, number: int) -> None: - self._manager.get(self._request_factory.createRequest(f'/issues/{number}/comments')) + self._manager.get( + self._request_factory.createRequest(f'/issues/{number}/comments') + ) @Slot(int) def getFiles(self, pull_request_number) -> None: - self._manager.get(self._request_factory.createRequest(f'/pulls/{pull_request_number}/files')) + self._manager.get( + self._request_factory.createRequest( + f'/pulls/{pull_request_number}/files' + ) + ) + + @Slot(int, str, str, str) + def createReview( + self, pull_request_number: str, commit_id: str, + body: str, event: ReviewEvent + ) -> None: + request = self._request_factory.createRequest( + f'/pulls/{pull_request_number}/reviews' + ) + data = { + 'commit_id': commit_id, + 'body': body, + 'event': event, + 'comments': [] + } + self._manager.post(request, json.dumps(data).encode()) diff --git a/kodereviewer/qml/PullRequestPage.qml b/kodereviewer/qml/PullRequestPage.qml index da2b8e5..b9106c1 100644 --- a/kodereviewer/qml/PullRequestPage.qml +++ b/kodereviewer/qml/PullRequestPage.qml @@ -30,32 +30,18 @@ Kirigami.ScrollablePage { } ] - Kirigami.Dialog { + ReviewDialog { id: reviewChangesDialog - standardButtons: Kirigami.Dialog.Ok | Kirigami.Dialog.Cancel - title: i18nc("@title:window", "Review changes") - padding: Kirigami.Units.largeSpacing - preferredWidth: Kirigami.Units.gridUnit * 20 - ColumnLayout { - MarkdownTextArea { - Layout.fillWidth: true - } - Kirigami.Separator { - Kirigami.FormData.isSection: true - Layout.fillWidth: true - } - QQC2.RadioButton { - text: "Approve" - } - QQC2.RadioButton { - text: "Comment" - } - QQC2.RadioButton { - text: "Request changes" - } + onAccepted: { + print('Sending', root.pullRequest.number, root.pullRequest.last_commit, + reviewBodyText, event) + root.connection.createReview( + root.pullRequest.number, root.pullRequest.last_commit, + reviewBodyText, event + ) + clearForm() } } - Loader { id: commentModelLoader active: !!root.pullRequest @@ -136,6 +122,17 @@ Kirigami.ScrollablePage { } } + RowLayout { + QQC2.Label { + text: "Last commit: " + elide: Text.ElideRight + } + QQC2.Label { + text: root.pullRequest ? root.pullRequest.last_commit : "" + elide: Text.ElideLeft + } + } + Loader { id: labelModelLoader active: !!root.pullRequest diff --git a/kodereviewer/qml/ReviewDialog.qml b/kodereviewer/qml/ReviewDialog.qml new file mode 100644 index 0000000..7dcaf9c --- /dev/null +++ b/kodereviewer/qml/ReviewDialog.qml @@ -0,0 +1,69 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtCore +import QtQuick.Controls as QQC2 +import QtQuick.Layouts + +import org.kde.kirigami as Kirigami +import org.kde.kirigamiaddons.formcard as FormCard + +import org.deprecated.kodereviewer 1.0 + + +Kirigami.Dialog { + id: root + standardButtons: Kirigami.Dialog.Ok | Kirigami.Dialog.Cancel + title: i18nc("@title:window", "Review changes") + padding: Kirigami.Units.largeSpacing + preferredWidth: Kirigami.Units.gridUnit * 20 + property string event: '' + property alias reviewBodyText: reviewBody.text + ColumnLayout { + MarkdownTextArea { + id: reviewBody + Layout.fillWidth: true + } + Kirigami.Separator { + Kirigami.FormData.isSection: true + Layout.fillWidth: true + } + QQC2.RadioButton { + id: approveRadioButton + text: "Approve" + onCheckedChanged: if(checked) { + root.event = 'APPROVE' + } + } + QQC2.RadioButton { + id: commentRadioButton + text: "Comment" + onCheckedChanged: if(checked) { + root.event = 'COMMENT' + } + } + QQC2.RadioButton { + id: requestChangesRadioButton + text: "Request changes" + onCheckedChanged: if(checked) { + root.event = 'REQUEST_CHANGES' + } + } + } + + function requiredFieldsFilled() { + return event != '' && reviewBodyText != '' + } + + function clearForm() { + reviewBodyText = '' + approveRadioButton.checked = false + commentRadioButton.checked = false + requestChangesRadioButton = false + event = '' + } + + Component.onCompleted: { + const button = standardButton(Kirigami.Dialog.Ok); + button.enabled = Qt.binding( () => requiredFieldsFilled() ); + } +} -- cgit v1.2.3-70-g09d2