Skip to content

Commit

Permalink
merging PR Chaosthebot#463: Count contributors' votes, regardless of …
Browse files Browse the repository at this point in the history
…account age

Chaosthebot#463: Count contributors' votes, regardless of account age

Description:
Reminder for meritocracy review.

:ok_woman: PR passed with a vote of 13 for and 1 against, a weighted total of 11.5 and a threshold of 6.5, and a current meritocracy review.

Vote record:
@Ayyko: 1
@Leigende: 1
@PlasmaPower: 1
@Smittyvb: 1
@adeelx: 1
@andrewda: 1
@e-beach: 1
@ike709: 1
@md678685: 1
@nstanard: -1
@phil-r: 1
@qgustavor: 1
@rhengles: 1
@rudehn: 1
  • Loading branch information
PlasmaPower authored and chaosbot committed Jun 2, 2017
1 parent bccb9a3 commit 873c338
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 22 deletions.
6 changes: 4 additions & 2 deletions cron/poll_pull_requests.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,10 @@ def poll_pull_requests(api):

top_contributors = sorted(gh.repos.get_contributors(api, settings.URN),
key=lambda user: user["total"], reverse=True)
top_contributors = [item["author"]["login"].lower() for item in top_contributors]
contributors = set(top_contributors) # store it while it's still a complete list
top_contributors = top_contributors[:settings.MERITOCRACY_TOP_CONTRIBUTORS]
top_contributors = set([item["author"]["login"].lower() for item in top_contributors])
top_contributors = set(top_contributors)
top_voters = sorted(total_votes, key=total_votes.get, reverse=True)
top_voters = set([user.lower() for user in top_voters[:settings.MERITOCRACY_TOP_VOTERS]])
meritocracy = top_voters | top_contributors
Expand All @@ -57,7 +59,7 @@ def poll_pull_requests(api):
votes, meritocracy_satisfied = gh.voting.get_votes(api, settings.URN, pr, meritocracy)

# is our PR approved or rejected?
vote_total, variance = gh.voting.get_vote_sum(api, votes)
vote_total, variance = gh.voting.get_vote_sum(api, votes, contributors)
threshold = gh.voting.get_approval_threshold(api, settings.URN)
is_approved = vote_total >= threshold and meritocracy_satisfied

Expand Down
27 changes: 15 additions & 12 deletions github_api/voting.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,32 +111,35 @@ def get_pr_review_reactions(api, urn, pr):
yield user, is_current, vote


def get_vote_weight(api, username):
def get_vote_weight(api, username, contributors):
""" for a given username, determine the weight that their -1 or +1 vote
should be scaled by """
user = users.get_user(api, username)

# determine their age. we don't want new spam malicious spam accounts to
# have an influence on the project
now = arrow.utcnow()
created = arrow.get(user["created_at"])
age = (now - created).total_seconds()
old_enough_to_vote = age >= settings.MIN_VOTER_AGE
weight = 1.0 if old_enough_to_vote else 0.0
# we don't want new spam malicious spam accounts to have an influence on the project
# if they've got a PR merged, they get a free pass
if user["login"] not in contributors:
# otherwise, check their account age
now = arrow.utcnow()
created = arrow.get(user["created_at"])
age = (now - created).total_seconds()
if age < settings.MIN_VOTER_AGE:
return 0

if username.lower() == "smittyvb":
weight /= 1.99991000197
return 0.50002250052

return weight
return 1


def get_vote_sum(api, votes):
def get_vote_sum(api, votes, contributors):
""" for a vote mapping of username => -1 or 1, compute the weighted vote
total and variance(measure of controversy)"""
total = 0
positive = 0
negative = 0
for user, vote in votes.items():
weight = get_vote_weight(api, user)
weight = get_vote_weight(api, user, contributors)
weighted_vote = weight * vote
total += weighted_vote
if weighted_vote > 0:
Expand Down
16 changes: 8 additions & 8 deletions tests/github_api/voting_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,21 @@ def test_get_vote_sum(self, mock_get_vote_weight):
mock_get_vote_weight.return_value = 1.0

# controversial example
mocked_vote_result = {"a": 1, "b": 1, "c": 1, "d": 1, "x": -1, "y": -1, "z": -1}
mocked_votes = {"a": 1, "b": 1, "c": 1, "d": 1, "x": -1, "y": -1, "z": -1}
expected_vote_sum = (1, 3)
self.assertEqual(voting.get_vote_sum('fake_api', mocked_vote_result), expected_vote_sum)
self.assertEqual(voting.get_vote_sum('fake_api', mocked_votes, set()), expected_vote_sum)

# all positive example
mocked_vote_result = {"a": 1, "b": 1, "c": 1, "d": 1, "x": 1, "y": 1, "z": 1}
mocked_votes = {"a": 1, "b": 1, "c": 1, "d": 1, "x": 1, "y": 1, "z": 1}
expected_vote_sum = (7, 0)
self.assertEqual(voting.get_vote_sum('fake_api', mocked_vote_result), expected_vote_sum)
self.assertEqual(voting.get_vote_sum('fake_api', mocked_votes, set()), expected_vote_sum)

# almost all negative example, level of controversy is low
mocked_vote_result = {"a": 1, "b": -1, "c": -1, "d": -1, "x": -1, "y": -1, "z": -1}
mocked_votes = {"a": 1, "b": -1, "c": -1, "d": -1, "x": -1, "y": -1, "z": -1}
expected_vote_sum = (-5, 1)
self.assertEqual(voting.get_vote_sum('fake_api', mocked_vote_result), expected_vote_sum)
self.assertEqual(voting.get_vote_sum('fake_api', mocked_votes, set()), expected_vote_sum)

# users without vote_weight don't add to total and variance
mocked_vote_result = {"a": 1, "b": 0}
mocked_votes = {"a": 1, "b": 0}
expected_vote_sum = (1, 0)
self.assertEqual(voting.get_vote_sum('fake_api', mocked_vote_result), expected_vote_sum)
self.assertEqual(voting.get_vote_sum('fake_api', mocked_votes, set()), expected_vote_sum)

0 comments on commit 873c338

Please sign in to comment.