# encoding=utf-8
class GamesService
include ApplicationHelper
include GamesHelper
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 show_game params , current_user
game = Game . min . find_by_identifier ( params [ :identifier ] )
Rails . logger . info ( " game id is #{ game . try ( :id ) } , current_user id is #{ current_user . try ( :id ) } " )
if game . blank? || current_user . blank?
return { :status = > 404 }
end
# 防止评测中途ajax被取消;3改成0是为了处理首次进入下一关的问题
game . update_attribute ( :status , 0 ) if game . status == 1
game . update_attributes ( :status = > 0 , :open_time = > Time . now ) if game . status == 3 # Redo: 后续可以去掉, 只是为了兼顾其它模块的耗时统计
myshixun = Myshixun . min . find ( game . myshixun_id )
shixun = Shixun . min . find ( myshixun . shixun_id )
unless ( ( myshixun . user_id == current_user . id || current_user . business? || current_user . id == shixun . try ( :user_id ) ||
current_user . is_certification_teacher ) && ( shixun . operable? ) ) || current_user . admin?
Rails . logger . info ( " # # # # # # 403??? " )
return { :status = > 403 }
end
game_challenge = Challenge . min . find ( game . challenge_id )
has_answer = game_challenge . answer . blank? ? false : true
game_challenge . answer = nil
if game_challenge . st == 1
game_challenge . score = game_challenge . choose_score . to_i
end
# st: 判断是选择类型还是实训类型
st = game_challenge . st
game_count = myshixun . games . count
discusses_count = shixun . discusses . count
mirror_name = myshixun . mirror_name
user = myshixun . owner
username = user . show_name
image_url = url_to_avatar ( user )
user_url = " /users/ #{ user . login } "
is_teacher = ( user . user_extensions . identity == 0 )
tpm_identifier = shixun . try ( :identifier )
# 实训超时设置
time_limit = shixun . exec_time
# 高性能取上一关、下一关
prev_game = Game . prev_identifier ( shixun . id , game . myshixun_id , game_challenge . position )
next_game = if shixun . vnc || current_user . is_certification_teacher || shixun_manager ( shixun , current_user ) || game . status || shixun . task_pass
Game . next_game ( shixun . id , game . myshixun_id , game_challenge . position ) . try ( :identifier )
end
# 该参数是为了判断是否需要加载loading
@is_subject = params [ :is_subject ]
# 关卡点赞数, 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 - 1
# 判断实训是否全部通关
had_done = game . had_done
# 统计时间
record = EvaluateRecord . where ( :game_id = > game . id ) . first . try ( :consume_time )
# 实训制作者当前拥有的金币
grade = myshixun . owner . try ( :grade )
# power判断用户是否有权限查看隐藏测试集(TPM管理员; 平台认证的老师; 花费金币查看者) -1 表示不能解锁 0 表示需要付费解锁 1表示可以看
# myshixun_manager
myshixun_manager = shixun_manager ( shixun , current_user ) || ( current_user . is_certification_teacher )
power = ( myshixun_manager || game . test_sets_view ) ? 1 : ( shixun . test_set_permission ? 0 : - 1 )
# 选择题和编程题公共部分
container = { :st = > st , :discusses_count = > discusses_count , :game_count = > game_count , :myshixun = > myshixun . try ( :attributes ) ,
:challenge = > game_challenge . try ( :attributes ) , :game = > game . try ( :attributes ) , :shixun = > shixun . try ( :attributes ) ,
:record = > record , :grade = > grade , :prev_game = > prev_game , :next_game = > next_game , :username = > username ,
:image_url = > image_url , :user_url = > user_url , :praise_count = > praise_count , :user_praise = > user_praise , :time_limit = > time_limit ,
:tomcat_url = > Redmine :: Configuration [ 'tomcat_php' ] , :is_teacher = > is_teacher , :power = > power , :myshixun_manager = > myshixun_manager }
if shixun . vnc
begin
shixun_tomcat = Redmine :: Configuration [ 'shixun_tomcat' ]
service_host = Redmine :: Configuration [ 'tomcat_php' ]
uri = " #{ shixun_tomcat } /bridge/vnc/getvnc "
params = { tpiID : myshixun . id , :containers = > " #{ Base64 . urlsafe_encode64 ( container_limit ( shixun . mirror_repositories ) ) } " }
res = uri_exec uri , params
if res && res [ 'code' ] . to_i != 0
raise ( " 实训云平台繁忙( 繁忙等级: 99) " )
end
url = service_host + " : #{ res [ 'port' ] } /vnc_lite.html "
Rails . logger . info ( " 66666666sssssss #{ url } " )
container = container . merge ( :vnc_url = > url )
Rails . logger . info ( " 777777666sssssss #{ container } " )
rescue Exception = > e
Rails . logger . error ( e . message )
end
end
if st == 0 && shixun . status == 2 && myshixun_manager
zip_path = Gitlab . endpoint . to_s + " /projects/ " + myshixun . gpid . to_s + " /repository/archive?&private_token= " + Gitlab . private_token . to_s
container = container . merge ( :zip_path = > zip_path )
end
# 区分选择题和编程题, st: 0编程题;
if st == 0
gitUrl = gitlab_url myshixun
gitUrl = Base64 . urlsafe_encode64 ( gitUrl )
shixun_tomcat = Redmine :: Configuration [ 'shixun_tomcat' ]
params = { :tpiID = > " #{ myshixun . id } " , :tpiGitURL = > " #{ gitUrl } " }
# 判断tpm是否修改了
tpm_modified = repository_is_modified ( myshixun , shixun . try ( :gpid ) ) # 判断TPM和TPI的版本库是否被改了
tpm_cases_modified = ( game_challenge . modify_time != game . modify_time ? true : false ) # modify_time 决定TPM测试集是否有更新
# tpm_script_modified = (shixun.reset_time > myshixun.reset_time ? true : false) # 新结构脚本是实时传送的,所以不会影响
# 区分评测过未评测过,未评测过按需求取数据
sql =
if max_query_index > 0
" SELECT
b . code , b . actual_output , b . out_put , b . result , b . compile_success , a . is_public , a . input , a . output
FROM
( SELECT position , input , output , challenge_id , is_public FROM test_sets where challenge_id = #{game_challenge.id}) a
LEFT JOIN
( SELECT
result , test_set_position , g . challenge_id , o . actual_output , o . out_put , o . compile_success , o . code
FROM
outputs o left join games g on g . id = o . game_id
WHERE
game_id = #{game.id} and query_index = #{max_query_index}
) b
ON
b . challenge_id = a . challenge_id and b . test_set_position = a . position "
else
# 这个地方如果加字段的话一定注意test_set_static_data方法, 因为这个地方的字段数在下面有判断
" SELECT t.is_public, t.input, t.output, t.position
FROM games g , challenges c , test_sets t
WHERE g . id = #{game.id} and c.id= g.challenge_id and t.challenge_id = c.id"
end
qurey_test_sets = TestSet . find_by_sql ( sql )
# 测试集统计及处理
unless qurey_test_sets . blank?
check_power = ( power == 1 || game . test_sets_view )
test_sets = test_set_static_data ( qurey_test_sets , check_power )
end
test_sets_count = qurey_test_sets . size
had_test = Output . where ( :game_id = > game . id , :query_index = > max_query_index )
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_error_count = had_test_count - had_passed_testsests_error_count
error_position = had_test . blank? ? nil : had_test . select { | had_test | had_test . result == false } . last
Rails . 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 / , "   \ ;  \ ;  \ ;  \ ;  \ ;  \ ;  \ ;  \ ; " ) unless error_position . try ( :out_put ) . blank?
Rails . logger . warn ( latest_output )
output_hash = { :test_sets = > test_sets } . merge ( :had_test_count = > had_test_count , :test_sets_count = > test_sets_count ,
:had_passed_testsests_error_count = > had_passed_testsests_error_count )
multi_webssh = shixun . webssh == 2 && shixun . multi_webssh
result = { :tpm_modified = > tpm_modified , :tpm_cases_modified = > tpm_cases_modified , :hide_code = > shixun . hide_code , :forbid_copy = > shixun . forbid_copy ,
:output_sets = > output_hash , :latest_output = > latest_output , :mirror_name = > mirror_name , :multi_webssh = > multi_webssh ,
:has_answer = > has_answer } . merge ( container )
else # 选择题类型的
# 该方法多个地方调用, 比如show、评测
# 最后一个字段true表示只显示数据, false表示可能有添加数据
result_container = choose_container ( game_challenge , game , max_query_index , true )
return result_container . merge ( container )
end
end
# output为testset的实际输出
# out_put为顶端结果显示输出 power 查看测试集的权限
def test_set_static_data test_sets , power
test_result = [ ]
unless test_sets . blank?
test_sets . each do | test_set |
# 第一次刷新或者没有评测的的时候test_set只需取四个字段, 所以取了count
actual_output = test_set . attributes . count > 4 ? test_set . try ( :actual_output ) : nil
result = test_set . attributes . count > 4 ? test_set . try ( :result ) : nil
compile_success = test_set . attributes . count > 4 ? test_set . try ( :compile_success ) : nil
status = test_set . attributes . count > 4 ? test_set . try ( :code ) : nil
#actual_output = Base64.encode64(actual_output)
actual_output = ( compile_success . to_s == " 0 " && status . to_s == " -1 " ? " 编译失败,请在测试结果中查看具体的错误信息 " : actual_output )
public_result = { :is_public = > ( test_set . is_public ? 1 : 0 ) , :result = > result ,
:actual_output = > actual_output , :compile_success = > compile_success }
# 测试集公开的话才返回input结果
( test_set . is_public || power ) && public_result . merge! ( { :input = > test_set . input , :output = > test_set . output } )
Rails . logger . info ( " # # # # # # # # # # # # # # # # # # # # # # # test_set: #{ public_result } " )
test_result << public_result . to_json
end
end
test_result = test_result . blank? ? test_result : test_result . join ( " , " )
return test_result . gsub ( / < \/ script> / , '<//script>' )
end
# 获取TPI关卡内容
def challenges params
result = [ ]
myshixun = Myshixun . find ( Game . where ( :identifier = > params [ :identifier ] ) . pluck ( :myshixun_id ) . first )
shixun = Shixun . select ( [ :id , :status ] ) . find ( myshixun . shixun_id )
games = myshixun . games . includes ( :challenge ) . reorder ( " challenges.position " )
games . each do | game |
game_status = game . try ( :status )
challenge = game . challenge
if challenge . st == 0 # 实战类型
if game_status == 2 # 通关了则取实际得分,没通关则取总分
gold = ( shixun . status < = 1 ) ? 0 : game . final_score . to_i
# 只要过关了, 查看了答案经验值就是0, 金币是负数
experience = ( shixun . status < = 1 || game . final_score . to_i < 0 ) ? 0 : challenge . score . to_i
challenge_tags_count = ( shixun . status < = 1 ) ? 0 : challenge . challenge_tags . count
else
gold = challenge . score . to_i
experience = gold
challenge_tags_count = challenge . challenge_tags . count
end
else
if game_status == 2
gold = ( shixun . status < = 1 ) ? 0 : game . final_score . to_i
experience = ( shixun . status < = 1 || game . final_score . to_i < 0 ) ? 0 : challenge . choose_score . to_i
challenge_tags_count = ( shixun . status < = 1 || game . final_score . to_i < = 0 ) ? 0 : challenge . choose_tags_num
else
# 选择题只有在全对的时候才会获取final score总分, 错任何一个题final_score就为0
gold = challenge . choose_score . to_i
experience = challenge . choose_score . to_i
challenge_tags_count = challenge . choose_tags_num
end
end
result << { :position = > challenge . position , :subject = > challenge . subject , :tag_count = > challenge_tags_count , :gold = > gold ,
:experience = > experience , :status = > game . status , :star = > game . star , :identifier = > game . identifier }
end
result
end
# 评论打星星
def star params , current_user
game = Game . find_by_identifier ( params [ :identifier ] )
shixun = Shixun . select ( [ :id , :status ] ) . find ( params [ :shixun_id ] )
# 如果已经评星,则不能重复评
grade = Grade . where ( :user_id = > current_user . id , :container_id = > game . id , :container_type = > 'Star' ) . first
if grade . present?
return { :reward_code = > - 1 }
else
game . update_column ( :star , params [ :star ] . to_i )
# 随机生成10-100金币
code = rand ( 10 .. 100 )
# 积分消耗情况记录;加积分之针对已发布的实训
if shixun . status > = 2
reward_grade ( current_user , game . id , 'Star' , code )
return { :reward_code = > code }
else
return { :reward_code = > 0 }
end
end
end
# TPI版本库文件目录结构
def entries params
g = Gitlab . client
gpid = params [ :gpid ]
path = params [ :path ] . try ( :strip )
rev = params [ :rev ] ? params [ :rev ] : " master "
result = [ ]
trees = g . trees ( gpid , path : path , rev : rev )
trees . each do | tree |
# 去掉第一行的"/"
new_path = File . join ( path , tree . name ) . reverse . chomp ( " / " ) . reverse
result << { :name = > tree . name , :path = > new_path , :kind = > tree . type == 'tree' ? 'dir' : 'file' }
end
return result
end
def answer params , current_user
game_base params [ :identifier ]
challenge = Challenge . select ( [ :answer , :id , :score , :st ] ) . find ( @game . challenge_id )
is_teacher = ( current_user . user_extensions . identity == 0 )
is_certification_teacher = current_user . is_certification_teacher
# 这几种情况可以直接查看答案的:实训未发布;当前用户为实训管理员;已经查看过答案;平台认证的老师;
if ( @shixun . status < 2 || shixun_manager ( @shixun , current_user ) || @game . answer_open == 1 || is_certification_teacher )
Rails . logger . info ( " answer #{ params [ :identifier ] } # # # # user is #{ current_user . id } , status is #{ @shixun . status } , manager is #{ shixun_manager ( @shixun , current_user ) } , open #{ @game . answer_open } , cer #{ is_certification_teacher } " )
if challenge . st == 0
result = challenge . try ( :answer )
else
result = [ ]
challenge . challenge_chooses . each do | choose |
result << { :position = > choose . position , :answer = > ( choose . answer . blank? ? choose . standard_answer : choose . answer ) }
end
end
return { :view_answer = > true , :answer = > result }
else
return { :is_teacher = > is_teacher , :view_answer = > false }
# view_answer = false
end
end
# 查看答案需要扣取金币
# 必须保证用户的金币数大于关卡的金币数
def answer_grade params , current_user
game_base params [ :identifier ]
challenge = Challenge . select ( [ :answer , :id , :score , :st ] ) . find ( @game . challenge_id )
challenge_score = challenge . try ( :score )
final_score = @game . final_score
if current_user . grade . to_i - challenge_score > 0
if @game . answer_open == 0 # 如果这是第一次查看答案
if challenge . st == 0
final_score = final_score - challenge_score
# 积分消耗情况记录
reward_grade ( current_user , @game . id , 'Answer' , - challenge_score )
else
final_score = final_score - challenge . choose_score . to_i
# 之所以不用final_score是因为过关后查看答案的final_score为0, 但是记录需要记录扣除的分数
reward_grade ( current_user , @game . id , 'Answer' , - challenge . choose_score . to_i )
end
@game . update_attributes! ( :answer_open = > true , :final_score = > final_score )
end
else
# 金币不足
raise ( " 0 " )
end
if challenge . st == 0
answer = challenge . try ( :answer )
else
answer = [ ]
challenge . challenge_chooses . each do | choose |
answer << { :position = > choose . position , :answer = > ( choose . answer . blank? ? choose . standard_answer : choose . answer ) }
end
end
return { :answer = > answer , :final_score = > final_score , :grade = > @myshixun . owner . try ( :grade ) }
end
# 查看隐藏测试集
def check_test_sets params , current_user
game = Game . select ( [ :id , :challenge_id , :test_sets_view ] ) . find_by_identifier ( params [ :identifier ] )
if game . test_sets_view == true
return { :status = > - 1 , :message = > " 已解锁,请误重复操作! " }
end
challenge = Challenge . select ( [ :id , :score ] ) . find ( game . challenge_id )
user_grade = current_user . grade
minus_grade = challenge . score * 5
max_query_index = game . query_index - 1
# 区分评测过未评测过,未评测过按需求取数据
if max_query_index > 0
qurey_test_sets = TestSet . find_by_sql ( " SELECT o.code, o.actual_output, o.out_put, o.result, o.compile_success, o.test_set_position, o.query_index,t.is_public,t.input, t.output, g.id as game_id, c.id as challenge_id FROM outputs o,games g ,challenges c,test_sets t where
g . id = #{game.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
" )
else
qurey_test_sets = TestSet . find_by_sql ( " SELECT t.is_public,t.input, t.output,t.position FROM games g ,challenges c,test_sets t where
g . id = #{game.id} and c.id= g.challenge_id and t.challenge_id = c.id
" )
end
test_sets = test_set_static_data ( qurey_test_sets , true )
if user_grade > = minus_grade
current_user . update_attribute ( :grade , user_grade - minus_grade )
game . update_attribute ( :test_sets_view , true )
# 扣分记录
gardes = Grade . create ( :user_id = > current_user . id , :container_id = > game . id , :score = > - minus_grade , :container_type = > " testSet " )
gardes . attributes . merge! ( { test_sets : test_sets } )
else
return { :status = > - 1 , :message = > " 本操作需要扣除 #{ minus_grade } 金币,您的金币不够了 " }
end
end
# 文件更新;数据评测记录
# 生成重新评测认证码
# content_modified:0 表示文件没有更新; content_modified:1 表示文件有更新
def file_update params , current_user
game_base params [ :identifier ]
path = params [ :path ] . strip unless params [ :path ] . blank?
rev = params [ :rev ] ? params [ :rev ] : " master "
content_modified = 0
ActiveRecord :: Base . transaction do
# params[:evaluate] 实训评测时更新必须给的参数,需要依据该参数做性能统计,其它类型的更新可以跳过
# 自动保存的时候evaluate为0; 点评测的时候为1
if params [ :evaluate ] == 1
sec_key = generates_identifier ( EvaluateRecord , 10 )
record = EvaluateRecord . create! ( :user_id = > current_user . id , :shixun_id = > @myshixun . shixun . id ,
:game_id = > @game . id , :identifier = > sec_key )
Rails . logger . warn ( " # # game is is #{ @game . id } , record id is #{ record . id } , time is**** #{ Time . now . strftime ( " %Y-%m-%d %H:%M:%S.%L " ) } " )
# @myshixun.student_works.update_all(:update_time => Time.now) if !@myshixun.student_works.blank?
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
@g = Gitlab . client
last_content = @g . files ( @myshixun . gpid , path , " master " ) . try ( :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 = > " master " ,
:commit_message = > ( params [ :evaluate ] == 1 ? " commit by author " : " auto commit " ) )
end
# REDO: 是否有读写分离的问题
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 ( " update file failed " )
else
return { :success = > " success " , :resubmit = > resubmit , :content_modified = > content_modified , sec_key : record . try ( :identifier ) }
end
end
rescue Exception = > e
Rails . logger . error ( " file update failed #{ e . message } " )
return { :status = > - 1 , :success = > " fail " , :message = > " 实训云平台繁忙( 繁忙等级: 81) , 请稍后刷新并重试 " , :position = > @game . challenge . position , :had_done = > @game . had_done }
end
# 恢复初始代码
# 注意path为当前打开文件的path
def reset_original_code params , current_user
g = Gitlab . client
path = params [ :path ]
begin
file_base params [ :identifier ]
shixun = Shixun . select ( :gpid ) . find ( @myshixun . shixun_id )
content = g . files ( shixun . gpid , path , " master " ) . try ( :content )
content = tran_base64_decode64 ( content )
g . edit_file ( @myshixun . gpid , current_user . login , :content = > content , :file_path = > path , :branch_name = > " master " ,
:commit_message = > " reset_original_code " )
if content . nil?
raise ( " 初始代码为空,代码重置失败 " )
end
return content
rescue Exception = > e
raise ( " #{ e . message } " )
end
end
def reset_passed_code params , current_user
g = Gitlab . client
path = params [ :path ]
game = Game . select ( [ :id , :myshixun_id ] ) . where ( :identifier = > params [ :identifier ] ) . first
myshixun = Myshixun . find ( game . myshixun_id )
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
g . edit_file ( myshixun . try ( :gpid ) , current_user . login , :content = > content , :file_path = > path , :branch_name = > " master " , :commit_message = > " game passed reset " )
return content
else
return { :status = > - 1 , :message = > " 重置失败,代码为空 " }
end
end
# 获取版本库文件内容
# 通过版本库的默认分支可以判断版本库是否正常
# 注: 如果本身path传错, 内容肯定也为空; fork成功后, 可能短时间内也获取不到版本库内容
# status =0 表示获取默认打开的内容, 1表示右侧目录结构中获取的内容
def rep_content params , current_user
file_base params [ :identifier ]
# current_user: fork不应该是用当前用户去fork
current_user = @myshixun . owner
g = Gitlab . client
gpid = @myshixun . try ( :gpid )
path = @game . challenge . try ( :path ) . split ( " ; " ) [ 0 ] . strip ( )
path = params [ :path ] ? params [ :path ] : path
shixun_gpid = params [ :shixun_gpid ]
status = params [ :status ]
path = path . try ( :strip )
rev = params [ :rev ] ? params [ :rev ] : " master "
Rails . logger . warn ( " path is #{ path } " )
content = g . files ( gpid , path , rev ) . try ( :content )
if content . blank?
# 监测版本库HEAD是否存在, 不存在则取最新的HEAD
gitUrl = gitlab_url @myshixun
gitUrl = Base64 . urlsafe_encode64 ( gitUrl )
shixun_tomcat = Redmine :: Configuration [ 'shixun_tomcat' ]
rep_params = { :tpiID = > " #{ @myshixun . id } " , :tpiGitURL = > " #{ gitUrl } " }
# 监测版本库HEAD是否存在, 不存在则取最新的HEAD
uri = " #{ shixun_tomcat } /bridge/game/check "
res = uri_exec uri , rep_params
# res值: 0 表示正常;-1表示有错误; -2表示代码版本库没了
if ( status == 0 && res && res [ 'code' ] == - 2 && params [ :retry ] . to_i == 1 )
# 没有版本库则删除现有的版本库关联, 重新fork一个版本库
g = Gitlab . client
if current_user . gid . nil?
s = Trustie :: Gitlab :: Sync . new
s . sync_user ( current_user )
end
gpid = sync_gitlab_rep @myshixun , shixun_gpid , current_user . try ( :gid )
content = g . files ( gpid , path , rev ) . content
else
if ( res && res [ 'code' ] != 0 )
Rails . logger . error ( " get rep_content failed: bridge return !0 " )
# raise(status)
return nil
end
end
# gitlab缺陷: forked完成, 短暂时间内取不了内容的, 所以做一个小轮询, 间隔0.2秒
# 超过3秒则失败, 需通过页面刷新
# for i in 0..15 do
# sleep(0.2)
# content = g.files(gpid, path, rev).content
# break if content.present?
# end
end
content = tran_base64_decode64 ( content ) if content . present?
return content
end
# 编程题评测
def game_build params
# 三类实训只取基础部分数据
game = Game . select ( [ :myshixun_id , :status , :challenge_id , :id , :evaluate_count ] ) . find_by_identifier ( params [ :identifier ] )
myshixun = Myshixun . select ( [ :updated_at , :gpid , :id , :shixun_id ] ) . find ( game . myshixun_id )
shixun = Shixun . select ( [ :id , :evaluate_script , :webssh , :exec_time , :sigle_training , :identifier , :status ] ) . find ( myshixun . shixun_id )
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 = gitlab_url myshixun
gitUrl = Base64 . urlsafe_encode64 ( gitUrl )
taskId = game . id
shixun_tomcat = Redmine :: Configuration [ 'shixun_tomcat' ]
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没有更新, 则轮询等待更新
params = { :tpiID = > " #{ myshixun . id } " , :tpiGitURL = > " #{ gitUrl } " , :buildID = > " #{ taskId } " , :instanceChallenge = > " #{ step } " ,
:testCases = > " #{ testCases } " , :resubmit = > " #{ resubmit } " , :times = > params [ :first ] . to_i , :podType = > shixun . webssh ,
:containers = > " #{ Base64 . urlsafe_encode64 ( container_limit ( shixun . mirror_repositories ) ) } " , :tpmScript = > " #{ tpmScript } " ,
:timeLimit = > " #{ shixun . exec_time } " , :content_modified = > content_modified , :persistenceName = > shixun . identifier ,
:isPublished = > ( shixun . status < 2 ? 0 : 1 ) , :sec_key = > params [ :sec_key ] }
# 评测有文件输出的需要特殊传字段 path: 表示文件存储的位置
params [ 'file' ] = Base64 . urlsafe_encode64 ( { :path = > " #{ game_challenge . picture_path } " } . to_json ) if game_challenge . picture_path . present?
# needPortMapping: web类型需要pod端口映射
params [ :needPortMapping ] = 8080 if myshixun . mirror_name . include? ( " Web " )
# 中间层交互
if shixun . sigle_training
uri = " #{ shixun_tomcat } /bridge/game/persistence/gameEvaluate "
else
uri = " #{ shixun_tomcat } /bridge/game/gameEvaluate "
end
res = interface_post uri , params , 502
# 单评测类型(比较快的类型, 实时返回结果, 即不用中间层再回调trainint_task_status)
if res [ 'syncResult' ] . present?
sigle_trainint_data res , myshixun , game
end
# ----单测模式end
return { :result = > " success " , :resubmit = > resubmit , :ableToCreate = > res [ 'ableToCreate' ] , :waitNum = > res [ 'waitNum' ] ,
:waitingTime = > res [ 'waitingTime' ] , :position = > game_challenge . position , :port = > res [ 'port' ] ,
:had_done = > game . had_done }
rescue Exception = > e
Rails . logger . error ( " 评测出错,详情: " + e . message )
return { :result = > 'fail' , :contents = > " 实训云平台繁忙( 繁忙等级: 502) , 请稍后刷新并重试 " , :position = > game_challenge . position , :had_done = > game . had_done }
end
# 单评测类型(比较快的类型, 实时返回结果, 即不用中间层再回调trainint_task_status)
def sigle_trainint_data res , myshixun , game
begin
t1 = Time . now
jsonTestDetails = JSON . parse ( res [ 'syncResult' ] [ 'jsonTestDetails' ] )
Rails . logger . info ( " res is #{ res } # # # jsonTestDetails is #{ jsonTestDetails } " )
timeCost = JSON . parse ( res [ 'syncResult' ] [ 'timeCost' ] )
brige_end_time = Time . parse ( timeCost [ 'evaluateEnd' ] ) if timeCost [ 'evaluateEnd' ] . present?
return_back_time = format ( " %.3f " , ( t1 . to_f - brige_end_time . to_f ) ) . to_f
status = jsonTestDetails [ 'status' ]
game_id = jsonTestDetails [ 'buildID' ]
Rails . logger . info ( " sigle training_task_status start # 1** #{ game_id } **** #{ Time . now . strftime ( " %Y-%m-%d %H:%M:%S.%L " ) } " )
resubmit = jsonTestDetails [ 'resubmit' ]
outPut = tran_base64_decode64 ( jsonTestDetails [ 'outPut' ] )
jenkins_testsets = jsonTestDetails [ 'msg' ]
compile_success = jsonTestDetails [ 'compileSuccess' ]
Rails . logger . info ( outPut )
challenge = game . challenge
if challenge . picture_path . present?
pics = res [ :tpiRepoPath ]
game . update_column ( :picture_path , pics )
end
Rails . logger . info ( " training_task_status start # 2** #{ game_id } **** #{ Time . now . strftime ( " %Y-%m-%d %H:%M:%S.%L " ) } " )
unless jenkins_testsets . blank?
jenkins_testsets . each do | j_test_set |
Rails . logger . info ( " j_test_set: # # # # # # # # # # # # # # #{ j_test_set } " )
actual_output = tran_base64_decode64 ( j_test_set [ 'output' ] )
Rails . logger . info " actual_output: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #{ actual_output } "
Output . create! ( :code = > status , :game_id = > game_id , :out_put = > outPut , :test_set_position = > j_test_set [ 'caseId' ] ,
:actual_output = > actual_output , :result = > j_test_set [ 'passed' ] . to_i , :query_index = > game . query_index ,
:compile_success = > compile_success . to_i )
end
end
Rails . logger . info ( " # # # # # # # # # # # # # status: #{ status } " )
record = EvaluateRecord . where ( :game_id = > game_id ) . first
Rails . logger . info ( " training_task_status start # 3** #{ game_id } **** #{ Time . now . strftime ( " %Y-%m-%d %H:%M:%S.%L " ) } " )
# status: 0表示评测成功
if status == " 0 "
if ! resubmit . blank?
game . update_attributes! ( :retry_status = > 2 , :resubmit_identifier = > resubmit )
#if game.had_done == 1
challenge . path . split ( " ; " ) . each do | path |
game_passed_code ( game . id , path . try ( :strip ) , myshixun . try ( :gpid ) , 1 )
end
#end
else
game . update_attributes! ( :status = > 2 , :end_time = > Time . now , :accuracy = > format ( " %.4f " , 1 . 0 / game . query_index ) )
myshixun . update_attributes! ( :status = > 1 ) if game . had_done == 1
challenge . path . split ( " ; " ) . each do | path |
game_passed_code ( game . id , path , myshixun . try ( :gpid ) , 1 )
end
if ! game . answer_open && ( challenge . shixun . try ( :status ) > 1 ) # 如果没有查看答案,则获得该关卡得分
reward_grade ( game . user , game . id , 'Game' , challenge . score )
reward_experience ( game . user , game . id , 'Game' , challenge . score )
game . update_attributes! ( :final_score = > challenge . score )
end
# 更新实训关联的作品分数
update_myshixun_work_score myshixun
end
# 如果过关了, 下一关的状态是3( 为开启) , 则需要把状态改成1( 已开启)
# next_game = game.next_game
next_game = Game . next_game ( myshixun . shixun_id , game . myshixun_id , challenge . position )
next_game . update_column ( :status , 0 ) if next_game . present? && next_game . status == 3
else # status == "-1" 表示返回结果错误
if ! resubmit . blank?
game . update_attributes! ( :retry_status = > 1 , :resubmit_identifier = > resubmit )
else
game . update_attributes! ( :status = > 0 )
end
end
test_cases_time = format ( " %.3f " , ( Time . now . to_f - t1 . to_f ) ) . to_f
if record . present?
consume_time = format ( " %.3f " , ( Time . now - record . created_at ) ) . to_f
record . update_attributes! ( :consume_time = > consume_time , :git_pull = > timeCost [ 'pull' ] ,
:create_pod = > timeCost [ 'createPod' ] , :pod_execute = > timeCost [ 'execute' ] , :test_cases = > test_cases_time ,
:brige = > timeCost [ 'evaluateAllTime' ] , :return_back = > return_back_time )
end
end
end
# 选择题评测
def choose_build params
# 三类实训只取基础部分数据
game = Game . select ( [ :myshixun_id , :status , :challenge_id , :id , :answer_open , :final_score , :identifier ,
:evaluate_count ] ) . find_by_identifier ( params [ :identifier ] )
# 更新评测次数
game . update_column ( :evaluate_count , ( game . evaluate_count . to_i + 1 ) )
# 选择题如果通关了,则不让再评测
if game . status == 2
return { :message = > " 您已通过该关卡 " }
end
myshixun = Myshixun . select ( [ :updated_at , :gpid , :id , :shixun_id , :status , :user_id ] ) . find ( game . myshixun_id )
shixun = Shixun . select ( [ :id , :evaluate_script , :webssh , :status ] ) . find ( myshixun . shixun_id )
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
total_score = 0
had_passed = true
test_sets = [ ]
str = " "
game_challenge . challenge_chooses . includes ( :challenge_tags ) . each_with_index do | choose , index |
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 . query_index } ', ' #{ Time . now . strftime ( '%Y-%m-%d %H:%M:%S' ) } ', ' #{ Time . now . strftime ( '%Y-%m-%d %H:%M:%S' ) } ') "
# Output.create(:game_id => @game.id, :test_set_position => choose.position, :actual_output => params[:answer][index], :result => correct, :query_index => @game.query_index)
# 只要有一题错误就不能通关
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
# 批量插入评测结果
Rails . logger . warn ( " sql str is #{ str } " )
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
# 查看答案的时候处理final_scor和扣分记录
experience = score
reward_grade ( myshixun . owner , game . id , 'Game' , score )
game . update_attribute ( :final_score , score )
final_score = score
reward_experience ( myshixun . owner , game . id , 'Game' , score )
end
end
had_done = game . had_done
myshixun . update_attribute ( :status , 1 ) if had_done == 1
grade = myshixun . owner . try ( :grade )
# 更新实训关联的作品分数
update_myshixun_work_score myshixun
# 高性能取上一关、下一关
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 )
# 如果过关了, 下一关的状态是3( 为开启) , 则需要把状态改成1( 已开启)
next_game . update_column ( :status , 0 ) if next_game . present? && next_game . status == 3
return { :grade = > grade , :gold = > final_score , :experience = > experience , :had_submmit = > true ,
:challenge_chooses_count = > challenge_chooses_count , :choose_correct_num = > choose_correct_num ,
:test_sets = > test_sets , :prev_game = > prev_game , :next_game = > next_game . try ( :identifier ) }
end
# 实训选择题需要局部刷新或者显示的部分
def choose_container game_challenge , game , max_query_index , just_show
# category 1: 单选题,其它的多选题(目前只有两种)
challenge_chooses = game_challenge . challenge_chooses . includes ( :challenge_questions )
test_sets = [ ]
choose_test_cases = [ ]
chooses = [ ]
# 选择题测试集统计
challenge_chooses_count = challenge_chooses . count
choose_correct_num = game . choose_correct_num
game_outputs = game . outputs . where ( :query_index = > max_query_index )
# 判断用户是否有提交
had_submmit = game_outputs . blank? ? false : true
# 判断选择题是否写了标准答案
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 }
return { :choose = > chooses , :choose_test_cases = > choose_test_cases , :has_answer = > has_answer }
end
# 轮询获取状态
# resubmit是在file_update中生成的, 从game_build中传入的
def game_status params , current_user
Rails . logger . info ( " sec_key is #{ params [ :sec_key ] } **1 " )
game = Game . find_by_identifier ( params [ :identifier ] )
resubmit_identifier = game . resubmit_identifier
# 如果没有超时并且正在评测中
# 判断评测中的状态有两种: 1、如果之前没有通关的, 只需判断status为1即可; 如果通过关, 则判断game的resubmit_identifier是否更新
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 )
return { :running_code_status = > running_code_status , :running_code_message = > running_code_message }
end
Rails . logger . info ( " sec_key is #{ params [ :sec_key ] } **2 " )
Rails . logger . info ( " # # # # # 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 )
shixun = Shixun . select ( [ :id , :status , :user_id , :test_set_permission ] ) . find ( game_challenge . shixun_id )
if params [ :resubmit ] . blank? # 非重新评测
if game_status == 2 # 通关
if shixun . status > 1
score = game . final_score # 查看答案的时候有对最终获得金币进行处理
experience = game . answer_open > 0 ? 0 : game_challenge . score . to_i
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
Rails . logger . info ( " sec_key is #{ params [ :sec_key ] } **3 " )
# 实训的最大评测次数,这个值是为了优化查询,每次只取最新的最新一次评测的结果集
max_query_index = game . query_index - 1
# 区分评测过未评测过,未评测过按需求取数据
if max_query_index > 0
qurey_test_sets = TestSet . find_by_sql ( " SELECT o.code, o.actual_output,o.result, o.compile_success, o.test_set_position,
o . query_index , t . is_public , t . input , t . output , g . id as game_id , c . id as challenge_id FROM outputs o , games g , challenges c , test_sets t where
g . id = #{game.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
" )
else
qurey_test_sets = TestSet . find_by_sql ( " SELECT t.is_public,t.input, t.output,t.position FROM games g ,challenges c,test_sets t where
g . id = #{game.id} and c.id= g.challenge_id and t.challenge_id = c.id
" )
end
# 能进入到此处, 肯定是已经返回了结果, 也就是说outputs中肯定有了数据
Rails . logger . info ( " sec_key is #{ params [ :sec_key ] } **4 " )
test_sets_count = qurey_test_sets . size
# had_test = Output.where(:game_id => game.id, :query_index => max_query_index)
# had_test_count = had_test.count
had_passed_testsests_error_count = max_query_index > 0 ? qurey_test_sets . select { | qurey_test_set | qurey_test_set . result == false } . count : 0
had_passed_testsests_error_count = test_sets_count - had_passed_testsests_error_count
had_test = Output . where ( :game_id = > game . id , :query_index = > max_query_index )
error_position = had_test . blank? ? nil : had_test . select { | had_test | had_test . result == false } . last
Rails . 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 / , "   \ ;  \ ;  \ ;  \ ;  \ ;  \ ;  \ ;  \ ; " ) if error_position . try ( :out_put ) . present?
Rails . logger . warn ( " last_output is #{ latest_output } " )
# power判断用户是否有权限查看隐藏测试集(TPM管理员; 平台认证的老师; 花费金币查看者)
power = ( shixun_manager ( shixun , current_user ) || ( current_user . is_certification_teacher ) ) ? 1 : ( shixun . test_set_permission ? 0 : - 1 )
# 测试集统计及处理
unless qurey_test_sets . blank?
check_power = ( power == 1 || game . test_sets_view )
test_sets = test_set_static_data ( qurey_test_sets , check_power )
end
# 处理生成图片类型文件
picture = ( game . picture_path . nil? ? 0 : game . id )
# 针对web类型的实训
web_route = game_challenge . try ( :web_route )
mirror_name = shixun . mirror_name
Rails . logger . info ( " sec_key is #{ params [ :sec_key ] } **5 " )
# 轮询结束,更新评测耗时
e_record = EvaluateRecord . where ( :identifier = > params [ :sec_key ] ) . first
if game_status == 0 || game_status == 2
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
Rails . logger . warn ( " # # game is is #{ game . id } , record id is #{ e_record . id } , time is**** #{ Time . now . strftime ( " %Y-%m-%d %H:%M:%S.%L " ) } " )
# 记录前端总耗时
record = e_record . try ( :consume_time )
# 实训制作者当前拥有的金币
grade = User . where ( :id = > game . user_id ) . pluck ( :grade ) . first
Rails . logger . info ( " sec_key is #{ params [ :sec_key ] } **6 " )
# 高性能取上一关、下一关
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 ) . try ( :identifier )
output_hash = { :test_sets = > test_sets , :had_test_count = > test_sets_count , :test_sets_count = > test_sets_count , :had_passed_testsests_error_count = > had_passed_testsests_error_count }
Rails . logger . info ( " sec_key is #{ params [ :sec_key ] } **7 " )
return { :grade = > grade , :gold = > score , :experience = > experience , :status = > game_status , :had_done = > had_done ,
:position = > game_challenge . position , :port = > port , :power = > power , :record = > record ,
:mirror_name = > mirror_name , :picture = > picture , :web_route = > web_route , :latest_output = > latest_output ,
:star = > game . star , :next_game = > next_game , :prev_game = > prev_game } . merge ( output_hash )
end
# 记录实训花费的时间, 前端是通过ent_time - open_time, 所以最终只更新open_time即可
# 总花费的时间是花费的总时间加上开启的时间( open_time)
# 注意: endtime和当前时间没关系, 非时间差值, 必须和opentime结合使用才有意义
def cost_time params
game = Game . select ( [ :id , :cost_time , :identifier ] ) . find_by_identifier ( params [ :identifier ] )
cost_time = params [ :time ] . to_i
game . update_attribute ( :cost_time , cost_time )
return { :game = > game }
end
def sync_modify_time params
game = Game . select ( [ :id , :modify_time , :challenge_id ] ) . find_by_identifier ( params [ :identifier ] )
modify_time = Challenge . where ( :id = > game . challenge_id ) . pluck ( :modify_time ) . first
game . update_column ( :modify_time , modify_time )
return { :status = > 1 , :message = > " success " }
end
# tpi弹框状态更新; true则不再显示; false每次刷新显示
def system_update params
myshixun = Myshixun . find ( params [ :myshixun_id ] )
myshixun . update_attribute ( :system_tip , true )
return { :status = > 1 , :message = > " success " }
end
# For admin
# 强制重置实训
def reset_my_game params , current_user
unless current_user . admin?
return { :status = > 403 , :message = > " unauthorized " }
end
myshixun = Myshixun . find ( Game . where ( :identifier = > params [ :identifier ] ) . pluck ( :myshixun_id ) . first )
shixun = myshixun . shixun
ActiveRecord :: Base . transaction do
begin
g = Gitlab . client
shixun_tomcat = Redmine :: Configuration [ 'shixun_tomcat' ]
challenges = shixun . challenges
# 删除选择题用户记录
unless challenges . blank?
challenges . each do | challenge |
if challenge . st != 0
challenge . challenge_chooses . each do | choose |
user_output = choose . choose_outputs
unless user_output . blank?
user_output . delete
end
end
end
end
end
#end
myshixun_job = Base64 . urlsafe_encode64 ( " myshixun_ #{ myshixun . id } " )
StudentWork . where ( :myshixun_id = > myshixun . id ) . update_all ( :myshixun_id = > 0 , :work_status = > 0 , :work_score = > nil , :final_score = > nil ,
:cost_time = > 0 , :update_time = > nil , :compelete_status = > 0 , :commit_time = > nil )
# myshixun.destroy
myshixun . delete
# 主从复制出现脏读的情况
if myshixun . gpid
g . delete_project ( myshixun . gpid )
end
rescue Exception = > e
Rails . logger . error ( " myshixun reset failed #{ e } " )
raise ActiveRecord :: Rollback
end
end
# 主从复制出现脏读的情况
sleep ( 1 )
return { :status = > 1 , :message = > " sucess " , :url = > " /shixuns/ #{ shixun . identifier } " }
end
def close_webssh params , current_user
myshixun_id = Game . where ( :identifier = > params [ :identifier ] ) . pluck ( :myshixun_id ) . first
digest = params [ :identifier ] + Redmine :: Configuration [ 'bridge_secret_key' ]
digest_key = Digest :: SHA1 . hexdigest ( " #{ digest } " )
begin
shixun_tomcat = Redmine :: Configuration [ 'shixun_tomcat' ]
uri = " #{ shixun_tomcat } /bridge/webssh/delete "
Rails . logger . info ( " #{ current_user } => cloese_webssh digest is #{ digest } " )
params = { :tpiID = > myshixun_id , :digestKey = > digest_key , :identifier = > params [ :identifier ] }
res = uri_exec uri , params
if res && res [ 'code' ] . to_i != 0
raise ( " 实训云平台繁忙( 繁忙等级: 110) " )
end
return { :status = > 1 , :message = > " webssh closed " }
rescue Exception = > e
Rails . logger . error ( e )
return { :status = > 0 , :message = > " authentication failed " }
end
end
# return: 头像,姓名,用户主页链接,学校, 点赞数, 等级(通关数)
def get_passed_user params
page = params [ :page ] . to_i
offset = page * 15
file_base params [ :identifier ]
games = @game . challenge . games . where ( :status = > 2 ) . includes ( user : [ :myshixuns , user_extensions : :school ] ) . order ( " cost_time desc " ) . offset ( offset ) . limit ( 15 )
users = format_answer_list games
{ user_answer_list : users }
end
def get_passed_code params
file_base params [ :identifier ]
codes = @game . game_codes . select ( [ :new_code , :path ] ) . where ( " new_code is not null " )
code_list = [ ]
codes . each do | code |
code_list << { path : code . path , code : code . new_code }
end
{ code_list : code_list }
end
def shixun_manager shixun , user_current
member = shixun . shixun_members . where ( :role = > [ 1 , 2 ] , :user_id = > user_current . id )
( ! member . blank? || user_current . admin? ) ? true : false
end
# 实训一些基础查询
def game_base identifier
@game = Game . select ( [ :myshixun_id , :status , :final_score , :answer_open , :challenge_id , :id ] ) . find_by_identifier ( identifier )
@myshixun = Myshixun . select ( [ :id , :shixun_id , :gpid , :user_id ] ) . find ( @game . myshixun_id )
@shixun = Shixun . select ( [ :status , :id ] ) . find ( @myshixun . shixun_id )
end
# 与文件代码相关的一些通用查询
def file_base identifier
@game = Game . select ( [ :myshixun_id , :challenge_id , :id ] ) . find_by_identifier ( identifier )
@myshixun = Myshixun . select ( [ :shixun_id , :gpid , :id , :user_id ] ) . find ( @game . myshixun_id )
end
private
DCODES = %W( 2 3 4 5 6 7 8 9 a b c f e f g h i j k l m n o p q r s t u v w x y z )
# 随机生成字符
def generates_identifier ( container , num )
code = DCODES . sample ( num ) . join
while container . exists? ( identifier : code ) do
code = DCODES . sample ( num ) . join
end
code
end
def format_answer_list games
user_info = [ ]
games . each do | game |
user = game . user
votes = game . game_codes . first . votes
passed_count = user . myshixuns . where ( :status = > 1 ) . count
user_info << {
user_id : user . id ,
name : user . show_name ,
game_id : game . id ,
image_url : url_to_avatar ( user ) ,
school_name : user . school_name ,
votes : votes ,
passed_count : passed_count
}
end
user_info
end
end