You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
pgfqe6ch8/app/controllers/myshixuns_controller.rb

593 lines
24 KiB

6 years ago
# encoding: utf-8
class MyshixunsController < ApplicationController
layout 'base_myshixun'
skip_before_filter :verify_authenticity_token, :only => [:training_task_status, :close_webssh, :code_runinng_message]
before_filter :require_login, :except => [:training_task_status, :close_webssh, :code_runinng_message]
6 years ago
before_filter :check_authentication, :except => [:training_task_status, :close_webssh, :mul_test_home, :mul_test_user,
:mul_test_myshixun, :mul_test_shixun, :mul_test_start, :code_runinng_message]
6 years ago
before_filter :find_myshixun, :only => [:show, :myshixun_reset, :open_webssh, :sync_reset_time, :destroy, :search_file_list, :vnc]
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)
include ApplicationHelper
def forbidden
render_403
return
end
def not_found
render_404
return
end
def search_file_list
path = params[:path]
@path = to_path_param(path)
g = Gitlab.client
@dir = g.trees(@myshixun.gpid, :path => @path).map{|tree|[tree.type, (@path.blank? ? tree.name : "#{@path}"+"/"+tree.name )]}
logger.info("dir is ##{@dir}")
@page = (params['page'] || 1).to_i
@dir_count = @dir.count
@limit = 5
@is_remote = true
@dir_pages = Paginator.new @dir_count, @limit, params['page'] || 1
@offset ||= @dir_pages.offset
@dir = paginateHelper @dir, @limit
respond_to do |format|
format.js
end
end
# 并发测试:主页
# http://localhost:3000/myshixuns/mul_test_home?num=5
def mul_test_home
num = params[:num].to_i
uri = Setting.protocol + "://" + Setting.host_name
num.times do
Thread.new{get_url_exec(uri)}
end
end
# 并发测试:个人主页
def mul_test_user
num = params[:num].to_i
login = params[:login]
uri = Setting.protocol + "://" + Setting.host_name + "/users/#{login}"
num.times do
Thread.new{get_url_exec(uri)}
end
end
#并发测试tpi
def mul_test_myshixun
num = params[:num].to_i
identifier = params[:identifier]
uri = Setting.protocol + "://" + Setting.host_name + "/tasks/#{identifier}"
num.times do
Thread.new{get_url_exec(uri)}
end
end
#并发测试tpm
def mul_test_shixun
num = params[:num].to_i
identifier = params[:identifier]
uri = Setting.protocol + "://" + Setting.host_name + "/shixuns/#{identifier}"
num.times do
Thread.new{get_url_exec(uri)}
end
end
# 并发测试
# num为进程数
# 分三种JAVA/PYTHON/C++
# 方式模拟请求
# 实训ID: p - 122,178,120 J - 571,895,50 c++ - 101,70,118
# http://localhost:3000/myshixuns/mul_test?shixun_id=122,178,120&num=10
def mul_test
unless User.current.admin?
render_403
return
end
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)
begin
num = params[:num].to_i
@myshixuns = Myshixun.where(:shixun_id => params[:shixun_id].split(",")).first(num)
logger.warn("###1mul test game_build start : myshixuns count is #{@myshixuns.count}")
@myshixuns.each do |myshixun|
game = myshixun.games.last
logger.warn("###2mul test game_build start ")
identifier = game.try(:identifier)
if game.status == 2
code = codes.sample(8).join
resubmit = "#{code}_#{myshixun.id}"
end
logger.warn("###3mul test game_build start ...")
uri = Setting.protocol + "://" + Setting.host_name + "/api/v1/games/#{identifier}/game_build?resubmit=#{resubmit}&content_modified=0&first=1"
Thread.new{get_url_exec(uri)}
end
rescue Exception => e
logger.error("mul test failed ===> #{e.message}")
end
render :json => {:status => "start"}
end
# 最新可以用的并发测试接口
def sigle_mul_test
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)
begin
identifiers = Myshixun.where(:shixun_id => params[:shixun_id].split(",")).pluck(:identifier)
ide = identifiers[rand(identifiers.length)]
myshixun = Myshixun.where(:identifier => ide).first
game = myshixun.games.last
logger.warn("###2mul test game_build start ")
identifier = game.try(:identifier)
if game.status == 2
code = codes.sample(8).join
resubmit = "#{code}_#{myshixun.id}"
end
logger.warn("###3mul test game_build start ...")
EvaluateRecord.create!(:user_id => myshixun.user_id, :shixun_id => myshixun.shixun.id, :game_id => game.id)
redirect_to "/api/v1/games/#{identifier}/game_build?resubmit=#{resubmit}&content_modified=0&first=1"
rescue Exception => e
logger.error("mul test failed ===> #{e.message}")
end
end
# 慎用,只能用于测试版
def sigle_update_myshixun
shixun = Shixun.find_by_identifier(params[:shixun_id])
user = User.find(rand(50000))
code = down_generate_identifier("myshixun")
ActiveRecord::Base.transaction do
begin
Myshixun.create!(:shixun_id => shixun.id, :user_id => user.id, :identifier => code, :modify_time => shixun.modify_time,
:reset_time => shixun.reset_time, :onclick_time => Time.now, :gpid => -1,
:git_url => "", :commit_id => "123123")
render :json => {:status => 200}
rescue Exception => e
logger.error("sigle test #{e.message}")
render :json => {:error => "#{e}"}
raise ActiveRecord::Rollback
end
end
end
# 开启实训
def mul_test_start
unless User.current.admin?
render_403
return
end
num = params[:num].to_i
shixuns = params[:shixun_identifier].split(",")
num.times do |n|
lastnames = []
100.times do |n|
lastnames << "educoder#{sprintf("%04d", n)}"
end
lastname = lastnames[rand(lastnames.count)]
shixun = Shixun.where(:identifier => shixuns[rand(shixuns.count)]).first
current_user = User.where(:lastname => lastname).first
Thread.new{start_shixun(shixun, current_user)}
end
end
def start_shixun shixun, current_user
ActiveRecord::Base.transaction do
begin
# fork版本库如果用户没有同步则先同步用户
g = Gitlab.client
if current_user.gid.nil?
s = Trustie::Gitlab::Sync.new
s.sync_user(current_user)
end
gshixun = g.fork(shixun.gpid, current_user.gid)
shixun_tomcat = Redmine::Configuration['shixun_tomcat']
code = down_generate_identifier("myshixun")
# 一般通过默认分支是否存在来判断一个项目是否fork成功
if gshixun.try(:id).present?
commit_id = g.commits(shixun.gpid).first.try(:id)
# educoder 加入到myshixun中
myshixun_admin_gid = User.where(:login => "educoder").first.try(:gid)
g.add_team_member(gshixun.id, myshixun_admin_gid, 40) # 40代表角色master
myshixun = Myshixun.create!(:shixun_id => shixun.id, :user_id => current_user.id, :identifier => code, :modify_time => @shixun.modify_time,
:reset_time => shixun.reset_time, :onclick_time => Time.now, :gpid => gshixun.id,
:git_url => gshixun.try(:path_with_namespace), :commit_id => commit_id)
# tpm 不需要目录的
# rep = Repository.create!(:myshixun_id => myshixun.id, :identifier => gshixun.name,:project_id => -1, :shixun_id => -2)
# rep.update_column(:type, "Repository::Gitlab")
rep_url = Base64.urlsafe_encode64(gitlab_url shixun) # 注意educoder为默认给实训创建版本库的用户如果换成别的用户名字要相应的修改
logger.info("start openGameInstance")
uri = "#{shixun_tomcat}/bridge/game/openGameInstance"
logger.info("end openGameInstance")
params = {tpiID: "#{myshixun.id}", tpmGitURL:rep_url, tpiRepoName: gshixun.try(:name)}
logger.info("openGameInstance params is #{params}")
res = uri_exec uri, params
if (res && res['code'].to_i != 0)
raise("实训云平台繁忙繁忙等级83")
end
# 其它创建关卡等操作
challenges = shixun.challenges
# 之所以增加user_id是为了方便统计查询性能
challenges.each_with_index do |challenge, index|
status = (index == 0 ? 0 : 3)
code = down_generate_identifier("game")
Game.create!(:challenge_id => challenge.id, :myshixun_id => myshixun.id, :status => status, :user_id => myshixun.user_id,
:open_time => Time.now, :identifier => code, :modify_time => challenge.modify_time)
end
else
raise("实训云平台繁忙繁忙等级81")
end
# unlock
logger.info("myshixun id is #{myshixun.try(:identifier)} and current_task id is#{myshixun.try(:current_task).try(:id)}")
# 开启实训时更新关联作品的状态
update_myshixun_work_status myshixun
render :json => {:status => 1}
rescue Exception => e
if e.message == "shixun error"
flash[:error] = "正在后台执行,请稍后重试"
elsif e.message.include?("Mysql2::Error")
flash[:error] = "正在后台执行,请稍后重试"
else
flash[:error] = e.message
end
logger.info("failed to exec shixun: current task id is #{e}")
g.delete_project(gshixun.id) if gshixun.try(:id).present?
render :json => {:status => -1}
raise ActiveRecord::Rollback
end
end
end
def down_generate_identifier type
if type == "game"
code = DCODES.sample(12).join
return down_generate_identifier(type) if Game.where(identifier: code).present?
elsif type == "myshixun"
code = DCODES.sample(10).join
return down_generate_identifier(type) if Myshixun.where(identifier: code).present?
end
code
end
def game_build myshixun_id, game_id, gitUrl, step, language, mul_test, testcases, podtype, tpmScript, mirror_repositories, time_limit, file, port
logger.info("############### game_build")
shixun_tomcat = Redmine::Configuration['shixun_tomcat']
# 注意:这个地方的参数写的时候不能换行
params = {:tpiID => "#{myshixun_id}", :tpiGitURL => "#{gitUrl}", :buildID => "#{game_id}",:instanceChallenge => "#{step}",
:testCases => "#{testcases}", :resubmit => "", :times => 1, :podType => podtype,
:containers => "#{Base64.urlsafe_encode64(container_limit(mirror_repositories))}", :tpmScript => "#{tpmScript}",
:timeLimit => "#{time_limit}", :file => "#{file}", :needPortMapping => "#{port}"}
logger.info("############### start gameEvaluate")
uri = "#{shixun_tomcat}/bridge/game/gameEvaluate"
res = uri_exec uri, params
logger.info("############### end gameEvaluate")
if (res && res['code'] != 0)
@message = "实训云平台繁忙繁忙等级86"
raise("实训云平台繁忙繁忙等级86")
mul_test.update_attributes(:time => (Time.now - mul_test.updated_at), :status => 3)
else
mul_test.update_attribute(:time, res['costTime'].to_i)
end
end
# 临时使用
def destroy
if User.current.admin?
user = @myshixun.owner
@myshixun.games.each do |game|
ex =Experience.where(:container_type => "Game", :container_id => game.id).first
ex.destroy if ex.present?
gr = Grade.where(:container_type => "Game", :container_id => game.id).first
gr.destroy if gr.present?
grade = user.try(:grade).to_i - game.try(:final_score).to_i
grade = grade > 0 ? grade : 0
user.update_column(:grade, grade)
end
@myshixun.destroy
# 删除积分和经验值
#g = Gitlab.client
#g.delete_project(@myshixun.gpid) if @myshixun.gpid.present?
end
end
def sync_reset_time
@myshixun.update_column(:modify_time, @myshixun.shixun.try(:modify_time))
render :json => {:data => "success"}
end
# 将代码重置到开启状态
# 方法使用文件更新非Git重置
def code_reset
end
def code_reset_success
end
# 连接webssh
def open_webssh
username = Redmine::Configuration['webssh_username']
password = Redmine::Configuration['webssh_password']
old_time = Time.now.to_i
begin
shixun_tomcat = Redmine::Configuration['shixun_tomcat']
uri = "#{shixun_tomcat}/bridge/webssh/getConnectInfo"
user_id = User.current.id
params = {tpiID:@myshixun.id, podType:@myshixun.shixun.try(:webssh), containers:(Base64.urlsafe_encode64(container_limit @myshixun.shixun.mirror_repositories))}
res = uri_exec uri, params
if res && res['code'].to_i != 0
raise("实训云平台繁忙繁忙等级92")
end
render :json => {:host => res['address'],
:port => res['port'],
:ws_url => res['ws_address'],
:username => username,
:password => password,
:game_id => @myshixun.id,
:webssh_url => Redmine::Configuration['tomcat_webssh']}
rescue Exception => e
logger.error(e)
render :json => {:error => e.try(:message)}
# @message = e.try(:message)
ensure
use_time = Time.now.to_i - old_time
logger.info "open_webssh tpiID #{@myshixun.id} use time #{use_time}"
end
end
def vnc
vnc_password = Redmine::Configuration['vnc']
shixun_tomcat = Redmine::Configuration['shixun_tomcat']
host = Redmine::Configuration['tomcat_php']
begin
uri = "#{shixun_tomcat}/bridge/vnc/getvnc"
params = {tpiID:@myshixun.id}
res = uri_exec uri, params
if res && res['code'].to_i != 0
raise("实训云平台繁忙繁忙等级99")
end
# url = host + ":" + res['port'] + "?password=" + vnc_password
url = host +":#{res['port']}/vnc.html"
render :json => {:url => url}
rescue Exception => e
logger.error(e)
end
end
# 断开webssh连接
def close_webssh
username = Redmine::Configuration['webssh_username']
password = Redmine::Configuration['webssh_password']
begin
shixun_tomcat = Redmine::Configuration['shixun_tomcat']
uri = "#{shixun_tomcat}/bridge/webssh/deleteSSH"
user_id = User.current.id
logger.info("#############################")
logger.info("game_id:" + @_params[:id])
params = {tpiID:@_params[:id]} # @_params[:id] 是webssh传过来的gameId
res = uri_exec uri, params
if res && res['code'].to_i != 0
raise("实训云平台繁忙繁忙等级93")
end
render :jsonp => {:host => res['address'], :port => res['port'], :username => username, :password => password}
rescue Exception => e
logger.error(e)
@message = e.try(:message)
end
end
# 实训重置只更新脚本
def myshixun_reset
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
StudentWork.where(:myshixun_id => @myshixun.id).update_all(:myshixun_id => nil, :work_status => 0)
# 数据不关联删除,以后的数据可能要做行为分析
# @myshixun.destroy
@myshixun.delete
# 主从复制出现脏读的情况
if @myshixun.gpid
g.delete_project(@myshixun.gpid)
end
sleep(1)
redirect_to shixun_exec_shixun_path(shixun, :type => 1, :is_subject => params[:is_subject])
rescue Exception => e
flash[:error] = "实训云平台繁忙繁忙等级89"
redirect_to shixun_challenges_path(@shixun)
logger.error("myshixun reset failed #{e}")
# raise ActiveRecord::Rollback
end
# end
end
# 代码运行中的信息接口
def code_runinng_message
begin
6 years ago
logger.info("######################params: #{params}")
jsonTestDetails = JSON.parse(params[:jsonTestDetails])
game_id = jsonTestDetails['buildID']
message = jsonTestDetails['textMsg']
logger.info("##################code_runinng_message:#{jsonTestDetails}")
logger.info("##################buildID: #{game_id}")
logger.info("##################textMsg: #{message}")
if game_id.present? && message.present?
game = Game.find game_id
msg = game.run_code_message
# 只有评测中的game才会创建和更新代码评测中的信息
logger.info("##################game: #{game.status}")
logger.info("##################retry_status: #{game.retry_status}")
6 years ago
if game.status == 1 || game.status == 2
if msg.blank?
RunCodeMessage.create!(:game_id => game_id, :status => 1, :message => message)
else
msg.update_attributes(:status => (msg.status + 1), :message => message)
end
end
render :json => {:data => "success"}
end
rescue Exception => e
render :json => {:data => "failed, exception_message: #{e}"}
end
end
6 years ago
# taskId 即返回的game id
# 返回结果params [:stauts] 0 表示成功,其它则失败
# msg 错误信息
# output 为测试用户编译输出结果
# myshixun:status 1为完成实训
# @jenkins: caseId对应test_set的positionpassed: 1表示成功0表示失败
# resubmit 1表示已通关后重新评测0表示非重新评测
# retry_status 0初始值1重新评测失败2重新评测成功
# tpiRepoPath 中间层图片的workspace路径
# params[:jsonTestDetails] = '{"buildID":"19284","compileSuccess":"1",
# "msg":[{"caseId":"1","expectedOutput":"MSAyIDMNCg","input":"MiAzIDE","output":"MSAyIDMNCg","passed":"1"},
# {"caseId":"2","expectedOutput":"LTMgMSA2DQo","input":"LTMgNiAx","output":"LTMgMSA2DQo","passed":"1"},
# {"caseId":"3","expectedOutput":"LTcgLTUgLTMNCg","input":"LTcgLTMgLTU","output":"LTcgLTUgLTMNCg","passed":"1"}],
# "outPut":"Y29tcGlsZSBzdWNjZXNzZnVsbHk","resubmit":"","status":"0"}'
# params[:timeCost] = '{"evaluateEnd":"2017-11-24 11:04:37","pull":"0.086",
# "createPod":"1.610","evaluateAllTime":2820,"evaluateStart":"2017-11-24 11:04:35","execute":"0.294"}'
# params[:pics] = "a.png,b.png,c.png"
def training_task_status
ActiveRecord::Base.transaction do
begin
t1 = Time.now
jsonTestDetails = JSON.parse(params[:jsonTestDetails])
timeCost = JSON.parse(params[: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']
logger.info("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']
# message = Base64.decode64(params[:msg]) unless params[:msg].blank?
logger.info(outPut)
game = Game.find(game_id)
myshixun = game.myshixun
challenge = game.challenge
# test_sets = challenge.test_sets
if challenge.picture_path.present?
#pics = params[:files]
pics = params[:tpiRepoPath]
game.update_column(:picture_path, pics)
end
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_with_index do |j_test_set, i|
logger.info("j_test_set: ############## #{j_test_set}")
actual_output = tran_base64_decode64(j_test_set['output'])
# is_public = test_sets.where(:position => j_test_set['caseId']).first.try(:is_public)
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
logger.info("#############status: #{status}")
logger.info("#############resubmit: #{resubmit}")
record = EvaluateRecord.where(:game_id => game_id).first
logger.info("training_task_status start#3**#{game_id}**** #{Time.now.strftime("%Y-%m-%d %H:%M:%S.%L")}")
# status0表示评测成功
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 == 0 && (challenge.shixun.try(:status) > 1) # 如果没有查看答案,则获得该关卡得分
6 years ago
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
logger.info("training_task_status start#4**#{game_id}**** #{Time.now.strftime("%Y-%m-%d %H:%M:%S.%L")}")
render :json => {:data => "success"}
rescue Exception => e
render :json => {:data => "failed, errer_message:#{e}"}
6 years ago
logger.error("training_task_status error: #{e}")
raise ActiveRecord::Rollback
end
end
end
def show
respond_to do |format|
format.html{redirect_to myshixun_game_path(@myshixun.current_task, :myshixun_id => @myshixun)}
end
end
private
# Find myshixun of id params[:id]
def find_myshixun
@myshixun = Myshixun.find_by_identifier(params[:id])
render_404 unless @myshixun.present?
end
end