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

402 lines
14 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 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'
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
has_many :shixun_tag_repertoires, :dependent => :destroy
has_many :shixun_service_configs, :dependent => :destroy
scope :visible, lambda{where(status: [2,3])}
scope :min, lambda { select([:id, :name, :gpid, :modify_time, :reset_time, :language, :status, :identifier,
:test_set_permission, :hide_code, :forbid_copy, :hidden, :webssh, :user_id, :code_hidden,
:task_pass, :exec_time, :multi_webssh, :vnc]) }
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) }
after_create :send_tiding
def description
self.has_attribute?(:description) ? self[:description] : ""
end
def propaedeutics
self.has_attribute?(:propaedeutics) ? self[:propaedeutics] : ""
end
def evaluate_script
self.has_attribute?(:evaluate_script) ? self[:evaluate_script] : ""
end
def should_compile?
self.mirror_repositories.published_main_mirror.first.try(:should_compile)
end
# 可供使用的实训
def operable?
logger.info("####")
self.status != -1 && !self.hidden
end
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)
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