Merge pull request '增加了对项目中显示贡献者贡献度的支持' (#324) from Nigel/forgeplus:master into dev_nanda

This commit is contained in:
xxq250 2023-02-02 17:16:03 +08:00
commit 490d875618
8 changed files with 336 additions and 51 deletions

View File

@ -55,8 +55,6 @@ class JournalsController < ApplicationController
if success_blockchain == false
normal_status(-1, "评论失败")
raise ActiveRecord::Rollback
else
render json: {status: 0, message: "评论成功", id: journal.id}
end
render :json => { status: 0, message: "评论成功", id: journal.id}

View File

@ -82,8 +82,6 @@ class PullRequestsController < ApplicationController
if success_blockchain == false
render_error("create pull request error: cannot save to blockchain")
raise ActiveRecord::Rollback
else
render_ok
end
else
render_error("create pull request error: #{@gitea_pull_request[:status]}")

View File

@ -2,8 +2,8 @@ class UsersController < ApplicationController
include ApplicationHelper
include Ci::DbConnectable
before_action :load_user, only: [:show, :homepage_info, :sync_token, :sync_gitea_pwd, :projects, :watch_users, :fan_users, :hovercard]
before_action :check_user_exist, only: [:show, :homepage_info,:projects, :watch_users, :fan_users, :hovercard]
before_action :load_user, only: [:show, :homepage_info, :sync_token, :sync_gitea_pwd, :projects, :watch_users, :fan_users, :hovercard, :hovercard4proj]
before_action :check_user_exist, only: [:show, :homepage_info,:projects, :watch_users, :fan_users, :hovercard, :hovercard4proj]
before_action :require_login, only: %i[me sync_user_info]
before_action :connect_to_ci_db, only: [:get_user_info]
before_action :convert_image!, only: [:update, :update_image]
@ -81,9 +81,134 @@ class UsersController < ApplicationController
@watchers = paginate(watchers)
end
def contribution_perc
project_id = params[:project_id]
@project = Project.find(project_id)
user_id = params[:id]
@user = User.find(user_id)
def cal_perc(count_user, count_all)
(count_user * 1.0 / (count_all + 0.000000001)).round(5)
end
if @project.use_blockchain == true or @project.use_blockchain == 1
balance_user = Blockchain::BalanceQueryOneProject.call({"user_id": user_id, "project_id": project_id})[:balance]
balance_all = Blockchain::RepoBasicInfo.call({"project_id": project_id})["cur_supply"]
scores = {
"final" => cal_perc(balance_user, balance_all),
"blockchain" => true
}
else
# 获取所有行为对应的项目内记录总数和个人记录数
features = {
"requirement" => {},
"development" => {},
"review" => {}
}
# 1. issue创建
issues = Issue.where(project_id: project_id, issue_classify: 'issue')
issue_all = issues.count
issue_user = issues.where(author_id: user_id).count
features["requirement"] = features["requirement"].merge({"issue" => {"all" => issue_all, "perc" => cal_perc(issue_user, issue_all)}})
# 2. 里程碑创建
milestones = Version.where(project_id: project_id)
milestone_all = milestones.count
milestone_user = milestones.where(user_id: user_id).count
features["requirement"] = features["requirement"].merge({"milestone" => {"all" => milestone_all, "perc" => cal_perc(milestone_user, milestone_all)}})
# 3. issue评论
issue_comments = Journal.joins("INNER JOIN issues on journals.journalized_id=issues.id").where("issues.project_id=#{project_id} and journalized_type='Issue' and issues.issue_classify='issue'")
issue_comment_all = issue_comments.count
issue_comment_user = issue_comments.where("journals.user_id=#{user_id}").count
features["requirement"] = features["requirement"].merge({"issue_comment" => {"all" => issue_comment_all, "perc" => cal_perc(issue_comment_user, issue_comment_all)}})
# 4. 合并请求
prs = PullRequest.where(project_id: project_id)
pr_all = prs.count
pr_user = prs.where(user_id: user_id).count
features["development"] = features["development"].merge({"pr" => {"all" => pr_all, "perc" => cal_perc(pr_user, pr_all)}})
# 5. pr评论
pr_comments = Journal.joins("INNER JOIN issues on journals.journalized_id=issues.id").where("issues.project_id=#{project_id} and journalized_type='Issue' and issues.issue_classify='pull_request'")
pr_comment_all = pr_comments.count
pr_comment_user = pr_comments.where("journals.user_id=#{user_id}").count
features["review"] = features["review"].merge({"pr_comment" => {"all" => pr_comment_all, "perc" => cal_perc(pr_comment_user, pr_comment_all)}})
# 6. 代码行评论
line_comments = Journal.joins("INNER JOIN pull_requests on journals.journalized_id=pull_requests.id").where("pull_requests.project_id=#{project_id} and journalized_type='PullRequest'")
line_comment_all = line_comments.count
line_comment_user = line_comments.where("journals.user_id=#{user_id}").count
features["review"] = features["review"].merge({"line_comment" => {"all" => line_comment_all, "perc" => cal_perc(line_comment_user, line_comment_all)}})
# 7. 代码行、commit贡献统计
code_contributions = Api::V1::Projects::CodeStats::ListService.call(@project, {ref: nil})
commit_all = code_contributions["commit_count"]
addition_all = code_contributions["additions"]
deletion_all = code_contributions["deletions"]
commit_user = 0
addition_user = 0
deletion_user = 0
code_contributions["authors"].each do |author|
if author["name"] == @user.login
commit_user = author["commits"]
addition_user = author["additions"]
deletion_user = author["deletions"]
end
end
features["development"] = features["development"].merge({"commit" => {"all" => commit_all, "perc" => cal_perc(commit_user, commit_all)}})
features["development"] = features["development"].merge({"addition" => {"all" => addition_all, "perc" => cal_perc(addition_user, addition_all)}})
features["development"] = features["development"].merge({"deletion" => {"all" => deletion_all, "perc" => cal_perc(deletion_user, deletion_all)}})
def cal_weight(features)
weights = {} # 计算每一项的权重
categories = []
features.each do |key, _|
categories << key
weights[key] = Hash.new
end
count_all = 0
counts = {}
categories.each do |category|
count_1 = 0
features[category].each do |_, value|
count_1 += value["all"]
end
count_all += count_1
counts[category] = count_1
features[category].each do |key, value|
weight = cal_perc(value["all"], count_1)
weights[category] = weights[category].merge({key => weight})
end
end
categories.each do |category|
weight = cal_perc(counts[category], count_all)
weights[category] = weights[category].merge({"category_weight" => weight})
end
return weights
end
weights_categories = cal_weight(features)
scores = {
"final" => 0.0,
"blockchain" => false
}
features.each do |category, value_1|
category_score = 0.0
value_1.each do |action, value_2|
category_score += weights_categories[category][action] * value_2["perc"]
end
scores["final"] += weights_categories[category]["category_weight"] * category_score.round(4)
scores = scores.merge({category => category_score.round(4)})
end
end
render json: { scores: scores }
end
def hovercard
end
# author: zxh, 查询贡献者的贡献度
def hovercard4proj
end
def update
return render_not_found unless @user = User.find_by(login: params[:id]) || User.find_by_id(params[:id])
return render_forbidden unless User.current.logged? && (current_user&.admin? || current_user.id == @user.id)

View File

@ -1,45 +1,160 @@
module Watchable
extend ActiveSupport::Concern
included do
has_many :watchers, as: :watchable, dependent: :destroy
has_many :watcher_users, through: :watchers, source: :user, validate: false
scope :watched_by, -> (user_id) { includes(:watchers).where(watchers: { user_id: user_id }) }
scope :following, -> (user_id) { watched_by(user_id) }
end
def watched?(watchable)
watchable.watchers.exists?(user: self)
end
def watch!(watchable)
watchable.watchers.create!(user: self, created_at: Time.current)
end
def unwatch!(watchable)
obj = watchable.watchers.find_by(user: self)
obj.destroy! if obj.present?
end
# 我正在关注的、我追随的
def following
User.following(self.id)
end
def following_count
following.size
end
# 关注我的、我的粉丝、我的追随者
def followers
watcher_users
end
def followers_count
followers.size
end
module ClassMethods
end
end
module Watchable
extend ActiveSupport::Concern
included do
has_many :watchers, as: :watchable, dependent: :destroy
has_many :watcher_users, through: :watchers, source: :user, validate: false
scope :watched_by, -> (user_id) { includes(:watchers).where(watchers: { user_id: user_id }) }
scope :following, -> (user_id) { watched_by(user_id) }
end
def watched?(watchable)
watchable.watchers.exists?(user: self)
end
def watch!(watchable)
watchable.watchers.create!(user: self, created_at: Time.current)
end
def unwatch!(watchable)
obj = watchable.watchers.find_by(user: self)
obj.destroy! if obj.present?
end
# 我正在关注的、我追随的
def following
User.following(self.id)
end
def following_count
following.size
end
def contribution_perc
project_name = params[:project_name]
user_login = params[:id]
@project = Project.find_by_name(project_name)
@user = User.find_by_login(user_login)
def cal_perc(count_user, count_all)
(count_user * 1.0 / (count_all + 0.000000001)).round(5)
end
if @project.use_blockchain == true or @project.use_blockchain == 1
balance_user = Blockchain::BalanceQueryOneProject.call({"user_id": @user.id, "project_id": @project.id})[:balance]
balance_all = Blockchain::RepoBasicInfo.call({"project_id": @project.id})["cur_supply"]
score = cal_perc(balance_user, balance_all)
else
# 获取所有行为对应的项目内记录总数和个人记录数
features = {
"requirement" => {},
"development" => {},
"review" => {}
}
# 1. issue创建
issues = Issue.where(project_id: @project.id, issue_classify: 'issue')
issue_all = issues.count
issue_user = issues.where(author_id: @user.id).count
features["requirement"] = features["requirement"].merge({"issue" => {"all" => issue_all, "perc" => cal_perc(issue_user, issue_all)}})
# 2. 里程碑创建
milestones = Version.where(project_id: @project.id)
milestone_all = milestones.count
milestone_user = milestones.where(user_id: @user.id).count
features["requirement"] = features["requirement"].merge({"milestone" => {"all" => milestone_all, "perc" => cal_perc(milestone_user, milestone_all)}})
# 3. issue评论
issue_comments = Journal.joins("INNER JOIN issues on journals.journalized_id=issues.id").where("issues.project_id=#{@project.id} and journalized_type='Issue' and issues.issue_classify='issue'")
issue_comment_all = issue_comments.count
issue_comment_user = issue_comments.where("journals.user_id=#{@user.id}").count
features["requirement"] = features["requirement"].merge({"issue_comment" => {"all" => issue_comment_all, "perc" => cal_perc(issue_comment_user, issue_comment_all)}})
# 4. 合并请求
prs = PullRequest.where(project_id: @project.id)
pr_all = prs.count
pr_user = prs.where(user_id: @user.id).count
features["development"] = features["development"].merge({"pr" => {"all" => pr_all, "perc" => cal_perc(pr_user, pr_all)}})
# 5. pr评论
pr_comments = Journal.joins("INNER JOIN issues on journals.journalized_id=issues.id").where("issues.project_id=#{@project.id} and journalized_type='Issue' and issues.issue_classify='pull_request'")
pr_comment_all = pr_comments.count
pr_comment_user = pr_comments.where("journals.user_id=#{@user.id}").count
features["review"] = features["review"].merge({"pr_comment" => {"all" => pr_comment_all, "perc" => cal_perc(pr_comment_user, pr_comment_all)}})
# 6. 代码行评论
line_comments = Journal.joins("INNER JOIN pull_requests on journals.journalized_id=pull_requests.id").where("pull_requests.project_id=#{@project.id} and journalized_type='PullRequest'")
line_comment_all = line_comments.count
line_comment_user = line_comments.where("journals.user_id=#{@user.id}").count
features["review"] = features["review"].merge({"line_comment" => {"all" => line_comment_all, "perc" => cal_perc(line_comment_user, line_comment_all)}})
# 7. 代码行、commit贡献统计
code_contributions = Api::V1::Projects::CodeStats::ListService.call(@project, {ref: nil})
commit_all = code_contributions["commit_count"]
addition_all = code_contributions["additions"]
deletion_all = code_contributions["deletions"]
commit_user = 0
addition_user = 0
deletion_user = 0
code_contributions["authors"].each do |author|
if author["name"] == @user.login
commit_user = author["commits"]
addition_user = author["additions"]
deletion_user = author["deletions"]
end
end
features["development"] = features["development"].merge({"commit" => {"all" => commit_all, "perc" => cal_perc(commit_user, commit_all)}})
features["development"] = features["development"].merge({"addition" => {"all" => addition_all, "perc" => cal_perc(addition_user, addition_all)}})
features["development"] = features["development"].merge({"deletion" => {"all" => deletion_all, "perc" => cal_perc(deletion_user, deletion_all)}})
def cal_weight(features)
weights = {} # 计算每一项的权重
categories = []
features.each do |key, _|
categories << key
weights[key] = Hash.new
end
count_all = 0
counts = {}
categories.each do |category|
count_1 = 0
features[category].each do |_, value|
count_1 += value["all"]
end
count_all += count_1
counts[category] = count_1
features[category].each do |key, value|
weight = cal_perc(value["all"], count_1)
weights[category] = weights[category].merge({key => weight})
end
end
categories.each do |category|
weight = cal_perc(counts[category], count_all)
weights[category] = weights[category].merge({"category_weight" => weight})
end
return weights
end
weights_categories = cal_weight(features)
score = 0.0
features.each do |category, value_1|
category_score = 0.0
value_1.each do |action, value_2|
category_score += weights_categories[category][action] * value_2["perc"]
end
score += weights_categories[category]["category_weight"] * category_score.round(4)
end
end
score
(score * 100).round(1).to_s + "%"
end
# 关注我的、我的粉丝、我的追随者
def followers
watcher_users
end
def followers_count
followers.size
end
module ClassMethods
end
end

View File

@ -63,4 +63,14 @@ class ApplicationQuery
return resp_body
end
# query the basic info of a repository
def find_blockchain_repo_info(project_id)
param = {
"request-type": "query repo basic info",
"token_name": project_id.to_s
}.to_json
resp_body = invoke_blockchain_api(Blockchain.blockchain_config[:api_url], param)
return resp_body
end
end

View File

@ -0,0 +1,17 @@
class Blockchain::RepoBasicInfo < ApplicationQuery
attr_reader :params, :is_current_admin_user
def initialize(params)
@params = params
end
def call
info = find_blockchain_repo_info(params[:project_id].to_s)
if info == false
return false
else
return info
end
end
end

View File

@ -0,0 +1,21 @@
json.id @user.id
json.login @user.login
json.name @user.full_name
json.location @user.location
json.image_url url_to_avatar(@user)
json.url "#{request.base_url }/#{@user.login}"
json.followers_count @user.followers_count
json.followers_url "#{base_url}/#{@user.login}/fan_users"
json.following_count @user.following_count
json.following_url "#{base_url}/#{@user.login}/watchers"
json.projects_count @user.projects_count
json.projects_url "#{base_url}/#{@user.login}"
json.projects_count @user.projects_count
json.is_watch current_user&.watched?(@user)
json.contribution_perc @user.contribution_perc
json.organizations @user.organizations do |organization|
json.login organization.login
json.name organization.real_name
json.image_url url_to_avatar(organization)
json.url "#{base_url}/#{organization.login}"
end

View File

@ -280,6 +280,7 @@ Rails.application.routes.draw do
get :fan_users
put :update_sponsor_description
get :hovercard
get :hovercard4proj # author: zxh, 获取用户对项目的贡献情况
put :update_image
get :get_image
end