|
|
|
|
class GamesController < ApplicationController
|
|
|
|
|
before_action :require_login
|
|
|
|
|
before_action :find_game
|
|
|
|
|
before_action :find_shixun, only: [:show, :answer, :rep_content, :choose_build, :game_build, :game_status]
|
|
|
|
|
|
|
|
|
|
before_action :allowed
|
|
|
|
|
|
|
|
|
|
#require 'iconv'
|
|
|
|
|
|
|
|
|
|
include GamesHelper
|
|
|
|
|
include ApplicationHelper
|
|
|
|
|
|
|
|
|
|
def show
|
|
|
|
|
uid_logger("--games show start")
|
|
|
|
|
# 防止评测中途ajaxE被取消;3改成0是为了处理首次进入下一关的问题
|
|
|
|
|
@game.update_attribute(:status, 0) if @game.status == 1
|
|
|
|
|
@game.update_attributes(status: 0, open_time: Time.now) if @game.status == 3
|
|
|
|
|
game_challenge = Challenge.base_attrs.find(@game.challenge_id)
|
|
|
|
|
|
|
|
|
|
# 选择题类型的实训关卡总分
|
|
|
|
|
@st = game_challenge.st
|
|
|
|
|
if @st == 1
|
|
|
|
|
game_challenge.score = game_challenge.choose_score.to_i
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
game_count = Game.where(myshixun_id: @game.myshixun_id).count
|
|
|
|
|
|
|
|
|
|
discusses = @shixun.discusses
|
|
|
|
|
discusses = discusses.where('hidden = false OR user_id = :user_id', user_id: current_user.id) unless current_user.admin?
|
|
|
|
|
discusses_count = discusses.count
|
|
|
|
|
|
|
|
|
|
@user = @game.owner
|
|
|
|
|
is_teacher = @user.is_teacher?
|
|
|
|
|
|
|
|
|
|
# 实训超时设置
|
|
|
|
|
time_limit = game_challenge.exec_time
|
|
|
|
|
|
|
|
|
|
# 上一关、下一关
|
|
|
|
|
prev_game = @game.prev_of_current_game(@shixun.id, @game.myshixun_id, game_challenge.position)
|
|
|
|
|
next_game = @game.next_of_current_game(@shixun.id, @game.myshixun_id, game_challenge.position)
|
|
|
|
|
|
|
|
|
|
# 关卡点赞数, praise_or_tread = 1则表示赞过
|
|
|
|
|
praise_count = PraiseTread.where(praise_tread_object_id: game_challenge.id, praise_tread_object_type: "Challenge",
|
|
|
|
|
praise_or_tread: 1).count
|
|
|
|
|
user_praise = PraiseTread.where(praise_tread_object_id: game_challenge.id, praise_tread_object_type: "Challenge",
|
|
|
|
|
user_id: current_user.id, praise_or_tread: 1).present? ? true : false
|
|
|
|
|
|
|
|
|
|
# 实训的最大评测次数,这个值是为了优化查询,每次只取最新的最新一次评测的结果集
|
|
|
|
|
max_query_index = @game.query_index.to_i
|
|
|
|
|
|
|
|
|
|
# 统计评测时间
|
|
|
|
|
record_onsume_time = EvaluateRecord.where(game_id: @game.id).first.try(:consume_time)
|
|
|
|
|
|
|
|
|
|
# myshixun_manager判断用户是否有权限查看隐藏测试集(TPM管理员;平台认证的老师;花费金币查看者)
|
|
|
|
|
myshixun_manager = @identity < User::EDU_GAME_MANAGER
|
|
|
|
|
|
|
|
|
|
# 选择题和编程题公共部分
|
|
|
|
|
@base_date = {st: @st, discusses_count: discusses_count, game_count: game_count, myshixun: @myshixun,
|
|
|
|
|
challenge: game_challenge.attributes.except("answer"), game: @game.try(:attributes), shixun: @shixun.try(:attributes),
|
|
|
|
|
record_onsume_time: record_onsume_time, prev_game: prev_game, next_game: next_game,
|
|
|
|
|
praise_count: praise_count, user_praise: user_praise, time_limit: time_limit,
|
|
|
|
|
tomcat_url: edu_setting('cloud_tomcat_php'), is_teacher: is_teacher,
|
|
|
|
|
myshixun_manager: myshixun_manager}
|
|
|
|
|
if @shixun.vnc
|
|
|
|
|
begin
|
|
|
|
|
shixun_tomcat = edu_setting('cloud_bridge')
|
|
|
|
|
service_host = edu_setting('vnc_url')
|
|
|
|
|
uri = "#{shixun_tomcat}/bridge/vnc/getvnc"
|
|
|
|
|
params = {tpiID: @myshixun.id, :containers => "#{Base64.urlsafe_encode64(shixun_container_limit(@shixun))}"}
|
|
|
|
|
res = uri_post uri, params
|
|
|
|
|
if res && res['code'].to_i != 0
|
|
|
|
|
raise("实训云平台繁忙(繁忙等级:99)")
|
|
|
|
|
end
|
|
|
|
|
@vnc_url = "http://#{service_host}:#{res['port']}/vnc_lite.html?password=headless"
|
|
|
|
|
rescue Exception => e
|
|
|
|
|
Rails.logger.error(e.message)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# 区分选择题和编程题,st:0编程题;
|
|
|
|
|
if @st == 0
|
|
|
|
|
has_answer = game_challenge.challenge_answers.size == 0 ? false : true
|
|
|
|
|
game_challenge.answer = nil
|
|
|
|
|
mirror_name = @shixun.mirror_name
|
|
|
|
|
|
|
|
|
|
# 判断tpm是否修改了
|
|
|
|
|
begin
|
|
|
|
|
tpm_modified = @myshixun.repository_is_modified(@shixun.repo_path) # 判断TPM和TPI的版本库是否被改了
|
|
|
|
|
rescue
|
|
|
|
|
uid_logger("实训平台繁忙,繁忙等级(81)")
|
|
|
|
|
end
|
|
|
|
|
tpm_cases_modified = (game_challenge.modify_time != @game.modify_time ? true : false) # modify_time 决定TPM测试集是否有更新
|
|
|
|
|
|
|
|
|
|
@task_result = {tpm_modified: tpm_modified, tpm_cases_modified: tpm_cases_modified, mirror_name: mirror_name, has_answer: has_answer}
|
|
|
|
|
|
|
|
|
|
testset_detail max_query_index, game_challenge
|
|
|
|
|
else # 选择题类型的
|
|
|
|
|
# 该方法多个地方调用,比如show、评测
|
|
|
|
|
# 最后一个字段true表示只显示数据,false表示可能有添加数据
|
|
|
|
|
choose_container(game_challenge, @game, max_query_index)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# 查看效果
|
|
|
|
|
# todo : 这块代码有很大的改进空间
|
|
|
|
|
# todo : 中文排序问题
|
|
|
|
|
def picture_display
|
|
|
|
|
myshixun = Myshixun.find(@game.myshixun_id)
|
|
|
|
|
if myshixun.main_mirror.try(:type_name) == "Android"
|
|
|
|
|
@type = "qrcode"
|
|
|
|
|
workspace = @game.try(:picture_path)
|
|
|
|
|
game_challenge = @game.challenge
|
|
|
|
|
qr = RQRCode::QRCode.new("#{edu_setting('host_name')}/api/shixuns/download_file?file_name=#{workspace}/#{game_challenge.picture_path}/manual-ok.apk", :size => 12, :level => :h)
|
|
|
|
|
@qrcode_str = Base64.encode64( qr.to_img.resize(400,400).to_s )
|
|
|
|
|
|
|
|
|
|
else
|
|
|
|
|
@type = "image"
|
|
|
|
|
#conv = Iconv.new("GBK", "utf-8")
|
|
|
|
|
@game_challenge = @game.challenge
|
|
|
|
|
type = @game_challenge.show_type
|
|
|
|
|
workspace_path = @game.try(:picture_path)
|
|
|
|
|
@answer_path = "#{Rails.root}/#{workspace_path}/#{@game_challenge.expect_picture_path}"
|
|
|
|
|
@user_path = "#{Rails.root}/#{workspace_path}/#{@game_challenge.picture_path}"
|
|
|
|
|
@original_path = "#{Rails.root}/#{workspace_path}/#{@game_challenge.original_picture_path}"
|
|
|
|
|
|
|
|
|
|
@answer_picture = @game_challenge.expect_picture_path.nil? ? [] : get_dir_filename(@answer_path, type, @game.id)
|
|
|
|
|
#@answer_picture = @answer_picture.sort {|x, y| conv.iconv(x) <=> conv.iconv(y)} if @answer_picture.present?
|
|
|
|
|
|
|
|
|
|
@user_picture = @game_challenge.picture_path.nil? ? [] : get_dir_filename(@user_path, type, @game.id)
|
|
|
|
|
#@user_picture = @user_picture.sort {|x, y| conv.iconv(x) <=> conv.iconv(y)}
|
|
|
|
|
if @game_challenge.original_picture_path.blank?
|
|
|
|
|
@orignal_picture = nil
|
|
|
|
|
else
|
|
|
|
|
@orignal_picture = @game_challenge.original_picture_path.nil? ? [] : get_dir_filename(@original_path, type, @game.id)
|
|
|
|
|
#@orignal_picture = @orignal_picture.sort {|x, y| conv.iconv(x) <=> conv.iconv(y)}
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# 同步更新最新代码
|
|
|
|
|
# 同步完成后,需要更新myshixun的commit_id为实训的commit最新值
|
|
|
|
|
# --------------------------
|
|
|
|
|
# 新思路,有冲突则则重置,没有冲突直接pull
|
|
|
|
|
# identifier为password---
|
|
|
|
|
# --------------------------
|
|
|
|
|
# todo: TPI弹框的立即更新
|
|
|
|
|
def sync_codes
|
|
|
|
|
shixun_tomcat = edu_setting('cloud_bridge')
|
|
|
|
|
begin
|
|
|
|
|
git_myshixun_url = repo_ip_url @myshixun.repo_path
|
|
|
|
|
git_shixun_url = repo_ip_url @myshixun.shixun.try(:repo_path)
|
|
|
|
|
git_myshixun_url = Base64.urlsafe_encode64(git_myshixun_url)
|
|
|
|
|
git_shixun_url = Base64.urlsafe_encode64(git_shixun_url)
|
|
|
|
|
# todo: identifier 是以前的密码,用来验证的,新版如果不需要,和中间层协调更改.
|
|
|
|
|
params = {tpiID: "#{@myshixun.try(:id)}", tpiGitURL: "#{git_myshixun_url}", tpmGitURL: "#{git_shixun_url}",
|
|
|
|
|
identifier: "xinhu1ji2qu3"}
|
|
|
|
|
uri = "#{shixun_tomcat}/bridge/game/resetTpmRepository"
|
|
|
|
|
res = uri_post uri, params
|
|
|
|
|
if (res && res['code'] != 0)
|
|
|
|
|
tip_exception("实训云平台繁忙(繁忙等级:95)")
|
|
|
|
|
end
|
|
|
|
|
shixun_new_commit = GitService.commits(repo_path: @myshixun.shixun.repo_path).first["id"]
|
|
|
|
|
@myshixun.update_attributes!(commit_id: shixun_new_commit, reset_time: @myshixun.shixun.try(:reset_time))
|
|
|
|
|
if @game.challenge.st == 0 && @game.challenge.path.present?
|
|
|
|
|
paths = @game.challenge.path.split(";")
|
|
|
|
|
paths.each do |path|
|
|
|
|
|
game_code_init @game.id, path.try(:strip)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
@path = @game.challenge.path
|
|
|
|
|
# 更新完成后,弹框则隐藏不再提示
|
|
|
|
|
@myshixun.update_column(:system_tip, false)
|
|
|
|
|
rescue Exception => e
|
|
|
|
|
tip_exception("立即更新代码失败!#{e.message}")
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
## 给关卡打星星
|
|
|
|
|
def star
|
|
|
|
|
shixun = Shixun.select([:id, :averge_star, :status]).where(id: params[:shixun_id]).first
|
|
|
|
|
grades = Grade.where(user_id: current_user.id, container_id: @game.id, container_type: 'Star')
|
|
|
|
|
if grades.exists?
|
|
|
|
|
tip_exception("您已经评价过该实训")
|
|
|
|
|
else
|
|
|
|
|
@game.update_column(:star, params[:star].to_i)
|
|
|
|
|
# 更新实训平均星星数值
|
|
|
|
|
averge_star = Game.find_by_sql("select ifnull(sum(g.star),0)/ifnull(count(*),1) as averge_star from (games g left join
|
|
|
|
|
(myshixuns m join shixuns s on s.id = m.shixun_id) on m.id = g.myshixun_id)
|
|
|
|
|
where star != 0 and s.id = #{shixun.id}").first.try(:averge_star)
|
|
|
|
|
averge_star = averge_star.to_f || 5
|
|
|
|
|
shixun.update_column(:averge_star, averge_star.round(1))
|
|
|
|
|
# 随机生成10-100金币作为奖励
|
|
|
|
|
@gold = 0
|
|
|
|
|
# 加积分只针对已发布的实训
|
|
|
|
|
if shixun.status >= 2
|
|
|
|
|
@gold = rand(10..100)
|
|
|
|
|
|
|
|
|
|
RewardGradeService.call(current_user, container_id: @game.id, container_type: 'Star', score: @gold)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
## 代码文件目录结构
|
|
|
|
|
def git_entries
|
|
|
|
|
gpid = params[:gpid]
|
|
|
|
|
@path = params[:path].try(:strip)
|
|
|
|
|
rev = params[:rev] ? params[:rev] : "master"
|
|
|
|
|
@trees = @g.trees(gpid, path: @path, rev: rev)
|
|
|
|
|
unless @trees.count
|
|
|
|
|
tip_exception("版本库异常")
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
## 是否可以查看答案,如果是管理员或者系统认证的老师则直接查看,不需要弹框
|
|
|
|
|
def answer
|
|
|
|
|
challenge = Challenge.select([:answer, :id, :score, :st]).find(@game.challenge_id)
|
|
|
|
|
# 这几种情况可以直接查看答案的:实训未发布;当前用户为实训管理员;已经查看过答案;平台认证的老师;
|
|
|
|
|
@allowed = @shixun.status < 2 || @game.answer_open == 1 || current_user.shixun_identity(@shixun) <= User::EDU_CERTIFICATION_TEACHER
|
|
|
|
|
uid_logger("-- is manager #{current_user.manager_of_shixun?(@shixun)}")
|
|
|
|
|
|
|
|
|
|
@result = challenge.st == 0 ? challenge.try(:answer) : challenge.choose_answer
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# 获取答案
|
|
|
|
|
# GET: /tasks/:identifier/get_answer_info
|
|
|
|
|
# 0 直接查看答案, 1 查看答案弹框, 2 答案详情弹框
|
|
|
|
|
def get_answer_info
|
|
|
|
|
@challenge = @game.challenge
|
|
|
|
|
if @challenge.st == 0
|
|
|
|
|
@challenge_answers = @challenge.challenge_answers
|
|
|
|
|
else
|
|
|
|
|
result = []
|
|
|
|
|
@challenge.challenge_chooses.each do |choose|
|
|
|
|
|
result << {:position => choose.position, :answer => (choose.answer.blank? ? choose.standard_answer : choose.answer)}
|
|
|
|
|
end
|
|
|
|
|
@challenge_answers = result
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 平台已认证的老师需要控制
|
|
|
|
|
@power = (@identity < User::EDU_GAME_MANAGER)
|
|
|
|
|
if !@power
|
|
|
|
|
if @challenge_answers.size == 0
|
|
|
|
|
tip_exception("无答案可以查看")
|
|
|
|
|
elsif @challenge_answers.size == 1
|
|
|
|
|
# 未看答案,提示弹框
|
|
|
|
|
if @game.answer_open == 0
|
|
|
|
|
tip_exception(1, {answer_id: @challenge_answers.first.id, answer_score:@challenge_answers.first.score})
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
if @game.answer_open == 0
|
|
|
|
|
tip_exception(2, @challenge_answers.map{|a| {answer_id: a.id, answer_name: a.name, answer_score:a.score}})
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# 解锁答案
|
|
|
|
|
# GET: /tasks/:identifier/get_answer_info?answer_id=?
|
|
|
|
|
def unlock_answer
|
|
|
|
|
@answer = ChallengeAnswer.find(params[:answer_id])
|
|
|
|
|
challenge = @answer.challenge
|
|
|
|
|
# 解锁需要本层级的答案是否需要扣分
|
|
|
|
|
points = challenge.challenge_answers.where(level: @game.answer_open+1..@answer.level).sum(:score)
|
|
|
|
|
deduct_score = ((points / 100.0) * challenge.score).to_i
|
|
|
|
|
uid_logger("############金币数目: #{current_user.grade}")
|
|
|
|
|
unless current_user.grade.to_i - deduct_score > 0
|
|
|
|
|
tip_exception("您没有足够的金币")
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
ActiveRecord::Base.transaction do
|
|
|
|
|
begin
|
|
|
|
|
# 积分消耗情况记录
|
|
|
|
|
score = challenge.st.zero? ? -deduct_score : -challenge.choose_score.to_i
|
|
|
|
|
RewardGradeService.call(current_user, container_id: @answer.id, container_type: 'Answer', score: score)
|
|
|
|
|
|
|
|
|
|
# 通关查看答案 不扣 得分
|
|
|
|
|
if @game.status == 2
|
|
|
|
|
@game.update_attributes!(:answer_open => @answer.level)
|
|
|
|
|
else
|
|
|
|
|
# 扣除总分计算
|
|
|
|
|
answer_deduction = challenge.challenge_answers.where("level <= #{@answer.level}").sum(:score)
|
|
|
|
|
@game.update_attributes!(:answer_open => @answer.level, :answer_deduction => answer_deduction)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
rescue Exception => e
|
|
|
|
|
uid_logger_error("#######金币扣除异常: #{e.message}")
|
|
|
|
|
raise ActiveRecord::Rollback
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# 查看答案需要扣取金币
|
|
|
|
|
# 必须保证用户的金币数大于关卡的金币数
|
|
|
|
|
def answer_grade
|
|
|
|
|
challenge = Challenge.select([:answer, :id, :score, :st]).find(@game.challenge_id)
|
|
|
|
|
challenge_score = challenge.try(:score)
|
|
|
|
|
final_score = @game.final_score
|
|
|
|
|
@allowed_viewed = current_user.grade.to_i - challenge_score > 0
|
|
|
|
|
unless @allowed_viewed
|
|
|
|
|
tip_exception("您没有足够的金币")
|
|
|
|
|
end
|
|
|
|
|
ActiveRecord::Base.transaction do
|
|
|
|
|
begin
|
|
|
|
|
if @game.answer_open == 0 # 如果这是第一次查看答案
|
|
|
|
|
if challenge.st == 0
|
|
|
|
|
@final_score = final_score - challenge_score
|
|
|
|
|
# 积分消耗情况记录
|
|
|
|
|
RewardGradeService.call(
|
|
|
|
|
current_user,
|
|
|
|
|
container_id: @game.id,
|
|
|
|
|
container_type: 'Answer',
|
|
|
|
|
score: -challenge_score
|
|
|
|
|
)
|
|
|
|
|
else
|
|
|
|
|
@final_score = final_score - challenge.choose_score.to_i
|
|
|
|
|
# 之所以不用final_score是因为过关后查看答案的final_score为0,但是记录需要记录扣除的分数
|
|
|
|
|
RewardGradeService.call(
|
|
|
|
|
current_user,
|
|
|
|
|
container_id: @game.id,
|
|
|
|
|
container_type: 'Answer',
|
|
|
|
|
score: -challenge.choose_score.to_i)
|
|
|
|
|
end
|
|
|
|
|
@game.update_attributes!(:answer_open => true, :final_score => final_score)
|
|
|
|
|
end
|
|
|
|
|
if challenge.st == 0
|
|
|
|
|
@answer = challenge.try(:answer)
|
|
|
|
|
else
|
|
|
|
|
@answer = challenge.choose_answer
|
|
|
|
|
end
|
|
|
|
|
@answer =
|
|
|
|
|
# 更新当前用户的总金币数
|
|
|
|
|
@grade = User.where(:id => @game.user_id).pluck(:grade).first
|
|
|
|
|
rescue Exception => e
|
|
|
|
|
uid_logger_error("#######奖励金币异常: #{e.message}")
|
|
|
|
|
raise ActiveRecord::Rollback
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# 查看隐藏测试集
|
|
|
|
|
# REDO:有漏洞,通过game详情可以看到隐藏的测试集
|
|
|
|
|
def check_test_sets
|
|
|
|
|
challenge = Challenge.select([:id, :score]).find(@game.challenge_id)
|
|
|
|
|
user_grade = current_user.grade
|
|
|
|
|
@minus_grade = challenge.score * 5
|
|
|
|
|
@allowed_viewed = user_grade >= @minus_grade
|
|
|
|
|
if @allowed_viewed
|
|
|
|
|
current_user.update_attribute(:grade, user_grade - @minus_grade)
|
|
|
|
|
@game.update_attribute(:test_sets_view, true)
|
|
|
|
|
# 扣分记录
|
|
|
|
|
Grade.create(:user_id => current_user.id, :container_id => @game.id, :score => -@minus_grade, :container_type => "testSet")
|
|
|
|
|
max_query_index = @game.query_index.to_i
|
|
|
|
|
testset_detail max_query_index, challenge
|
|
|
|
|
else
|
|
|
|
|
tip_exception(-1, "本操作需要扣除#{ @minus_grade }金币,您的金币不够了")
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# # 文件更新;数据评测记录
|
|
|
|
|
# # 生成重新评测认证码
|
|
|
|
|
# # content_modified:0 表示文件没有更新;content_modified:1 表示文件有更新
|
|
|
|
|
# def file_update
|
|
|
|
|
# path = params[:path].strip unless params[:path].blank?
|
|
|
|
|
# myshixun = @game.myshixun
|
|
|
|
|
# rev = params[:rev] ? params[:rev] : "master"
|
|
|
|
|
# @content_modified = 0
|
|
|
|
|
# # params[:evaluate] 实训评测时更新必须给的参数,需要依据该参数做性能统计,其它类型的更新可以跳过
|
|
|
|
|
# # 自动保存的时候evaluate为0;点评测的时候为1
|
|
|
|
|
# if params[:evaluate] == 1
|
|
|
|
|
# record = EvaluateRecord.create!(:user_id => current_user.id, :shixun_id => myshixun.shixun_id, :game_id => @game.id)
|
|
|
|
|
# uid_logger("-- game is #{@game.id}, record id is #{record.id}, time is **** #{Time.now.strftime("%Y-%m-%d %H:%M:%S.%L")}")
|
|
|
|
|
# student_work_time = format("%.3f", (Time.now.to_f - record.created_at.to_f)).to_f
|
|
|
|
|
# record.update_attributes!(:student_work => student_work_time)
|
|
|
|
|
# end
|
|
|
|
|
# # 远程版本库文件内容
|
|
|
|
|
# last_content = GitService.file_content(repo_path: @repo_path, path: path)["content"]
|
|
|
|
|
# last_content = tran_base64_decode64(last_content)
|
|
|
|
|
#
|
|
|
|
|
# content = if @myshixun.mirror_name.select{|a| a.include?("MachineLearning") || a.include?("Python")}.present? && params[:content].present?
|
|
|
|
|
# params[:content].gsub(/\t/, ' ')
|
|
|
|
|
# else
|
|
|
|
|
# params[:content]
|
|
|
|
|
# end
|
|
|
|
|
# if content != last_content
|
|
|
|
|
# @content_modified = 1
|
|
|
|
|
# code_file = @g.edit_file(myshixun.gpid, current_user.login, :content => content, :file_path => path,
|
|
|
|
|
# :branch_name => rev, :commit_message => params[:evaluate] == 0 ? "auto commit" : "task commit")
|
|
|
|
|
# uid_logger("-- file update #{code_file}")
|
|
|
|
|
# # REDO:更新失败的处理
|
|
|
|
|
# raise("文件更新失败") unless code_file
|
|
|
|
|
# end
|
|
|
|
|
#
|
|
|
|
|
# if record.present?
|
|
|
|
|
# consume_time = format("%.3f", (Time.now.to_f - record.created_at.to_f)).to_f
|
|
|
|
|
# record.update_attributes!(:file_update => consume_time)
|
|
|
|
|
# end
|
|
|
|
|
#
|
|
|
|
|
# # status为2说明是重新评测
|
|
|
|
|
# if @game.status == 2
|
|
|
|
|
# code = CODES.sample(8).join
|
|
|
|
|
# @resubmit = "#{code}_#{@myshixun.id}"
|
|
|
|
|
# end
|
|
|
|
|
#
|
|
|
|
|
# if content != last_content && code_file.blank?
|
|
|
|
|
# raise("实训平台繁忙(繁忙等级:81),请稍后刷新并重试")
|
|
|
|
|
# end
|
|
|
|
|
# rescue Exception => e
|
|
|
|
|
# uid_logger("-- file update failed #{e.message}")
|
|
|
|
|
# raise Educoder::TipException.new("#{e.message}")
|
|
|
|
|
# end
|
|
|
|
|
|
|
|
|
|
# 恢复初始代码
|
|
|
|
|
# 注意path为当前打开文件的path
|
|
|
|
|
def reset_original_code
|
|
|
|
|
path = params[:path]
|
|
|
|
|
# 恢复初始代码应该找tpm的版本库
|
|
|
|
|
repo_path = @myshixun.shixun.repo_path
|
|
|
|
|
|
|
|
|
|
@content = git_fle_content(repo_path, path)
|
|
|
|
|
tip_exception("初始代码为空,代码重置失败") if @content.nil?
|
|
|
|
|
|
|
|
|
|
# 将tpm的代码内容同步更新到tpi
|
|
|
|
|
update_file_content(@content, @myshixun.repo_path, path, current_user.mail, current_user.full_name, "reset_original_code")
|
|
|
|
|
rescue Exception => e
|
|
|
|
|
uid_logger_error("#{e.message}")
|
|
|
|
|
tip_exception("初始化代码失败")
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# 加载上次通过的代码
|
|
|
|
|
def reset_passed_code
|
|
|
|
|
path = params[:path]
|
|
|
|
|
game_code = GameCode.where(:game_id => @game.try(:id), :path => path).first
|
|
|
|
|
if game_code.present?
|
|
|
|
|
content = game_code.try(:new_code)
|
|
|
|
|
# @content = if @myshixun.mirror_name.select{|a| a.include?("MachineLearning") || a.include?("Python")}.present? && content.present?
|
|
|
|
|
# content.gsub(/\t/, ' ')
|
|
|
|
|
# else
|
|
|
|
|
# content
|
|
|
|
|
# end
|
|
|
|
|
update_file_content(content, @myshixun.repo_path, path, current_user.mail, current_user.full_name, "game passed reset")
|
|
|
|
|
else
|
|
|
|
|
tip_exception("代码重置失败,代码为空")
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# 获取版本库文件内容
|
|
|
|
|
# 注:如果本身path传错,内容肯定也为空;fork成功后,可能短时间内也获取不到版本库内容
|
|
|
|
|
# params[:status] 1: 目录树点击的请求 0:正常自动加载
|
|
|
|
|
# 返回参数status : -3 需要重试,带retry参数;-1 给出提示
|
|
|
|
|
def rep_content
|
|
|
|
|
challenge_path = @game.challenge.try(:path)
|
|
|
|
|
if challenge_path.blank?
|
|
|
|
|
tip_exception("代码获取异常,请检查实训模板的评测设置是否正确")
|
|
|
|
|
end
|
|
|
|
|
path = @game.challenge.try(:path).split(";")[0].strip()
|
|
|
|
|
path = params[:path] || path
|
|
|
|
|
status = params[:status].to_i
|
|
|
|
|
path = path.try(:strip)
|
|
|
|
|
uid_logger("--rep_content: path is #{path}")
|
|
|
|
|
begin
|
|
|
|
|
@content = git_fle_content(@myshixun.repo_path, path) || ""
|
|
|
|
|
rescue
|
|
|
|
|
if params[:retry].to_i == 1
|
|
|
|
|
begin
|
|
|
|
|
begin
|
|
|
|
|
# 检测TPM对应的路径代码是否正常
|
|
|
|
|
git_fle_content(@myshixun.shixun.repo_path, path)
|
|
|
|
|
rescue Exception => e
|
|
|
|
|
uid_logger_error("#{e.message}")
|
|
|
|
|
# 如果已发布的TPM实训也不能获取到内容,那么肯定是版本库异常了
|
|
|
|
|
if @myshixun.shixun.try(:status) < 2
|
|
|
|
|
tip_exception("代码获取异常,请检查实训模板的评测设置是否正确")
|
|
|
|
|
else
|
|
|
|
|
tip_exception("代码获取异常,请联系系统管理员")
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# 如果模板没有问题,则通过中间层检测实训仓库是否异常
|
|
|
|
|
# 监测版本库HEAD是否存在,不存在则取最新的HEAD
|
|
|
|
|
gitUrl = repo_url @myshixun.repo_path
|
|
|
|
|
gitUrl = Base64.urlsafe_encode64(gitUrl)
|
|
|
|
|
shixun_tomcat = edu_setting('cloud_bridge')
|
|
|
|
|
rep_params = {:tpiID => "#{@myshixun.id}", :tpiGitURL => "#{gitUrl}"}
|
|
|
|
|
# 监测版本库HEAD是否存在,不存在则取最新的HEAD
|
|
|
|
|
uri = "#{shixun_tomcat}/bridge/game/check"
|
|
|
|
|
res = uri_post uri, rep_params
|
|
|
|
|
# res值:0 表示正常;-1表示有错误;-2表示代码版本库没了
|
|
|
|
|
if status == 0 && res && (res['code'] == -2 || res['code'] == -1)
|
|
|
|
|
# 删除不需要的仓库
|
|
|
|
|
begin
|
|
|
|
|
GitService.delete_repository(repo_path: @myshixun.repo_path)
|
|
|
|
|
rescue Exception => e
|
|
|
|
|
uid_logger_error("#{e.message}")
|
|
|
|
|
end
|
|
|
|
|
# fork一个新的仓库
|
|
|
|
|
project_fork(@myshixun, @shixun.repo_path, current_user.login)
|
|
|
|
|
end
|
|
|
|
|
rescue Exception => e
|
|
|
|
|
uid_logger_error(e.message)
|
|
|
|
|
# 报错继续retry
|
|
|
|
|
tip_exception(-3, "#{e.message}")
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
tip_exception(-3, "#{e.message}")
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# 编程题评测
|
|
|
|
|
def game_build
|
|
|
|
|
game_challenge = Challenge.select([:id, :position, :picture_path]).find(@game.challenge_id)
|
|
|
|
|
|
|
|
|
|
# 更新评测次数
|
|
|
|
|
@game.update_column(:evaluate_count, (@game.evaluate_count.to_i + 1))
|
|
|
|
|
# 清空代码评测信息
|
|
|
|
|
msg = @game.run_code_message
|
|
|
|
|
msg.update_attributes(:status => 0, :message => nil) if msg.present?
|
|
|
|
|
|
|
|
|
|
# 更新时间是为了TPM端显示的更新,退出实训及访问实训的时候会更新
|
|
|
|
|
@myshixun.update_column(:updated_at, Time.now)
|
|
|
|
|
|
|
|
|
|
gitUrl = repo_ip_url @myshixun.repo_path
|
|
|
|
|
logger.info("#############giturl: ##{gitUrl}")
|
|
|
|
|
gitUrl = Base64.urlsafe_encode64(gitUrl)
|
|
|
|
|
|
|
|
|
|
shixun_tomcat = edu_setting('cloud_bridge')
|
|
|
|
|
step = game_challenge.try(:position)
|
|
|
|
|
mirror_repository_limit = @shixun.mirror_repositories.where(main_type: 1).select(:resource_limit).try(:first).try(:resource_limit)
|
|
|
|
|
# mirror表中很很大的脚本字段,所以单独查询一个字段效果更好
|
|
|
|
|
resource_limit = "echo 'ulimit -f #{mirror_repository_limit}' >> /root/.bashrc ; source /root/.bashrc\n"
|
|
|
|
|
tpmScript = @shixun.evaluate_script.nil? ? "" : Base64.urlsafe_encode64((resource_limit + @shixun.evaluate_script).gsub("\r\n", "\n"))
|
|
|
|
|
|
|
|
|
|
# status为2已经通过关,是重新评测
|
|
|
|
|
if @game.status == 2
|
|
|
|
|
resubmit = params[:resubmit]
|
|
|
|
|
else
|
|
|
|
|
# 重新评测不影响已通关的实训状态;first为第一次评测,通过前端JS轮询获取
|
|
|
|
|
@game.update_attributes!(status: 1) if params[:first].to_i == 1
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
testSet = []
|
|
|
|
|
game_challenge.test_sets.each do |test_set|
|
|
|
|
|
input = test_set.input.nil? ? "" : test_set.input.gsub("\r\n", "\n")
|
|
|
|
|
output = test_set.output.nil? ? "" : test_set.output.gsub("\r\n", "\n")
|
|
|
|
|
test_cases = {:input => input, :output => output}
|
|
|
|
|
testSet << test_cases
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
testCases = Base64.urlsafe_encode64(testSet.to_json) unless testSet.blank?
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 注意:这个地方的参数写的时候不能换行
|
|
|
|
|
content_modified = params[:content_modified] # 决定文件内容是否有修改,有修改如果中间层pull没有更新,则轮询等待更新
|
|
|
|
|
br_params = {:tpiID => "#{@myshixun.id}", :tpiGitURL => "#{gitUrl}", :buildID => "#{@game.id}",
|
|
|
|
|
:instanceChallenge => "#{step}", :testCases => "#{testCases}", :resubmit => "#{resubmit}",
|
|
|
|
|
:times => params[:first].to_i, :podType => @shixun.webssh, :content_modified => content_modified,
|
|
|
|
|
:containers => "#{Base64.urlsafe_encode64(shixun_container_limit(@shixun))}",
|
|
|
|
|
:persistenceName => @shixun.identifier, :tpmScript => "#{tpmScript}",
|
|
|
|
|
:timeLimit => "#{@shixun.exec_time}", :isPublished => (@shixun.status < 2 ? 0 : 1) }
|
|
|
|
|
|
|
|
|
|
# 评测有文件输出的需要特殊传字段 path:表示文件存储的位置
|
|
|
|
|
br_params['file'] = Base64.urlsafe_encode64({path: "#{game_challenge.picture_path}"}.to_json) if game_challenge.picture_path.present?
|
|
|
|
|
|
|
|
|
|
# needPortMapping: web类型需要pod端口映射
|
|
|
|
|
br_params[:needPortMapping] = 8080 if @myshixun.mirror_name.include?("Web")
|
|
|
|
|
|
|
|
|
|
# 中间层交互
|
|
|
|
|
uri = "#{shixun_tomcat}/bridge/game/gameEvaluate"
|
|
|
|
|
res = interface_post uri, br_params, 502, "gameEvaluate failed"
|
|
|
|
|
|
|
|
|
|
@result = {status: 1, resubmit: resubmit, position: game_challenge.position, port: res['port'], had_done: @game.had_done}
|
|
|
|
|
rescue Exception => e
|
|
|
|
|
uid_logger("评测出错,详情:" + e.message)
|
|
|
|
|
@result = {status: -1, message: "实训云平台繁忙(繁忙等级:502),请稍后刷新并重试", position: game_challenge.position,
|
|
|
|
|
had_done: @game.had_done}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# 选择题评测
|
|
|
|
|
def choose_build
|
|
|
|
|
Rails.logger.error("#################{params}")
|
|
|
|
|
# 选择题如果通关了,则不让再评测
|
|
|
|
|
if @game.status == 2
|
|
|
|
|
raise Educoder::TipException.new("您已通过该关卡")
|
|
|
|
|
end
|
|
|
|
|
# 更新评测次数
|
|
|
|
|
@game.update_column(:evaluate_count, (@game.evaluate_count.to_i + 1))
|
|
|
|
|
|
|
|
|
|
game_challenge = Challenge.select([:id, :position]).find(@game.challenge_id)
|
|
|
|
|
user_answer = params[:answer]
|
|
|
|
|
challenge_chooses_count = user_answer.length
|
|
|
|
|
|
|
|
|
|
choose_correct_num = 0
|
|
|
|
|
score = 0
|
|
|
|
|
had_passed = true
|
|
|
|
|
test_sets = []
|
|
|
|
|
str = ""
|
|
|
|
|
game_challenge.challenge_chooses.includes(:challenge_tags).each_with_index do |choose, index|
|
|
|
|
|
# user_answer虽然是传的数组,但是可能存在多选择提的情况.
|
|
|
|
|
user_answer_tran = user_answer[index].size > 1 ? user_answer[index].split("").sort.join("") : user_answer[index]
|
|
|
|
|
standard_answer_tran = choose.standard_answer.size > 1 ? choose.standard_answer.split("").sort.join("") : choose.standard_answer
|
|
|
|
|
correct = (user_answer_tran == standard_answer_tran) ? true : false
|
|
|
|
|
if str.present?
|
|
|
|
|
str += ","
|
|
|
|
|
end
|
|
|
|
|
str += "('#{@game.id}', '#{choose.position}', '#{user_answer_tran}', '#{correct ? 1 : 0}', '#{@game.next_query_index}', '#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}', '#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}')"
|
|
|
|
|
# 只要有一题错误就不能通关
|
|
|
|
|
had_passed = false if !correct
|
|
|
|
|
choose_correct_num += 1 if correct
|
|
|
|
|
|
|
|
|
|
# 全部通关的时候,需要对所得的总分记录
|
|
|
|
|
# 总分的记录应该是根据每一题累加,如果该题正确,则加分
|
|
|
|
|
score += choose.score if correct
|
|
|
|
|
|
|
|
|
|
standard_answer = correct ? standard_answer_tran : -1
|
|
|
|
|
sin_test_set = {:result => correct,
|
|
|
|
|
:actual_output => user_answer_tran,
|
|
|
|
|
:standard_answer => standard_answer,
|
|
|
|
|
:position => choose.position}
|
|
|
|
|
test_sets << sin_test_set
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# 批量插入评测结果
|
|
|
|
|
uid_logger("#------------chooice score: #{score}")
|
|
|
|
|
sql = "INSERT INTO outputs (game_id, test_set_position, actual_output, result, query_index, created_at, updated_at) VALUES" + str
|
|
|
|
|
ActiveRecord::Base.connection.execute sql
|
|
|
|
|
|
|
|
|
|
# 没通关或者查看了答案通关的时候经验为0
|
|
|
|
|
# 通关但是查看了答案,评测的时候金币显示0(避免用户以为重复扣除),但是在关卡列表中金币显示负数
|
|
|
|
|
experience = 0
|
|
|
|
|
final_score = 0
|
|
|
|
|
# 如果本次答题全部正确,并且之前没有通关,则进行相应的奖励,每关只要有错题,则不进行任何奖励
|
|
|
|
|
# 注:扣除分数是在查看答案的时候扣除的
|
|
|
|
|
if had_passed && !@game.had_passed?
|
|
|
|
|
@game.update_attributes(:status => 2, :end_time => Time.now)
|
|
|
|
|
# TPM实训已发布并且没有查看答案
|
|
|
|
|
if @shixun.is_published? && @game.answer_open == 0
|
|
|
|
|
uid_logger("@@@@@@@@@@@@@@@@@chooice score: #{score}")
|
|
|
|
|
# 查看答案的时候处理final_scor和扣分记录
|
|
|
|
|
experience = score
|
|
|
|
|
reward_attrs = { container_id: @game.id, container_type: 'Game', score: score }
|
|
|
|
|
RewardGradeService.call(@myshixun.owner, reward_attrs)
|
|
|
|
|
@game.update_attribute(:final_score, score)
|
|
|
|
|
final_score = score
|
|
|
|
|
RewardExperienceService.call(@myshixun.owner, reward_attrs)
|
|
|
|
|
end
|
|
|
|
|
had_done = @game.had_done
|
|
|
|
|
@myshixun.update_attribute(:status, 1) if had_done == 1
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
grade = @myshixun.owner.try(:grade)
|
|
|
|
|
|
|
|
|
|
# 更新实训关联的作品分数 TODO: 更新作业需要等作业模块开了再打开
|
|
|
|
|
# update_myshixun_work_score myshixun
|
|
|
|
|
|
|
|
|
|
# 高性能取上一关、下一关
|
|
|
|
|
prev_game = @game.prev_of_current_game(@shixun.id, @game.myshixun_id, game_challenge.position)
|
|
|
|
|
next_game = @game.next_of_current_game(@shixun.id, @game.myshixun_id, game_challenge.position) if had_passed
|
|
|
|
|
|
|
|
|
|
# 高性能取上一关、下一关
|
|
|
|
|
#prev_game = Game.prev_identifier(@shixun.id, @game.myshixun_id, game_challenge.position)
|
|
|
|
|
#next_game = Game.next_game(@shixun.id, @game.myshixun_id, game_challenge.position)
|
|
|
|
|
|
|
|
|
|
@result = {grade: grade,
|
|
|
|
|
gold: final_score,
|
|
|
|
|
experience: experience,
|
|
|
|
|
challenge_chooses_count: challenge_chooses_count,
|
|
|
|
|
choose_correct_num: choose_correct_num,
|
|
|
|
|
test_sets: test_sets,
|
|
|
|
|
prev_game: prev_game,
|
|
|
|
|
next_game: next_game}
|
|
|
|
|
rescue Exception => e
|
|
|
|
|
uid_logger("choose build failed #{e.message}")
|
|
|
|
|
@result = [status: -1, contents: "#{e.message}"]
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# 轮询获取状态
|
|
|
|
|
# resubmit是在file_update中生成的,从game_build中传入的
|
|
|
|
|
def game_status
|
|
|
|
|
resubmit_identifier = @game.resubmit_identifier
|
|
|
|
|
# 如果没有超时并且正在评测中
|
|
|
|
|
# 判断评测中的状态有两种:1、如果之前没有通关的,只需判断status为1即可;如果通过关,则判断game的resubmit_identifier是否更新
|
|
|
|
|
uid_logger("################game_status: #{@game.status}")
|
|
|
|
|
uid_logger("################params[:resubmit]: #{params[:resubmit]}")
|
|
|
|
|
uid_logger("################resubmit_identifier: #{resubmit_identifier}")
|
|
|
|
|
uid_logger("################time_out: #{params[:time_out]}")
|
|
|
|
|
if (params[:time_out] == "false") && ((params[:resubmit].blank? && @game.status == 1) || (params[:resubmit].present? &&
|
|
|
|
|
(params[:resubmit] != resubmit_identifier)))
|
|
|
|
|
# 代码评测的信息
|
|
|
|
|
running_code_status = @game.run_code_message.try(:status)
|
|
|
|
|
running_code_message = @game.run_code_message.try(:message)
|
|
|
|
|
render :json => { running_code_status: running_code_status, running_code_message: running_code_message }
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
uid_logger("##### resubmit_identifier is #{resubmit_identifier}")
|
|
|
|
|
port = params[:port]
|
|
|
|
|
score = 0
|
|
|
|
|
experience = 0
|
|
|
|
|
game_status = @game.status
|
|
|
|
|
had_done = @game.had_done
|
|
|
|
|
game_challenge = Challenge.select([:id, :score, :position, :shixun_id, :web_route]).find(@game.challenge_id)
|
|
|
|
|
|
|
|
|
|
if params[:resubmit].blank? # 非重新评测
|
|
|
|
|
if game_status == 2 # 通关
|
|
|
|
|
if @shixun.status > 1
|
|
|
|
|
score = @game.final_score # 查看答案的时候有对最终获得金币进行处理
|
|
|
|
|
experience = @game.final_score
|
|
|
|
|
else
|
|
|
|
|
score = 0
|
|
|
|
|
experience = 0
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
else # 重新评测
|
|
|
|
|
# 如果满足前面的条件,进入此处只可能是结果已返回并存入了数据库
|
|
|
|
|
if params[:resubmit] == resubmit_identifier # 本次重新评测结果已经返回并存入数据库
|
|
|
|
|
game_status = (@game.retry_status == 2 ? 2 : 0) # retry_status是判断重新评测的通关情况。2表示通关
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# 实训的最大评测次数,这个值是为了优化查询,每次只取最新的最新一次评测的结果集
|
|
|
|
|
max_query_index = @game.query_index
|
|
|
|
|
#max_query_index = @game.outputs.first.try(:query_index)
|
|
|
|
|
# 区分评测过未评测过,未评测过按需求取数据
|
|
|
|
|
testset_detail max_query_index.to_i, game_challenge
|
|
|
|
|
|
|
|
|
|
# 处理生成图片类型文件
|
|
|
|
|
picture = (@game.picture_path.nil? ? 0 : @game.id)
|
|
|
|
|
# 针对web类型的实训
|
|
|
|
|
web_route = game_challenge.try(:web_route)
|
|
|
|
|
mirror_name = @shixun.mirror_name
|
|
|
|
|
|
|
|
|
|
# 轮询结束,更新评测统计耗时
|
|
|
|
|
if game_status == 0 || game_status == 2
|
|
|
|
|
e_record = EvaluateRecord.where(:game_id => @game.id).first
|
|
|
|
|
if e_record
|
|
|
|
|
front_js = format("%.3f", (Time.now.to_f - e_record.try(:updated_at).to_f)).to_f
|
|
|
|
|
consume_time = format("%.3f", (Time.now - e_record.created_at)).to_f
|
|
|
|
|
e_record.update_attributes(:consume_time => consume_time, :front_js => front_js)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
uid_logger("game is is #{@game.id}, record id is #{e_record.try(:id)}, time is**** #{Time.now.strftime("%Y-%m-%d %H:%M:%S.%L")}")
|
|
|
|
|
# 记录前端总耗时
|
|
|
|
|
record_consume_time = EvaluateRecord.where(:game_id => @game.id).first.try(:consume_time)
|
|
|
|
|
# 实训制作者当前拥有的金币
|
|
|
|
|
grade = User.where(:id => @game.user_id).pluck(:grade).first
|
|
|
|
|
|
|
|
|
|
# 高性能取上一关、下一关
|
|
|
|
|
# 上一关、下一关
|
|
|
|
|
prev_game = @game.prev_of_current_game(@shixun.id, @game.myshixun_id, game_challenge.position)
|
|
|
|
|
next_game = @game.next_of_current_game(@shixun.id, @game.myshixun_id, game_challenge.position) if game_status == 2
|
|
|
|
|
@base_date = {grade: grade, gold: score, experience: experience, status: game_status, had_done: had_done,
|
|
|
|
|
position: game_challenge.position, port: port, record_consume_time: record_consume_time,
|
|
|
|
|
mirror_name: mirror_name, picture: picture, web_route: web_route, star: @game.star,
|
|
|
|
|
next_game: next_game, prev_game: prev_game}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# 记录实训花费的时间
|
|
|
|
|
# REDO:需要添加详细的说明
|
|
|
|
|
def cost_time
|
|
|
|
|
cost_time = params[:time].to_i
|
|
|
|
|
@game.update_attribute(:cost_time, cost_time)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# 同步challenge的更新时间
|
|
|
|
|
def sync_modify_time
|
|
|
|
|
modify_time = Challenge.where(:id => @game.challenge_id).pluck(:modify_time).first
|
|
|
|
|
@game.update_column(:modify_time, modify_time)
|
|
|
|
|
sucess_status
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# tpi弹框状态更新;true则不再显示;false每次刷新显示
|
|
|
|
|
def system_update
|
|
|
|
|
myshixun = Myshixun.find(params[:myshixun_id])
|
|
|
|
|
myshixun.update_attribute(:system_tip, true)
|
|
|
|
|
sucess_status
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# 关闭webssh
|
|
|
|
|
def close_webssh
|
|
|
|
|
myshixun_id = @game.myshixun_id
|
|
|
|
|
digest = @game.identifier + edu_setting('bridge_secret_key')
|
|
|
|
|
digest_key = Digest::SHA1.hexdigest("#{digest}")
|
|
|
|
|
begin
|
|
|
|
|
shixun_tomcat = edu_setting('cloud_bridge')
|
|
|
|
|
uri = "#{shixun_tomcat}/bridge/webssh/delete"
|
|
|
|
|
Rails.logger.info("#{current_user} => cloese_webssh digest is #{digest}")
|
|
|
|
|
params = {:tpiID => myshixun_id, :digestKey => digest_key, :identifier => @game.identifier}
|
|
|
|
|
res = uri_post uri, params
|
|
|
|
|
if res && res['code'].to_i != 0
|
|
|
|
|
raise("实训云平台繁忙(繁忙等级:110)")
|
|
|
|
|
end
|
|
|
|
|
rescue Exception => e
|
|
|
|
|
Rails.logger.error(e)
|
|
|
|
|
tip_exception("实训云平台繁忙, 关闭失败!")
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# tpi对于实训关卡的点赞或取消点赞
|
|
|
|
|
def plus_or_cancel_praise
|
|
|
|
|
challenge = @game.challenge
|
|
|
|
|
pt = PraiseTread.where(:praise_tread_object_id => challenge.id, :praise_tread_object_type => 'Challenge',
|
|
|
|
|
:user_id => current_user, :praise_or_tread => 1).first
|
|
|
|
|
# 如果当前用户已赞过,则不能重复赞
|
|
|
|
|
if pt.blank?
|
|
|
|
|
PraiseTread.create!(:praise_tread_object_id => challenge.id, :praise_tread_object_type => 'Challenge',
|
|
|
|
|
:user_id => current_user.id, :praise_or_tread => 1) if pt.blank?
|
|
|
|
|
@praise = true
|
|
|
|
|
else
|
|
|
|
|
pt.destroy if pt.present? # 如果已赞过,则删掉这条赞(取消);如果没赞过,则为非法请求不处理
|
|
|
|
|
@praise = false
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
@praise_count = PraiseTread.where(:praise_tread_object_id => challenge.id, :praise_tread_object_type => 'Challenge',
|
|
|
|
|
:praise_or_tread => 1).count
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
|
|
# 评测测试机封装
|
|
|
|
|
def testset_detail max_query_index, challenge
|
|
|
|
|
# 是否允许查看隐藏的测试集,以前的power
|
|
|
|
|
@allowed_hidden_testset = @identity < User::EDU_GAME_MANAGER || @game.test_sets_view #解锁的用户
|
|
|
|
|
|
|
|
|
|
if max_query_index > 0
|
|
|
|
|
uid_logger("max_query_index is #{max_query_index} game id is #{@game.id}, challenge_id is #{challenge.id}")
|
|
|
|
|
@qurey_test_sets = TestSet.find_by_sql("SELECT o.code, o.actual_output, o.out_put, o.result, o.test_set_position,
|
|
|
|
|
o.query_index, t.is_public, t.input, t.output, o.compile_success FROM outputs o, games g, challenges c,
|
|
|
|
|
test_sets t where g.id=#{@game.id} and c.id=#{challenge.id} and o.query_index=#{max_query_index}
|
|
|
|
|
and g.id = o.game_id and c.id= g.challenge_id and t.challenge_id = c.id and
|
|
|
|
|
t.position =o.test_set_position order by o.query_index
|
|
|
|
|
")
|
|
|
|
|
@test_sets_count = @qurey_test_sets.count
|
|
|
|
|
# 错误的测试集总数
|
|
|
|
|
@sets_error_count = 0
|
|
|
|
|
@qurey_test_sets.each do |set|
|
|
|
|
|
@sets_error_count += 1 unless set.result
|
|
|
|
|
end
|
|
|
|
|
@last_compile_output = @qurey_test_sets.first['out_put'].gsub(/\n/, '<br/>').gsub(/\t/, " \; \; \; \; \; \; \; \;") if @qurey_test_sets.first['out_put'].present?
|
|
|
|
|
else
|
|
|
|
|
@qurey_test_sets = TestSet.find_by_sql("SELECT t.is_public, t.input, t.output, t.position
|
|
|
|
|
FROM test_sets t where t.challenge_id = #{challenge.id}")
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# 实训选择题需要局部刷新或者显示的部分
|
|
|
|
|
def choose_container game_challenge, game, max_query_index
|
|
|
|
|
# category 1: 单选题,其它的多选题(目前只有两种)
|
|
|
|
|
challenge_chooses = game_challenge.challenge_chooses.includes(:challenge_questions)
|
|
|
|
|
test_sets = []
|
|
|
|
|
@chooses = []
|
|
|
|
|
|
|
|
|
|
# 选择题测试集统计
|
|
|
|
|
challenge_chooses_count = challenge_chooses.count
|
|
|
|
|
choose_correct_num = game.choose_correct_num(max_query_index)
|
|
|
|
|
game_outputs = game.outputs.where(:query_index => max_query_index)
|
|
|
|
|
|
|
|
|
|
# 判断用户是否有提交
|
|
|
|
|
had_submmit = game_outputs.present?
|
|
|
|
|
|
|
|
|
|
# 判断选择题是否写了标准答案
|
|
|
|
|
has_answer = []
|
|
|
|
|
challenge_chooses.each do |choose|
|
|
|
|
|
challenge_question = []
|
|
|
|
|
output = game_outputs.select{|game_output| game_output.test_set_position == choose.position}[0] unless game_outputs.blank?
|
|
|
|
|
|
|
|
|
|
category = choose.category
|
|
|
|
|
subject = choose.subject
|
|
|
|
|
choose.challenge_questions.each do |question|
|
|
|
|
|
position = question.position
|
|
|
|
|
option_name = question.option_name
|
|
|
|
|
challenge_question << {:positon => position, :option_name => option_name}
|
|
|
|
|
end
|
|
|
|
|
# actual_output为空表示暂时没有评测答题,不允许查看
|
|
|
|
|
actual_output = output.try(:actual_output).try(:strip)
|
|
|
|
|
has_answer << choose.answer if choose.answer.present?
|
|
|
|
|
# 标准答案处理,错误的不让用户查看,用-1替代
|
|
|
|
|
standard_answer = (actual_output.blank? || !output.try(:result)) ? -1 : choose.standard_answer
|
|
|
|
|
result = output.try(:result)
|
|
|
|
|
sin_test_set = {:result => result, :actual_output => actual_output, :standard_answer => standard_answer,
|
|
|
|
|
:position => choose.position}
|
|
|
|
|
|
|
|
|
|
sin_choose = {:category => category, :subject => subject, :challenge_question => challenge_question}
|
|
|
|
|
@chooses << sin_choose
|
|
|
|
|
test_sets << sin_test_set
|
|
|
|
|
end
|
|
|
|
|
@has_answer = has_answer.present?
|
|
|
|
|
@choose_test_cases = {:had_submmit => had_submmit, :challenge_chooses_count => challenge_chooses_count,
|
|
|
|
|
:choose_correct_num => choose_correct_num, :test_sets => test_sets}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def find_game
|
|
|
|
|
@game = Game.find_by_identifier(params[:identifier])
|
|
|
|
|
if @game.blank?
|
|
|
|
|
normal_status(404, "...")
|
|
|
|
|
return
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
@myshixun = @game.myshixun
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def find_shixun
|
|
|
|
|
@shixun = Shixun.find(@myshixun.shixun_id)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# http://localhost:3000/tasks/hcie39pw2bjn
|
|
|
|
|
# 可以访问条件:学员本身;管理员;TPM制作者
|
|
|
|
|
def allowed
|
|
|
|
|
@identity = current_user.game_identity(@game)
|
|
|
|
|
raise Educoder::TipException.new(403, "..") if @identity > User::EDU_GAME_MANAGER
|
|
|
|
|
end
|
|
|
|
|
end
|