educoder/app/controllers/games_controller.rb

418 lines
17 KiB
Ruby
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#coding=utf-8
class GamesController < ApplicationController
layout "base_myshixun"
skip_before_filter :verify_authenticity_token, :only => [:file_update]
before_filter :find_myshixun, :only => [:index]
before_filter :find_game, :only => [:show, :game_build, :entry,:next_step, :outputs_show, :file_edit, :file_update, :game_status, :change_status, :answer, :minus_score, :refresh_game_list]
before_filter :find_repository, :only => [:show, :entry, :file_edit, :file_update]
before_filter :allowd_manager
before_filter :allowd_view, :only => [:show]
before_filter :find__shixun_language, :only => [:show, :entry]
include ApplicationHelper
def index
@games = @myshixun.games.includes(:challenge)
@games_count = @games.count
respond_to do |format|
format.html
format.js
end
end
# mushixun的版本库必须创建时就创建
# 首次进入版本库自动打开文件
# path"" && path: @game.path
# @praise 判断是否点赞
# @tread 判断是否踩
# git_repository_url @myshixun, "Myshixun"
def show
# 展示全部实训
if @game.status == 1
# 放置页面ajax数据还没获取就关闭
@game.update_attribute(:status, 0)
end
# logger.info("#################%%%%%%#{@game.outputs.first.out_put}")
# @games = @myshixun.games.includes(:challenge)
@challenges = Challenge.where(:shixun_id => @myshixun.shixun_id)
@shixun = @myshixun.shixun
# @games_count = @games.count
@game_challenge = @game.challenge
@had_done = @game.had_done
@praise = PraiseTread.where("praise_tread_object_id=? and praise_tread_object_type=? and user_id=?", @game_challenge.id, @game_challenge.class.to_s, User.current.id).first
@tread = PraiseTread.where("praise_tread_object_id=? and praise_tread_object_type=? and user_id=?", @game_challenge.id, "ChallengeTread", User.current.id).first
@rev = @rev.nil? ? "master" : @rev
@type = params[:type]
challenge_path = @game_challenge.path
# 默认打开文件
logger.info("repository id is #{@repository.try(:id)}, repository's shixun_id =#{@repository.try(:myshixun_id)} shixun_id =#{@repository.try(:shixun_id)}, project_id => #{@repository.try(:project_id)}")
# 验证challenge文件名的合法性
if @path.blank? && !challenge_path.blank? && challenge_path.include?(".") && @type != "root"
@path = challenge_path.strip
file_content = @g.files(@myshixun.gpid, @path, @rev).content
if file_content.blank?
# gitlab缺陷forked完成短暂时间内取不了内容的所以做一个小轮询间隔0.1秒
# 超过2秒则失败需通过页面刷新
for i in 0..20 do
sleep(0.1)
file_content = @g.files(@myshixun.gpid, @path, @rev).content
unless file_content.blank?
logger.info("start: file_content is####{file_content.first(6)}")
break
end
end
end
@content = Base64.urlsafe_decode64(file_content) unless file_content.blank?
end
if @content.nil?
@entries = @repository.entries(@path, @rev)
end
# 页面前后端分离数据转换
test_sets = @game_challenge.test_sets
unless test_sets.blank?
@test_sets = test_set_static_data(test_sets)
end
logger.info("#################################{@public_test_sets}")
@test_sets_count = test_sets.count
@test_sets_hidden_count = test_sets.blank? ? 0 :test_sets.select{|test_set| test_set.is_public == false}.count
@test_sets_public_count = @test_sets_count - @test_sets_hidden_count
had_test = @game.outputs
@had_test_count = had_test.count
@had_passed_testsests_error_count = had_test.blank? ? 0 : had_test.select{|had_test| had_test.result == false}.count
@had_passed_testsests_hidden_count = had_test.blank? ? 0 : had_test.select{|had_test| had_test.result == true && had_test.is_public == false}.count
@had_passed_testsests_public_count = had_test.blank? ? 0 : had_test.select{|had_test| had_test.result == true && had_test.is_public == true}.count
@final_score = (@game.answer_open == 1 ? 0 : @game.final_score.to_i)
@had_done = ((@myshixun.games.where(:status => 2).count == @myshixun.games.count && @game_challenge.position == @myshixun.games.count) ? 1 : 0)
error_position = had_test.blank? ? nil : had_test.select{|had_test| had_test.result == false}.last
logger.info("latest output id is #{error_position.id unless error_position.blank?}")
@latest_output = error_position.try(:out_put).gsub(/\n/, '<br />').gsub(/\t/, '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;').to_json unless error_position.try(:out_put).blank?
logger.info(@latest_output)
respond_to do |format|
format.html
format.js
end
rescue Exception => e
logger.error("game error##############{e.message}")
flash[:error] = e.message
end
# 代码预览
def entry
# entry_and_raw(false)
file_content = @g.files(@myshixun.gpid, @path, @rev).content
@content = Base64.urlsafe_decode64(file_content) unless file_content.blank?
# @content = @repository.cat(@path, @rev)
end
def file_edit
# entry_and_raw(false)
# @content = @repository.cat(@path, @rev).strip
# # respond_to do |format|
# # format.js
# end
end
def file_update
@g = Gitlab.client
content = params[:content].strip
code_file = @g.edit_file(@myshixun.gpid, :content => params[:content], :file_path => @path, :branch_name => @rev, :commit_message => "shixun exec")
unless code_file.blank?
render :json => {:success => "success"}
end
=begin
respond_to do |format|
format.js
end
rescue Exception => e
puts e.message
=end
end
# json调用显示左边的隐藏具体详情@game 字段 msg
# 刷新右边的输出结果 @game out_put
def outputs_show
@output = Output.find(params[:game_output_id])
respond_to do |format|
format.js
end
end
# status 0: 未提交测评或者提交测评失败后报错1中间状态还没返回值2返回值并成功
# resubmit 唯一随机码,为了区分每一次重新评测结果
CODES = %W(1 2 3 4 5 6 7 8 9 A B C D E F G H J K L N M O P Q R S T U V W X Y Z)
def game_build
game_challenge = @game.challenge
gitUrl = git_repository_url(@myshixun, "Myshixun")
gitUrl = Base64.encode64(gitUrl)
taskId = @game.id
jobName = "myshixun_#{@myshixun.id}"
code = CODES.sample(8).join
if @game.status == 2
resubmit = "#{code}_#{@myshixun.id}"
end
# 过关后重评不更新状态
if resubmit.nil?
@game.update_attributes!(:status => 1)
end
jenkins_shixuns = Redmine::Configuration['jenkins_shixuns']
step = game_challenge.try(:position)
params = {:jobNameForInstance => "#{jobName}", :buildID => "#{taskId}", :step => "#{step}", :instanceGitURL => "#{gitUrl}", :instanceChallenge => "#{step}", :resubmit => resubmit}
uri = URI("#{jenkins_shixuns}/jenkins-exec/api/buildJobForInstance")
res = uri_exec uri, params
logger.info("#####################buildJobForInstance result is #{res}")
if (res && res['code'] != 0)
@message = "实训云平台繁忙繁忙等级86"
raise("实训云平台繁忙繁忙等级86")
end
render :json => {:result => "success", :resubmit => resubmit}
rescue
if resubmit.nil?
@game.update_attributes!(:status => 0)
end
#REDO Jenkins服务异常弹框抛出
render :json => {:result => "failed"}
end
# @myshixun -status 1:完成实训
# 全部通关奖励1000分
# retry_status 评测的唯一标识,重新评测对以前已产生的结果不造成影响
# params[:resubmit] 不为空表示已通过该关后重新评测, 重新评测必须是通过该关后才能执行
# @had_done 1 通关所有关
def game_status
game_challenge = @game.challenge
had_done = @game.had_done
resubmit_identifier = @game.resubmit_identifier
logger.info("##### resubmit_identifier is #{resubmit_identifier}")
if params[:resubmit].blank? # 非重新评测
if had_done == 1 # 通过最后一关,即所有关卡
@myshixun.update_attribute(:status, 1)
else # 通过当前一关
if @game.status == 2
@game.next_game.update_attribute(:status, 0)
unless @game.answer_open == true
reward_grade(@game.user, @game.id, @game.class, game_challenge.score)
end
end
end
game_status = @game.status
else # 重新评测
if params[:resubmit] == resubmit_identifier # 本次重新评测结果已经返回并存入数据库
game_status = (@game.retry_status == 2 ? 2 : 0) # retry_status 2成功 1 失败
if game_status == 2 && had_done == 1 # 重新评测如果评测成功
had_done =1
else
had_done = 0
end
else
# game_status 在前端有行为判断2表示通关0表示失败1表示评测中的中间状态
game_status = 1
had_done = 0
end
end
test_sets = game_challenge.test_sets
unless test_sets.blank?
total_test_sets = test_set_static_data(test_sets)
end
logger.info("#################################{@public_test_sets}")
@test_sets_count = test_sets.count
@test_sets_hidden_count = test_sets.blank? ? 0 :test_sets.select{|test_set| test_set.is_public == false}.count
@test_sets_public_count = @test_sets_count - @test_sets_hidden_count
had_test = @game.outputs
@had_test_count = had_test.count
@had_passed_testsests_error_count = had_test.blank? ? 0 : had_test.select{|had_test| had_test.result == false}.count
@had_passed_testsests_hidden_count = had_test.blank? ? 0 : had_test.select{|had_test| had_test.result == true && had_test.is_public == false}.count
@had_passed_testsests_public_count = had_test.blank? ? 0 : had_test.select{|had_test| had_test.result == true && had_test.is_public == true}.count
@final_score = (@game.answer_open == 1 ? 0 : @game.final_score.to_i)
error_position = had_test.blank? ? nil : had_test.select{|had_test| had_test.result == false}.last
@latest_output = error_position.try(:out_put).gsub(/\n/, '<br />').gsub(/\t/, '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;') unless error_position.try(:out_put).blank?
render :json => {test_sets: total_test_sets,
test_sets_count: @test_sets_count,
test_sets_hidden_count: @test_sets_hidden_count,
test_sets_public_count: @test_sets_public_count,
had_test_count: @had_test_count,
had_passed_testsests_error_count: @had_passed_testsests_error_count,
had_passed_testsests_hidden_count: @had_passed_testsests_hidden_count,
had_passed_testsests_public_count: @had_passed_testsests_public_count,
final_score: @final_score,
latest_output: @latest_output,
status: game_status,
had_done: had_done}
end
def change_status
@game.update_attribute(:status, 0)
outputs = @game.outputs
if outputs.count == 0
outputs = ""
else
outputs = outputs.map{|result| [result.code, result.id]}
end
render :json => {status: @game.status, output: "服务器网络异常", results: outputs, had_done: 0}
end
# 自动推送下一个任务
def next_step
render_403 if @game.status != 2
next_game = @game.next_game
next_game.update_attributes(:status => 0, :open_time => Time.now) if next_game.status == 3
respond_to do |format|
format.js{ redirect_to myshixun_game_path(next_game, :myshixun_id => @myshixun)}
end
end
# 获取答案,第一次查看需扣掉该关卡的积分
# 如果已看过,下次可以免费查看
# viewed 1: 直接查看 2弹框提示将要扣除积分确定后显示内容 3弹框提示分数不够
def answer
challenge = @game.challenge
@challenge_score = challenge.score.to_i
# 已经开启过
@score = User.current.grade.to_i - @challenge_score
@answer = challenge.answer
if @game.answer_open
@viewed = 1
else
if @score >= 0
@viewed = 2
else
@viewed = 3
end
end
respond_to do |format|
format.js
format.html
end
end
def minus_score
@game_challenge = @game.challenge
user = User.current
challenge_score = @game_challenge.score.to_i
reward_grade(user, @game.id, 'Answer', -challenge_score)
@answer = @game_challenge.answer
@game.update_column(:answer_open, true)
respond_to do |format|
format.js
end
end
def refresh_game_list
myshixun_id = params[:myshixun_id]
@myshixun = Myshixun.find_by_identifier(myshixun_id)
@challenges = Challenge.where(:shixun_id => @myshixun.shixun_id)
respond_to do |format|
format.js
end
end
private
def test_set_static_data test_sets
test_result = []
unless test_sets.blank?
test_sets.each do |test_set|
output = Output.where(:game_id => @game.id, :test_set_position => test_set.position).first
actual_output = (output.try(:actual_output).gsub(/\n/, '<br />').gsub(/\t/, '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;') unless output.try(:actual_output).blank?)
result = output.try(:result)
public_result = {:is_public => (test_set.is_public ? 1 : 0), :result => result, :input => test_set.input,
:actual_output => actual_output, :output => test_set.output}
logger.info("1111#################################{public_result.to_json}")
test_result << public_result.to_json
end
end
test_result = test_result.blank? ? test_result : test_result.join(",")
logger.info("222#################################{test_result}")
return test_result
end
def entry_and_raw(is_raw)
@entry = @repository.entry(@path, @rev)
(show_error_not_found; return) unless @entry
# If the entry is a dir, show the browser
(show; return) if @entry.is_dir?
@content = @repository.cat(@path, @rev)
(show_error_not_found; return) unless @content
if is_raw || (@content.size && @content.size > Setting.file_max_size_displayed.to_i.kilobyte) || !is_entry_text_data?(@content, @path)
# Force the download
send_opt = { :filename => filename_for_content_disposition(@path.split('/').last) }
send_type = Redmine::MimeType.of(@path)
send_opt[:type] = send_type.to_s if send_type
send_opt[:disposition] = (Redmine::MimeType.is_type?('image', @path) && !is_raw ? 'inline' : 'attachment')
send_data @content, send_opt
else
# Prevent empty lines when displaying a file with Windows style eol
# TODO: UTF-16
# Is this needs? AttachmentsController reads file simply.
@content.gsub!("\r\n", "\n")
@changeset = @repository.find_changeset_by_name(@rev)
end
end
def is_entry_text_data?(ent, path)
# UTF-16 contains "\x00".
# It is very strict that file contains less than 30% of ascii symbols
# in non Western Europe.
return true if Redmine::MimeType.is_type?('text', path)
# Ruby 1.8.6 has a bug of integer divisions.
# http://apidock.com/ruby/v1_8_6_287/String/is_binary_data%3F
return false if ent.is_binary_data?
true
end
def find_repository
@repository = @myshixun.repository
render_404 if @myshixun.gpid.nil?
@path = params[:path].is_a?(Array) ? params[:path].join('/') : params[:path].to_s
@g = Gitlab.client
@g_project = @g.project(@myshixun.gpid)
@g_default_branch = @g_project.default_branch
# gitlab端获取默认分支
@rev = params[:rev].blank? ? @g_default_branch : params[:rev].to_s.strip
rescue ActiveRecord::RecordNotFound
render_404
end
def allowd_manager
render_403 unless (User.current.manager_of_myshixun?(@myshixun) || User.current.admin? || User.current.id == @myshixun.shixun.try(:user_id))
end
# 判断成员是否允许查看
def allowd_view
render_403 if (@game.status == 3 && User.current.id != @myshixun.shixun.try(:user_id))
end
def find__shixun_language
language = @myshixun.shixun.try(:language)
@language = language_switch(language)
end
# Find myshixun of id params[:id]
def find_myshixun
myshixun_id = params[:myshixun_id] || (params[:game] && params[:game][:myshixun_id])
@myshixun = Myshixun.find_by_identifier(myshixun_id)
if @myshixun.nil?
render_404
return
end
rescue ActiveRecord::RecordNotFound
render_404
end
def find_game
# myshixun_id = params[:myshixun_id]
@game = Game.find_by_identifier(params[:id])
if @game.nil?
render_404
return
end
@myshixun = @game.myshixun
rescue ActiveRecord::RecordNotFound
render_404
end
end