forked from Trustie/forgeplus
170 lines
6.4 KiB
Ruby
170 lines
6.4 KiB
Ruby
# == Schema Information
|
|
#
|
|
# Table name: project_evaluations
|
|
#
|
|
# id :integer not null, primary key
|
|
# project_id :integer
|
|
# influence :float(24) default("0")
|
|
# community :float(24) default("0")
|
|
# vitality :float(24) default("0")
|
|
# health :float(24) default("0")
|
|
# trend :float(24) default("0")
|
|
# norm_influence :float(24) default("0")
|
|
# norm_community :float(24) default("0")
|
|
# norm_vitality :float(24) default("0")
|
|
# norm_health :float(24) default("0")
|
|
# norm_trend :float(24) default("0")
|
|
# score :float(24) default("0")
|
|
# created_at :datetime not null
|
|
# updated_at :datetime not null
|
|
# weekly_score :float(24)
|
|
# monthly_score :float(24)
|
|
# quarterly_score :float(24)
|
|
# yearly_score :float(24)
|
|
#
|
|
# Indexes
|
|
#
|
|
# index_project_evaluations_on_project_id (project_id)
|
|
#
|
|
|
|
class ProjectEvaluation < ApplicationRecord
|
|
belongs_to :project
|
|
|
|
def self.calculate_influence(project)
|
|
issues_count = project.issues.issue_issue.pluck(:id).size
|
|
pull_requests_count = project.issues.issue_pull_request.pluck(:id).size
|
|
comment_count = Journal.joins("INNER JOIN issues ON journals.journalized_id = issues.id")
|
|
.where("issues.project_id = ? ", project.id)
|
|
.count
|
|
influence = project.praises_count * 0.25 +
|
|
project.forked_count * 0.3 +
|
|
issues_count * 0.5 +
|
|
pull_requests_count * 0.5 +
|
|
comment_count * 0.5
|
|
|
|
influence += 500 if project.recommend
|
|
influence
|
|
end
|
|
|
|
def self.calculate_community(project)
|
|
issue_closed_count = project.issues.issue_issue.where(status_id: 5).size
|
|
pull_requests_closed_recent_count = project.issues.issue_pull_request.where("closed_on >= ?", 4.months.ago).size
|
|
issue_closed_count * 0.7 + pull_requests_closed_recent_count * 0.3
|
|
end
|
|
|
|
def self.calculate_vitality(project)
|
|
first_page = true
|
|
start_page = 1
|
|
end_page = 1
|
|
page = 1
|
|
limit = 50
|
|
last_page_count = 0
|
|
four_months_ago = 4.months.ago.to_date
|
|
loop do
|
|
result = Gitea::Repository::Commits::ListSliceService.new(project.owner.login, project.identifier, page: page, limit: limit).call
|
|
commits = result.is_a?(Hash) ? result[:body] : []
|
|
if first_page
|
|
page_count = (result[:total_count] + limit - 1) / limit
|
|
end_page = page_count + 1
|
|
first_page = false
|
|
end
|
|
last_page_count = 0
|
|
commits.each do |commits_per_day|
|
|
commit_date = commits_per_day["commit_date"].to_date
|
|
if commit_date > four_months_ago
|
|
last_page_count += commits_per_day["Commits"].size
|
|
end
|
|
end
|
|
break if 0 < last_page_count && last_page_count < limit
|
|
|
|
if last_page_count == limit
|
|
start_page = page + 1
|
|
else
|
|
end_page = page
|
|
end
|
|
|
|
break unless start_page < end_page
|
|
page = (start_page + end_page) / 2
|
|
end
|
|
commit_recent_count = (page - 1) * limit + last_page_count
|
|
@commit_recent_count = commit_recent_count
|
|
release_recent_count = project.repository.version_releases.where("created_at >= ?", 4.months.ago).size
|
|
commit_recent_count * 0.5 + release_recent_count * 0.8
|
|
end
|
|
|
|
def self.calculate_health(project)
|
|
page = 1
|
|
limit = 50
|
|
total_contributors_count = 0
|
|
key_contributors_count = 0
|
|
loop do
|
|
result = Gitea::Repository::Contributors::GetService.call(project.owner, project.identifier, { page: page, limit: limit })
|
|
contributors = result.is_a?(Hash) ? result[:body] : []
|
|
total_contributors_count = result[:total_count]
|
|
break if contributors.size < limit
|
|
key_contributors_count += contributors.count { |contributor| contributor["contributions"] >= 3 }
|
|
last_contributor = contributors.last
|
|
break unless last_contributor && last_contributor["contributions"] >= 3
|
|
page += 1
|
|
end
|
|
total_contributors_count + key_contributors_count * 3
|
|
end
|
|
|
|
def self.calculate_trend(project)
|
|
issue_recent_count = project.issues.issue_issue.where("closed_on >= ?", 4.months.ago).size
|
|
commit_recent_count = @commit_recent_count
|
|
comment_recent_count = Journal.joins("INNER JOIN issues ON journals.journalized_id = issues.id")
|
|
.where("issues.project_id = ? AND journals.created_on > ?", project.id, 4.months.ago)
|
|
.count
|
|
issue_recent_count * 0.5 + commit_recent_count * 0.35 + comment_recent_count * 0.5
|
|
end
|
|
|
|
def self.update_raw_scores(project)
|
|
evaluation = find_or_initialize_by(project_id: project.id)
|
|
|
|
evaluation.influence = calculate_influence(project)
|
|
evaluation.community = calculate_community(project)
|
|
evaluation.vitality = calculate_vitality(project)
|
|
evaluation.health = calculate_health(project)
|
|
evaluation.trend = calculate_trend(project)
|
|
|
|
evaluation.save
|
|
end
|
|
|
|
def self.update_normalized_scores
|
|
max_influence = ProjectEvaluation.maximum(:influence)
|
|
min_influence = ProjectEvaluation.minimum(:influence)
|
|
|
|
max_community = ProjectEvaluation.maximum(:community)
|
|
min_community = ProjectEvaluation.minimum(:community)
|
|
|
|
max_vitality = ProjectEvaluation.maximum(:vitality)
|
|
min_vitality = ProjectEvaluation.minimum(:vitality)
|
|
|
|
max_health = ProjectEvaluation.maximum(:health)
|
|
min_health = ProjectEvaluation.minimum(:health)
|
|
|
|
max_trend = ProjectEvaluation.maximum(:trend)
|
|
min_trend = ProjectEvaluation.minimum(:trend)
|
|
|
|
ProjectEvaluation.find_each do |evaluation|
|
|
evaluation.norm_influence = (evaluation.influence - min_influence) / (max_influence - min_influence)
|
|
evaluation.norm_community = (evaluation.community - min_community) / (max_community - min_community)
|
|
evaluation.norm_vitality = (evaluation.vitality - min_vitality) / (max_vitality - min_vitality)
|
|
evaluation.norm_health = (evaluation.health - min_health) / (max_health - min_health)
|
|
evaluation.norm_trend = (evaluation.trend - min_trend) / (max_trend - min_trend)
|
|
evaluation.score = evaluation.norm_influence * 0.3 +
|
|
evaluation.norm_community * 0.2 +
|
|
evaluation.norm_vitality * 0.15 +
|
|
evaluation.norm_health * 0.1 +
|
|
evaluation.norm_trend * 0.25
|
|
evaluation.save
|
|
end
|
|
end
|
|
|
|
def self.update_scores_by_interval(interval)
|
|
score_column = "#{interval}_score"
|
|
ProjectEvaluation.where.not(score: nil).update_all("#{score_column} = score")
|
|
end
|
|
end
|