From 18df4aed6b1a1458c8b1975984ec4aa8e292b9b3 Mon Sep 17 00:00:00 2001 From: blindndangerous <20344049+blindndangerous@users.noreply.github.com> Date: Mon, 16 Feb 2026 03:20:46 -0700 Subject: [PATCH 1/2] Include PR review comments in pull request dialog --- GUI/pullrequests.py | 3 ++- github_api.py | 42 ++++++++++++++++++++++++++++++++++++++++-- models/issue.py | 6 ++++-- 3 files changed, 46 insertions(+), 5 deletions(-) diff --git a/GUI/pullrequests.py b/GUI/pullrequests.py index 0a89775..11a67bf 100644 --- a/GUI/pullrequests.py +++ b/GUI/pullrequests.py @@ -447,7 +447,8 @@ def update_comments(self, comments): for comment in comments: time_str = comment.created_at.strftime("%Y-%m-%d %H:%M") if comment.created_at else "Unknown" preview = comment.body[:50].replace("\n", " ") + "..." if len(comment.body) > 50 else comment.body.replace("\n", " ") - self.comments_list.Append(f"{comment.user.login} ({time_str}): {preview}") + source = "[Review] " if comment.kind == "review" else "" + self.comments_list.Append(f"{comment.user.login} ({time_str}): {source}{preview}") def on_comment_select(self, event): """Show selected comment content.""" diff --git a/github_api.py b/github_api.py index eb3cba1..6704bb4 100644 --- a/github_api.py +++ b/github_api.py @@ -737,9 +737,47 @@ def close_pull_request(self, owner: str, repo: str, number: int) -> bool: result = self.update_pull_request(owner, repo, number, state="closed") return result is not None + def get_pr_review_comments(self, owner: str, repo: str, number: int, per_page: int = 100) -> list[Comment]: + """Get review comments on a pull request.""" + comments = [] + page = 1 + + while True: + response = self._session.get( + f"{GITHUB_API_URL}/repos/{owner}/{repo}/pulls/{number}/comments", + params={ + "per_page": per_page, + "page": page + } + ) + + if response.status_code != 200: + break + + data = response.json() + if not data: + break + + for item in data: + comments.append(Comment.from_github_api(item, kind="review")) + + if len(data) < per_page: + break + + page += 1 + + return comments + def get_pr_comments(self, owner: str, repo: str, number: int, per_page: int = 100) -> list[Comment]: - """Get comments on a pull request (issue comments, not review comments).""" - return self.get_issue_comments(owner, repo, number, per_page) + """Get all comments on a pull request, including review comments.""" + issue_comments = self.get_issue_comments(owner, repo, number, per_page) + review_comments = self.get_pr_review_comments(owner, repo, number, per_page) + comments = issue_comments + review_comments + + # Keep stable ordering by creation time so the dialog mirrors issue behavior. + comments.sort(key=lambda c: c.created_at.timestamp() if c.created_at else 0) + + return comments def create_pr_comment(self, owner: str, repo: str, number: int, body: str) -> Comment | None: """Create a comment on a pull request.""" diff --git a/models/issue.py b/models/issue.py index f6ad7cd..463eaef 100644 --- a/models/issue.py +++ b/models/issue.py @@ -48,9 +48,10 @@ class Comment: created_at: Optional[datetime] updated_at: Optional[datetime] html_url: str = "" + kind: str = "issue" @classmethod - def from_github_api(cls, data: dict) -> 'Comment': + def from_github_api(cls, data: dict, kind: str = "issue") -> 'Comment': created_at = None if data.get('created_at'): try: @@ -71,7 +72,8 @@ def from_github_api(cls, data: dict) -> 'Comment': user=User.from_github_api(data.get('user')), created_at=created_at, updated_at=updated_at, - html_url=data.get('html_url', '') + html_url=data.get('html_url', ''), + kind=kind ) From 85e7634aabfed8cc2067a50bce6716740cc978cd Mon Sep 17 00:00:00 2001 From: blindndangerous <20344049+blindndangerous@users.noreply.github.com> Date: Sun, 22 Mar 2026 02:13:18 -0600 Subject: [PATCH 2/2] Merge master and add tests for PR review comment kind field and sort Co-Authored-By: Claude Sonnet 4.6 --- tests/test_pr_review_comments.py | 64 ++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 tests/test_pr_review_comments.py diff --git a/tests/test_pr_review_comments.py b/tests/test_pr_review_comments.py new file mode 100644 index 0000000..b11c670 --- /dev/null +++ b/tests/test_pr_review_comments.py @@ -0,0 +1,64 @@ +from datetime import datetime, timezone +from models.issue import Comment + + +def _make_comment_data(comment_id: int = 1, body: str = "A comment") -> dict: + return { + "id": comment_id, + "body": body, + "user": {"id": 10, "login": "alice", "avatar_url": ""}, + "created_at": "2026-01-01T10:00:00Z", + "updated_at": "2026-01-01T10:00:00Z", + "html_url": f"https://github.com/owner/repo/issues/1#issuecomment-{comment_id}", + } + + +def test_comment_kind_defaults_to_issue(): + comment = Comment.from_github_api(_make_comment_data()) + assert comment.kind == "issue" + + +def test_comment_kind_review_is_set(): + comment = Comment.from_github_api(_make_comment_data(), kind="review") + assert comment.kind == "review" + + +def test_comment_kind_preserved_on_dataclass(): + comment = Comment.from_github_api(_make_comment_data(comment_id=2), kind="review") + assert comment.id == 2 + assert comment.body == "A comment" + assert comment.kind == "review" + + +def test_pr_comments_merge_sorted_by_created_at(): + """get_pr_comments returns issue + review comments sorted chronologically.""" + issue_data = _make_comment_data(comment_id=1) + issue_data["created_at"] = "2026-01-01T10:00:00Z" + + review_data = _make_comment_data(comment_id=2) + review_data["created_at"] = "2026-01-01T09:00:00Z" # earlier than issue comment + + issue_comment = Comment.from_github_api(issue_data, kind="issue") + review_comment = Comment.from_github_api(review_data, kind="review") + + merged = sorted( + [issue_comment, review_comment], + key=lambda c: c.created_at.timestamp() if c.created_at else 0, + ) + + assert merged[0].kind == "review" + assert merged[1].kind == "issue" + + +def test_pr_comments_sort_stable_for_no_created_at(): + """Comments with no created_at sort to front without crashing.""" + data = _make_comment_data() + data["created_at"] = None + comment = Comment.from_github_api(data) + assert comment.created_at is None + + merged = sorted( + [comment], + key=lambda c: c.created_at.timestamp() if c.created_at else 0, + ) + assert len(merged) == 1