You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
pgfqe6ch8/app/models/shixun.rb

388 lines
13 KiB

6 years ago
# encoding: utf-8
# status 控制实训的状态0编辑1: 申请发布; 2正式发布 3关闭
# is_updated 字段为1表示实训的更改可能会影响到后续的评测0表示更改不会影响后续的评测
# reset_time表示需要更新脚本的时间
# modify_time表示仅测试集更新的时间
# main_mirror_id 实训使用镜像的主类别
# child_mirror_id 实训使用镜像的子类别,子类别可以有多个,所以字段是字符串数组类型
# webssh 0不开启webssh1开启练习模式; 2开启评测模式
# trainee 实训的难度
# user_scorp
# exec_time 程序执行时间
# 繁忙等级参数80发布实训失败返回code为-181实训开启的时候fork失败或者pull失败;83开启实训的时候openGameInstance返回非0值84jenkins系统异常post数据不成功
# 85challenge创建的时候Jenkins处理异常;86实训评测失败jenkins返回错误结果; 87版本库删除异常88点击评测失败没返回数据89重置链接jenkins返回失败
# 90: 更新公共测试用例失败返回code非091实训有改动评测的时候初始化数据返回数据code非092webssh开启失败接口getConnectInfo返回code值非0
# 93: 实训断开连接失败接口deleteSSH返回code值非0
# 94: 获取评测等待时间失败接口getWaitingTime返回code值为非0
# 95代码重置失败
# 96代码自动修复失败
# 97tpi获取当前代码为空
# 98更新tpi脚本异常
# 99vnc连接异常
# 110websshPod删除异常
# 116challenge创建关卡的时候返回结果错误
#
class Shixun < ActiveRecord::Base
attr_accessible :description, :name, :changeset_num, :status, :user_id, :gpid, :language, :identifier, :authentication, :closer_id, :end_time, :publish_time,
:propaedeutics, :trainee, :major_id, :homepage_show, :webssh, :hidden, :fork_from, :can_copy, :modify_time, :reset_time, :git_url, :use_scope,
:vnc, :evaluate_script, :image_text, :exec_time, :test_set_permission, :hide_code, :excute_time, :forbid_copy
belongs_to :creator, foreign_key: :user_id, class_name: 'User'
6 years ago
has_many :users, :through => :shixun_members
has_many :shixun_members, :dependent => :destroy
has_one :repository, :dependent => :destroy
has_many :homework_commons_shixunses
has_many :homework_challenge_settings, :dependent => :destroy
has_many :challenges, :dependent => :destroy
has_many :myshixuns, :dependent => :destroy
has_many :stage_shixuns, :dependent => :destroy
belongs_to :major
belongs_to :major_course
has_many :shixun_major_courses, :dependent => :destroy
has_and_belongs_to_many :homework_commons
has_many :discusses, :as => :dis, :dependent => :destroy
has_many :shixun_ports
has_many :evaluate_records, :dependent => :destroy
has_many :shixun_mirror_repositories, :dependent => :destroy
has_many :mirror_repositories, :through => :shixun_mirror_repositories
has_many :shixun_schools, :dependent => :destroy
has_many :schools, :through => :shixun_schools
has_many :exercise_shixun_challenges, :dependent => :destroy
has_many :exercise_bank_shixun_challenges, :dependent => :destroy
has_many :tag_repertoires, :through => :shixun_tag_repertoires
6 years ago
has_many :shixun_tag_repertoires, :dependent => :destroy
scope :visible, lambda{where(status: [2,3])}
scope :min, lambda { select([:id, :name, :gpid, :modify_time, :reset_time, :language, :propaedeutics, :status, :identifier, :test_set_permission, :hide_code, :forbid_copy,
:webssh, :user_id, :code_hidden, :task_pass, :exec_time, :multi_webssh]) }
scope :published, lambda{where(status: 2)}
scope :field_for_recommend, lambda{ select([:id, :name, :identifier, :myshixuns_count]) }
include ApplicationHelper
has_many :tidings, :as => :container, :dependent => :destroy
#scope :visible, -> { where(status: -1) }
6 years ago
after_create :send_tiding
def should_compile?
self.mirror_repositories.published_main_mirror.first.try(:should_compile)
end
# 可供使用的实训
def operable?
self.status != -1 && !self.hidden
end
6 years ago
def is_published?
self.status > 1 ? true : false
end
def fork_identifier
self.fork_from.nil? ? "--" : Shixun.where(id: self.fork_from).first.try(:identifier)
6 years ago
end
def get_fork
Shixun.where(:fork_from => self.id)
end
def switch_language
case self.shixun_main_name
when "C#"
"Cxp"
when "C/C++","C","C++"
"Cjia"
else
self.shixun_main_name
end
end
def send_tiding
self.tidings << Tiding.new(:user_id => self.user_id, :trigger_user_id => self.user_id, :belong_container_id => self.id, :belong_container_type =>'Shixun', :tiding_type => "System", :viewed => 0)
end
def is_choice_type
self.challenges.count == self.challenges.where(:st => 1).count ? true : false
end
def is_practice_type
self.challenges.count == self.challenges.where(:st => 0).count ? true : false
end
# 实训关卡的总分(由于大部分是实践题,因此没关联查choose表)
def all_score
sum = 0
self.challenges.each do |challenge|
if challenge.st == 0
sum += challenge.score
else
sum += challenge.choose_score
end
end
return sum
end
# 实训评分 cnt-评分次数 sum-总评分
def shixun_preference
game_star_info = Shixun.find_by_sql("select g.star from
(games g left join (myshixuns m join shixuns s on s.id = m.shixun_id) on m.id = g.myshixun_id)
where g.star != 0 and s.id = #{self.id}")
if game_star_info.present?
cnt = game_star_info.count
sum = game_star_info.map(&:star).sum
star = (sum / cnt.to_f).round(1)
else
5.0
end
end
# 实训评分信息
# return [实训评分, 5星评分比例 4星评分比例 3星评分比例 2星评分比例 1星评分比例]
def shixun_preference_info
game_star_info = Shixun.find_by_sql("select g.star from
(games g left join (myshixuns m join shixuns s on s.id = m.shixun_id) on m.id = g.myshixun_id)
where g.star != 0 and s.id = #{self.id}")
star_info = []
if game_star_info.present?
5.downto(1) do |i|
star_info << ((game_star_info.select{|s| s.star == i}.count / game_star_info.count.to_f) * 100).round
end
sum = star_info.sum
max = star_info.max
# 四舍五入引起5星比例超过100% 将最大的比例的评分做出调整
if sum > 100
star_info = star_info.map{|s| s == max ? s - 1 : s}
elsif sum < 100
star_info = star_info.map{|s| s == max ? s + 1 : s}
end
cnt = game_star_info.count
sum = game_star_info.map(&:star).sum
star_info.unshift((sum / cnt.to_f).round(1))
else
star_info = [5.0, 100, 0, 0, 0, 0]
end
return star_info
end
# 实训的版本库创建者为"educoder" 和"eduforge"
# "educoder"为原创,"eduforge"为复制的时候用户
def git_login
self.fork_from.blank? ? "educoder" : "eduforge"
end
def user_evaluate_status
myshixun = self.myshixuns.where(:user_id => User.current.id).first
if myshixun.blank?
"--"
else
game_id = myshixun.games.map(&:id)
output = Output.where(:game_id => game_id).reorder("updated_at desc").first
if output.blank?
"--"
else
if output.try(:code) == -1
time_from_now(output.updated_at).to_s + "评测失败"
else
time_from_now(output.updated_at).to_s + "评测成功"
end
end
end
end
def shixun_tag_name
self.tag_repertoires.order("name asc").map(&:name)
end
def has_web_route
self.mirror_name.include?('JFinal') || self.mirror_name.include?('PHP') && self.mirror_name.include?('Mysql') || self.mirror_name.include?('Web')
end
def script_tag
if self.mirror_script_id
MirrorScript.where(:id => self.mirror_script_id).first
end
end
def standrad_script
mirrors_id = self.mirror_repositories.map(&:id)
MirrorScript.where(:mirror_repository_id => mirrors_id)
end
def shixun_main_mirror_id
id = ''
self.mirror_repositories.each do |mirror|
if mirror.main_type == "1"
id = mirror.id
end
end
return id
end
def shixun_child_mirror_id
i = 0
id = ''
self.mirror_repositories.each do |mirror|
if mirror.main_type == "0"
i += 1
if i == 1
id = mirror.id
else
id = "#{id},#{mirror.id}"
end
end
end
return id
end
def shixun_main_name
self.mirror_repositories.published_main_mirror.first.try(:type_name)
end
def main_mirror
self.mirror_repositories.published_main_mirror.first
end
def shixun_child_name
name = ''
index = 0
self.mirror_repositories.each do |mirror|
if mirror.main_type == "0"
index += 1
if index == 1
name = mirror.type_name
else
name = "#{name};#{mirror.type_name}"
end
end
end
return name
end
def mirror_name
self.mirror_repositories.map(&:type_name).blank? ? "" : self.mirror_repositories.map(&:type_name)
end
# 实训脚本,如果不存在取标准脚本
#1.若脚本中只有sourceClassName,那么将传入的执行文件名直接替换SOURCECLASSNAME
#2.若脚本中只有challengeProgramName,那么将传入的执行文件名直接替换challengeProgramName
#3.若脚本中有challengeProgramName和sourceClassName,那么将传入文件名直接替换challengeProgramName并对这些文件名做处理得到执行类名替换sourceClassName目前是java类型的需如此
# 3.1 处理方法例如src/step1/helloword.java,处理得到step1.helloword
def tpm_script
script = self.evaluate_script || self.mirror_repositories.published_main_mirror.first.try(:script_template)
if script.present?
script = script.gsub("\r\n", "\n") # 保持和Linux一致
source_class_name = []
challenge_program_name = []
self.challenges.map(&:exec_path).each do |exec_path|
challenge_program_name << '"' + exec_path + '"'
source_class_name = exec_path.split("src/")[1].split(".java")[0]
source_class_name = source_class_name.nil? ? "" : source_class_name.gsub("/", ".")
end
script = if script.include?("sourceClassName") && script.include?("challengeProgramName")
script.gsub("CHALLENGEPROGRAMNAMES","#{challenge_program_name.join(" ")}").gsub("SOURCECLASSNAMES", "#{source_class_name.join(" ")}")
else
script.gsub("CHALLENGEPROGRAMNAMES","#{challenge_program_name.join(" ")}").gsub("SOURCECLASSNAMES", "#{challenge_program_name.join(" ")}")
end
#Base64.urlsafe_encode64(script)
end
end
# 获取特定格式的实训测试用例
# params = {:challengeStage => "#{@challenge.position}", :challengeType => "#{@challenge.evaluation_way.to_i}",
# :challengeProgramName => "#{@challenge.exec_path}",
# :trainingID => "#{@shixun.id}", :operationEnvironment => @shixun.language}
def gameInfo
fragments = []
testSet = []
testCases = []
challenges = self.challenges.includes(:test_sets)
challenges.each do |challenge|
pipeline_script = {:challengeStage => "#{challenge.position}",
:challengeType => "#{challenge.evaluation_way.to_i}",
:challengeProgramName => "#{challenge.exec_path}"}
fragments << pipeline_script
# 每一关所有的测试集
challenge.test_sets.each do |test_set|
test_cases = {:input => test_set.input, :output => test_set.output}
testSet << test_cases
end
test_sets_list = {:challengeStage => challenge.position.to_s, :challengeTestCases => testSet}
testCases << test_sets_list
testSet = []
end
gameInfo = {:tpmID => self.id.to_s, :operationEnvironment => "#{self.try(:language)}", :challengeInfo => fragments, :testCases => testCases}
gameInfo = Base64.urlsafe_encode64(gameInfo.to_json) unless gameInfo.blank?
return gameInfo
end
# id 转换成 identifier
def to_param
identifier
end
def shixun_trainee
trainee = ""
case self.trainee
when 1
trainee = "初级学员"
when 2
trainee = "中级学员"
when 3
trainee = "高级学员"
when 4
trainee = "顶级学员"
end
trainee
end
def shixun_level
level = ""
case self.trainee
when 1
level = "初级"
when 2
level = "中级"
when 3
level = "高级"
when 4
level = "顶级"
end
level
end
def owner
User.find(self.user_id)
rescue ActiveRecord::RecordNotFound
render_404
end
# 大部分是实践题题,因此加入逻辑判断可以减少查询
def shixun_score
self.challenges.map{|c| c.choose_score.to_i }.sum
end
def shixun_status
status = ""
case self.status
when 0
status = "编辑中"
when 1
status = "审核中"
when 2
status = "已发布"
when 3
status = "已关闭"
end
end
def school_count
user_ids = self.myshixuns.pluck(:user_id)
num = UserExtensions.where(:user_id => user_ids).pluck(:school_id).uniq.length
end
def collaborators
self.shixun_members.select{|member| member.role == 2} unless self.shixun_members.blank?
end
end