# encoding: utf-8 # status 控制实训的状态,0:编辑;1: 申请发布; 2:正式发布; 3:关闭 # is_updated 字段为1表示实训的更改可能会影响到后续的评测,0表示更改不会影响后续的评测 # reset_time表示需要更新脚本的时间 # modify_time表示仅测试集更新的时间 # main_mirror_id 实训使用镜像的主类别 # child_mirror_id 实训使用镜像的子类别,子类别可以有多个,所以字段是字符串数组类型 # webssh 0:不开启webssh;1:开启练习模式; 2:开启评测模式 # trainee 实训的难度 # user_scorp # exec_time 程序执行时间 # 繁忙等级参数:80发布实训失败,返回code为-1;81:实训开启的时候fork失败或者pull失败;83:开启实训的时候,openGameInstance返回非0值;84:jenkins系统异常,post数据不成功 # 85:challenge创建的时候Jenkins处理异常;86:实训评测失败,jenkins返回错误结果; 87:版本库删除异常;88:点击评测失败,没返回数据;89:重置链接jenkins返回失败 # 90: 更新公共测试用例失败,返回code非0;91:实训有改动,评测的时候初始化数据,返回数据code非0;92:webssh开启失败,接口getConnectInfo返回code值非0 # 93: 实训断开连接失败,接口deleteSSH返回code值非0 # 94: 获取评测等待时间失败,接口getWaitingTime返回code值为非0 # 95:代码重置失败 # 96:代码自动修复失败 # 97:tpi获取当前代码为空 # 98:更新tpi脚本异常 # 99:vnc连接异常 # 110:websshPod删除异常 # 116:challenge创建关卡的时候返回结果错误 # 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