UNPKG

hubot-code-review

Version:
263 lines (242 loc) 9.42 kB
sendFancyMessage = require './lib/sendFancyMessage' class CodeReviewKarma constructor: (@robot) -> @scores = {} @monthly_scores = {} # Note: 'Schedule Monthly Award and Score Reset' # cron task managed by CodeReviews @robot.brain.on 'loaded', => if @robot.brain.data.code_review_karma cache = @robot.brain.data.code_review_karma @scores = cache.scores || {} @monthly_scores = cache.monthly_scores || {} # Update Redis store of CR queues and karma scores # @return none update_redis: -> @robot.brain.data.code_review_karma = { scores: @scores, monthly_scores: @monthly_scores } # Increment user's karma score # @param string user Name of user # @param string dir Direction property, 'take' or 'give' # @param int qty Amount to increment by # @return none incr_score: (user, dir, qty = 1) -> qty -= 0 @scores[user] ||= { give: 0, take: 0 } @scores[user][dir] += qty @monthly_scores[user] ||= { give: 0, take: 0 } @monthly_scores[user][dir] += qty @update_redis() # Decrement user's karma score # @param string user Name of user # @param string dir Direction property, 'take' or 'give' # @param int qty Amount to decrement by # @return none decr_score: (user, dir, qty = 1) -> qty -= 0 if @scores[user] and @scores[user][dir] @scores[user][dir] -= qty @scores[user][dir] = 0 if @scores[user][dir] < 0 if @monthly_scores[user] and @monthly_scores[user][dir] @monthly_scores[user][dir] -= qty @monthly_scores[user][dir] = 0 if @monthly_scores[user][dir] < 0 @update_redis() # Calculate karm score # @param int give CRs given # @param int take CRs taken # @return int Karma score karma: (give, take) -> if take is 0 return give return Math.round( ( give / take - 1 ) * 100 ) / 100 # Get leaderboard of gives, takes, and karma score # @return array Array of most CRs given, most CRs taken, best karma score rankings: -> gives = takes = karmas = { score: -1, user: 'Nobody' } for user, scores of @scores if scores.give > gives.score gives = { score: scores.give, user: user } else if scores.give == gives.score gives.user = gives.user + ', ' + user if scores.take > takes.score takes = { score: scores.take, user: user } else if scores.take == takes.score takes.user = takes.user + ', ' + user karma = @karma(scores.give, scores.take) if karma > karmas.score karmas = { score: karma, user: user } else if karma == karmas.score karmas.user = karmas.user + ', ' + user [gives, takes, karmas ] # Get user's score # @param string user User name # @return obj Key-value of CRs given and taken scores_for_user: (user) -> all_scores = @scores[user] || { give: 0, take: 0 } month_scores = @monthly_scores[user] || { give: 0, take: 0 } return { all_scores, month_scores } # Remove user from scores # @return bool True if user was found; false if user not found remove_user: (user) -> if @scores[user] || @monthly_scores[user] if @scores[user] delete @scores[user] if @monthly_scores[user] delete @monthly_scores[user] @update_redis() return true return false # Reset all scores # @return none flush_scores: -> @robot.logger.info "CodeReviewKarma.flush_scores: resetting all scores..." @scores = {} @monthly_scores = {} @update_redis() # Reset monthly scores # @return none flush_monthly_scores: -> @robot.logger.info "CodeReviewKarma.flush_monthly_scores: resetting monthly_scores..." # Explicitly delete all monthly score entries if Object.keys(@monthly_scores).length for user of @monthly_scores delete @monthly_scores[user] delete @robot.brain.data.code_review_karma.monthly_scores[user] # Announce top reviewers this month: # Most reviews (up to three) # Most requests (up to three) # Best karma (up to 1) # # If called: # via cron: to HUBOT_CODE_REVIEW_KARMA_MONTHLY_AWARD_ROOM (if set) # Note: cron scheduled from CodeReviews to avoid duplicate schedules # via msg: to the original room # # @return none monthly_rankings: (msg = null) -> msg_prefix = "" attachments = [] if (msg)? # function start from message (not cron) msg_prefix = "Here's how things stand this month:" announce_room = msg.message.room else if (process.env.HUBOT_CODE_REVIEW_KARMA_MONTHLY_AWARD_ROOM)? # Triggered from cron and HUBOT_CODE_REVIEW_KARMA_MONTHLY_AWARD_ROOM set msg_prefix = "Here's the final code review leaderboard for last month:" announce_room = "\##{process.env.HUBOT_CODE_REVIEW_KARMA_MONTHLY_AWARD_ROOM}" else # Triggered from cron, no room set... clear monthly_scores and return @flush_monthly_scores() return reviews_this_month = Object.keys(@monthly_scores).length if reviews_this_month is 0 attachments.push fallback: "No code reviews seen this month yet." text: "No code reviews seen this month yet. :cricket:" color: "#C0C0C0" else attachments.push fallback: msg_prefix pretext: msg_prefix # Top three most reviews given followed by karma top_3_reviewers = Object.keys(@monthly_scores) .map((index) => return { user: index, list: 'Most Reviews', give: @monthly_scores[index].give, take: @monthly_scores[index].take, karma: @karma(@monthly_scores[index].give, @monthly_scores[index].take) } ).sort((a, b) -> if b.give is a.give return b.karma - a.karma else return b.give - a.give ).map((winner, rank) -> return Object.assign({}, winner, { placement: rank + 1 }) ).slice(0, 3) # Top three most reviews requested followed by karma top_3_requesters = Object.keys(@monthly_scores) .map((index) => return { user: index, list: 'Most Requests', give: @monthly_scores[index].give, take: @monthly_scores[index].take, karma: @karma(@monthly_scores[index].give, @monthly_scores[index].take) } ).sort((a, b) -> # Sort by most reviews given followed by karma if b.take is a.take return b.karma - a.karma else return b.take - a.take ).map((winner, rank) -> return Object.assign({}, winner, { placement: rank + 1 }) ).slice(0, 3) # Top three best karma followed by reviews top_1_karma = Object.keys(@monthly_scores) .map((index) => return { user: index, list: 'Best Karma' give: @monthly_scores[index].give, take: @monthly_scores[index].take, karma: @karma(@monthly_scores[index].give, @monthly_scores[index].take) } ).sort((a, b) -> # Sort by most reviews given followed by karma if b.karma is a.karma return b.give - a.give else return b.karma - a.karma ).map((winner, rank) -> return Object.assign({}, winner, { placement: rank + 1 }) ).slice(0, 1) monthly_leaderboard = [top_3_reviewers..., top_3_requesters..., top_1_karma...] for index of monthly_leaderboard entry = monthly_leaderboard[index] switch(entry.placement) when 1 then medal_color = "#D4AF37" # gold when 2 then medal_color = "#BCC6CC" # silver when 3 then medal_color = "#5B391E" # bronze else medal_color = "#FFFFFF" # white user_detail = @robot.brain.userForName("#{entry.user}") if (user_detail)? and (user_detail.slack)? # if slack, add some deeper data gravatar = user_detail.slack.profile.image_72 full_name = user_detail.slack.real_name else full_name = entry.user score_field_array = [] switch (entry.list) when 'Most Reviews' reviewed_requested_text = "*#{entry.give}* / #{entry.take}" karma_text = "#{entry.karma}" when 'Most Requests' reviewed_requested_text = "#{entry.give} / *#{entry.take}*" karma_text = "#{entry.karma}" when 'Best Karma' reviewed_requested_text = "#{entry.give} / #{entry.take}" karma_text = "*#{entry.karma}*" score_field_array.push title: "Reviewed / Requested", value: reviewed_requested_text, short: true score_field_array.push title: "Karma Score", value: karma_text, short: true attachments.push fallback: "#{full_name}: Reviewed #{entry.give}, Requested #{entry.take}, Karma: #{entry.karma}" text: "\#*_#{entry.placement}_ #{entry.list}* - *#{full_name}* (@#{entry.user}): " fields: score_field_array mrkdwn_in: ["text", "fields"] color: medal_color thumb_url: gravatar sendFancyMessage(@robot, "#{announce_room}", attachments) # If triggered by monthly cron task, reset the monthly scores unless (msg)? @flush_monthly_scores() module.exports = CodeReviewKarma