Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion discussions.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,17 @@ def get_discussions(token: str, search_query: str, ghe: str):
title
url
createdAt
comments(first: 1) {
author {
login
__typename
}
comments(first: 100) {
nodes {
createdAt
author {
login
__typename
}
}
}
answerChosenAt
Expand Down
49 changes: 29 additions & 20 deletions most_active_mentors.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,27 +112,36 @@ def count_comments_per_user(
else:
mentor_count[review_comment.user.login] = 1

# The discussion branch below is dead in production (tracked in #774):
# the GraphQL query in discussions.get_discussions fetches no comment
# author data, attribute access on dict nodes would AttributeError,
# and the ignore_comment call below passes comment.user as both
# issue_user and comment_user (self-reference always True).
if discussion and len(discussion["comments"]["nodes"]) > 0: # pragma: no cover
for comment in discussion["comments"]["nodes"]:
if ignore_comment(
comment.user,
comment.user,
ignore_users,
comment.submitted_at,
comment.ready_for_review_at,
):
continue
# The discussion branch: use dict access because GraphQL returns plain
# dicts (not github3 objects). Thread the discussion author as
# issue_user so we can filter out self-comments correctly.
if discussion and len(discussion["comments"]["nodes"]) > 0:
discussion_author_login = (
discussion.get("author") or {}
).get("login", "")
for comment in discussion["comments"]["nodes"]:
comment_author = comment.get("author") or {}
comment_login = comment_author.get("login", "")
comment_type = comment_author.get("__typename", "")
comment_created_at = comment.get("createdAt")

if (
not comment_login
# ignore bots
or comment_type == "Bot"
# ignore comments by the discussion author
or comment_login == discussion_author_login
# ignore users in the ignore list
or comment_login in ignore_users
# ignore comments without a timestamp
or not comment_created_at
):
continue

# increase the number of comments left by current user by 1
if comment.user.login in mentor_count:
mentor_count[comment.user.login] += 1
else:
mentor_count[comment.user.login] = 1
if comment_login in mentor_count:
mentor_count[comment_login] += 1
else:
mentor_count[comment_login] = 1

return mentor_count

Expand Down
123 changes: 123 additions & 0 deletions test_most_active_mentors.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,3 +157,126 @@ def test_count_comments_per_user_with_pr_reviews(self):

result = count_comments_per_user(mock_issue, pull_request=mock_pr)
self.assertEqual(result, {"reviewer": 2})


class TestCountCommentsDiscussions(unittest.TestCase):
"""Covers the discussion branch of count_comments_per_user.

Before the fix for #774 this branch was dead code because:
1. The GraphQL query fetched no comment author data.
2. Attribute access on dict nodes would raise AttributeError.
3. The ignore_comment call passed comment.user as both issue_user
and comment_user, so every comment was silently skipped.
"""

def _make_discussion(self, author_login, comments):
"""Return a minimal discussion dict as returned by get_discussions."""
return {
"title": "Test discussion",
"url": "https://github.com/org/repo/discussions/1",
"createdAt": "2024-01-01T00:00:00Z",
"author": {"login": author_login, "__typename": "User"},
"comments": {"nodes": comments},
"answerChosenAt": None,
"closedAt": None,
}

def test_counts_commenter(self):
"""A comment by a user other than the author is counted."""
discussion = self._make_discussion(
"op",
[
{
"createdAt": "2024-01-02T00:00:00Z",
"author": {"login": "mentor", "__typename": "User"},
},
],
)
result = count_comments_per_user(None, discussion=discussion)
self.assertEqual(result, {"mentor": 1})

def test_ignores_discussion_author_self_comment(self):
"""Comments by the discussion author are not counted."""
discussion = self._make_discussion(
"op",
[
{
"createdAt": "2024-01-02T00:00:00Z",
"author": {"login": "op", "__typename": "User"},
},
],
)
result = count_comments_per_user(None, discussion=discussion)
self.assertEqual(result, {})

def test_ignores_bot_commenter(self):
"""Comments from Bot actors are not counted."""
discussion = self._make_discussion(
"op",
[
{
"createdAt": "2024-01-02T00:00:00Z",
"author": {"login": "github-actions", "__typename": "Bot"},
},
],
)
result = count_comments_per_user(None, discussion=discussion)
self.assertEqual(result, {})

def test_ignores_user_in_ignore_list(self):
"""Comments by explicitly ignored users are not counted."""
discussion = self._make_discussion(
"op",
[
{
"createdAt": "2024-01-02T00:00:00Z",
"author": {"login": "spammer", "__typename": "User"},
},
],
)
result = count_comments_per_user(
None, discussion=discussion, ignore_users=["spammer"]
)
self.assertEqual(result, {})

def test_multiple_commenters(self):
"""Multiple distinct commenters each get their own count."""
discussion = self._make_discussion(
"op",
[
{
"createdAt": "2024-01-02T00:00:00Z",
"author": {"login": "alice", "__typename": "User"},
},
{
"createdAt": "2024-01-03T00:00:00Z",
"author": {"login": "bob", "__typename": "User"},
},
{
"createdAt": "2024-01-04T00:00:00Z",
"author": {"login": "alice", "__typename": "User"},
},
],
)
result = count_comments_per_user(None, discussion=discussion)
self.assertEqual(result, {"alice": 2, "bob": 1})

def test_no_comments(self):
"""A discussion with an empty comments list returns an empty dict."""
discussion = self._make_discussion("op", [])
result = count_comments_per_user(None, discussion=discussion)
self.assertEqual(result, {})

def test_null_author_skipped(self):
"""A comment with a null author (deleted account) is skipped gracefully."""
discussion = self._make_discussion(
"op",
[
{
"createdAt": "2024-01-02T00:00:00Z",
"author": None,
},
],
)
result = count_comments_per_user(None, discussion=discussion)
self.assertEqual(result, {})