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.
educoder/app/controllers/myshixuns_controller.rb

383 lines
18 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

class MyshixunsController < ApplicationController
before_action :require_login, :check_auth, :except => [:training_task_status, :code_runinng_message]
before_action :find_myshixun, :except => [:training_task_status, :code_runinng_message]
before_action :find_repo_name, :except => [:training_task_status, :code_runinng_message]
skip_before_action :verify_authenticity_token, :only => [:html_content]
## TPI关卡列表
def challenges
# @challenges = Challenge.where(shixun_id: params[:shixun_id])
@shixun = @myshixun.shixun
@games = @myshixun.games.includes(:challenge).reorder("challenges.position")
@identity = current_user.game_identity(@games.first)
end
# For Admin
# 强制重置实训
# 前段需要按照操作过程提示
def reset_my_game
unless (current_user.admin? || current_user.id == @myshixun.user_id)
tip_exception("403", "")
end
begin
ActiveRecord::Base.transaction do
begin
@shixun = Shixun.select(:id, :identifier).find(@myshixun.shixun_id)
@myshixun.destroy!
StudentWork.where(:myshixun_id => @myshixun.id).update_all(:myshixun_id => 0, :work_status => 0)
# 实训在申请发布前,是否玩过实训,如果玩过需要更改记录,防止二次重置
shixun_mod = ShixunModify.where(:shixun_id => @shixun.id, :myshixun_id => @myshixun.id, :status => 1).take
shixun_mod.update_column(:status, 0) if shixun_mod
rescue Exception => e
logger.error("######reset_my_game_failed:#{e.message}")
raise("ActiveRecord::RecordInvalid")
end
end
# 删除版本库
GitService.delete_repository(repo_path: @repo_path)
rescue Exception => e
if e.message != "ActiveRecord::RecordInvalid"
logger.error("######delete_repository_error:#{e.message}")
end
raise "delete_repository_error:#{e.message}"
end
end
# 代码运行中的信息接口
# 这个方法是中间层主动调用的点击评测后中间层会发送参数过来告诉目前Pod的启动情况一次评测会调用两次请求
def code_runinng_message
begin
jsonTestDetails = JSON.parse(params[:jsonTestDetails])
game_id = jsonTestDetails['buildID']
message = jsonTestDetails['textMsg']
if game_id.present? && message.present?
game = Game.find game_id
msg = game.run_code_message
# 只有评测中的game才会创建和更新代码评测中的信息
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
# 中间层评测接口
# 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
Rails.logger.info("@@@222222#{params[:jsonTestDetails]}")
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']
sec_key = jsonTestDetails['sec_key']
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?
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")}")
max_query_index = game.outputs ? (game.outputs.first.try(:query_index).to_i + 1) : 1
test_set_score = 0
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'])
#ts_time += j_test_set['testSetTime'].to_i
# is_public = test_sets.where(:position => j_test_set['caseId']).first.try(:is_public)
logger.info "actual_output:################################################# #{actual_output}"
ts_time = format("%.2f", j_test_set['testSetTime'].to_f/1000000000).to_f if j_test_set['testSetTime']
ts_mem = format("%.2f", j_test_set['testSetMem'].to_f/1024/1024).to_f if j_test_set['testSetMem']
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 => max_query_index,
:compile_success => compile_success.to_i, :sec_key => sec_key, :ts_time => ts_time, :ts_mem => ts_mem)
# 如果设置了按测试集给分,则需要统计测试集的分值
if challenge.test_set_score && j_test_set['passed'].to_i == 1
test_set_score += challenge.test_sets.where(:position => j_test_set['caseId']).pluck(:score).first
end
end
end
uid_logger("#############status: #{status}")
record = EvaluateRecord.where(:identifier => sec_key).first
logger.info("training_task_status start#3**#{game_id}**** #{Time.now.strftime("%Y-%m-%d %H:%M:%S.%L")}")
answer_deduction_percentage = (100 - game.answer_deduction) / 100.to_f # 查看答案后剩余分数的百分比.
# answer_deduction是查看答案的扣分比例
# status0表示评测成功
if status == "0"
if resubmit.present?
uid_logger("#############resubmitdaiao: #{resubmit}")
game.update_attributes!(:retry_status => 2, :resubmit_identifier => resubmit)
challenge.path.split("").each do |path|
game_passed_code(path.try(:strip), myshixun, game_id)
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(path.try(:strip), myshixun, game_id)
end
# 如果是已经发布的实训,则需要给出相应的奖励
if challenge.shixun.try(:status) > 1
score = (challenge.score * answer_deduction_percentage).to_i
if score > 0
reward_attrs = { container_id: game.id, container_type: 'Game', score: score }
RewardGradeService.call(game.user, reward_attrs)
RewardExperienceService.call(game.user, reward_attrs)
end
# 需要扣除查看答案的分数
game.update_attributes!(:final_score => score)
end
# 更新实训关联的作品分数 TODO: 更新作品分数
# HomeworksService.new.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
# status == "-1" 表示返回结果错误
else
if resubmit.present?
game.update_attributes!(:retry_status => 1, :resubmit_identifier => resubmit)
else
# 评测没通关则,测试集对的个数给分,并且还要扣除用户是否查看答案的值
test_set_percentage = test_set_score / 100.to_f # 测试集得分比
score = (challenge.score * test_set_percentage * answer_deduction_percentage).to_i
# 如果分数比上次多,则更新成绩
game_update =
if game.final_score < score
{final_score: score, status: 0}
else
{status: 0}
end
game.update_attributes!(game_update)
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
uid_logger("training_task_status start#4**#{game_id}**** #{Time.now.strftime("%Y-%m-%d %H:%M:%S.%L")}")
sucess_status
# rescue Exception => e
# tip_exception(e.message)
# uid_logger_error("training_task_status error: #{e}")
# raise ActiveRecord::Rollback
end
end
end
# 连接webssh
def open_webssh
username = edu_setting('webssh_username')
password = edu_setting('webssh_password')
old_time = Time.now.to_i
begin
shixun_tomcat = edu_setting('tomcat_webssh')
uri = "#{shixun_tomcat}/bridge/webssh/getConnectInfo"
params = {tpiID:@myshixun.id, podType:@myshixun.shixun.try(:webssh),
containers:(Base64.urlsafe_encode64(shixun_container_limit @myshixun.shixun))}
res = uri_post uri, params
if res && res['code'].to_i != 0
tip_exception("实训云平台繁忙繁忙等级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 => "#{shixun_tomcat}/bridge"}
rescue Exception => e
logger.error(e)
render :json => {:error => 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
include GitCommon
# -----Repository
# TODO: 之类需要一个resubmit参数,但是是关于games.
def update_file
begin
@hide_code = Shixun.where(id: @myshixun.shixun_id).pluck(:hide_code).first
tip_exception("技术平台为空!") if @myshixun.mirror_name.blank?
path = params[:path].strip unless params[:path].blank?
game_id = params[:game_id]
game = Game.find(game_id)
@content_modified = 0
# params[:evaluate] 实训评测时更新必须给的参数,需要依据该参数做性能统计,其它类型的更新可以跳过
# 自动保存的时候evaluate为0点评测的时候为1
if params[:evaluate] == 1
@sec_key = generate_identifier(EvaluateRecord, 12)
record = EvaluateRecord.create!(:user_id => current_user.id, :shixun_id => @myshixun.shixun_id, :game_id => game_id,
:identifier => @sec_key)
uid_logger("-- game build: file update #{@sec_key}, record id is #{record.id}, time is **** #{Time.now.strftime("%Y-%m-%d %H:%M:%S.%L")}")
end
unless @hide_code
# 远程版本库文件内容
last_content = GitService.file_content(repo_path: @repo_path, path: path)["content"]
content = params[:content]
Rails.logger.info("###11222333####{content}")
Rails.logger.info("###222333####{last_content}")
if content != last_content
@content_modified = 1
author_name = current_user.real_name
author_email = current_user.git_mail
message = params[:evaluate] == 0 ? "System automatically submitted" : "User submitted"
uid_logger("112233#{author_name}")
uid_logger("112233#{author_email}")
@content = GitService.update_file(repo_path: @repo_path,
file_path: path,
message: message,
content: content,
author_name: author_name,
author_email: author_email)
end
end
if game.status == 2
@resubmit = Time.now.to_i
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
rescue Exception => e
uid_logger_error(e.message)
tip_exception("文件内容更新异常,请稍后重试")
end
end
# 渲染实训代码
# educodercss: 字符串以 分隔存储的是版本库css的路径
# educoderscript: 字符串以 分隔存储的是版本库js的路径
# contents html实训的整体内容
def html_content
@contents = params[:contents] || ""
edu_css = params[:educodercss]
edu_js = params[:educoderscript]
if @contents.present?
@contents = @contents.gsub("w3equalsign", "=").gsub("w3scrw3ipttag", "script").gsub("edulink", "link").html_safe
end
# css
if edu_css.present?
css_path = edu_css.split(",")
css_path.each do |path|
file_content = GitService.file_content(repo_path: @repo_path, path: path)["content"]
file_content = tran_base64_decode64(file_content) unless file_content.blank?
@contents = @contents.sub(/EDUCODERCSS/, "<style>#{file_content}</style>")
end
end
# js
if edu_js.present?
js_path = edu_js.split(",")
js_path.each do |path|
file_content = GitService.file_content(repo_path: @repo_path, path: path)["content"]
file_content = tran_base64_decode64(file_content) unless file_content.blank?
@contents = @contents.sub(/EDUCODERJS/, "<script>#{file_content}</script>")
end
end
respond_to do |format|
format.json
format.html{render :layout => false}
end
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/games/#{identifier}/game_build?resubmit=#{resubmit}&content_modified=0&first=1"
rescue Exception => e
logger.error("mul test failed ===> #{e.message}")
end
end
# -----End
private
def find_myshixun
@myshixun = Myshixun.find_by!(identifier: params[:identifier])
end
def find_repo_name
@repo_path = @myshixun.try(:repo_path)
@path = params[:path]
end
end