chromesetting
杨树林 5 years ago
commit c775eff55e

@ -160,5 +160,16 @@ $(document).on('turbolinks:load', function() {
$addMemberModal.modal('hide');
}
});
$(".laboratory-list-container").on("change", '.laboratory-sync-course', function () {
var s_id = $(this).attr("data-id");
var json = {};
$.ajax({
url: "/admins/laboratories/" + s_id + "/update_sync_course",
type: "POST",
dataType:'script',
data: json
})
});
}
});

@ -0,0 +1,2 @@
// Place all the behaviors and hooks related to the matching controller here.
// All this logic will automatically be available in application.js.

@ -0,0 +1,2 @@
// Place all the behaviors and hooks related to the matching controller here.
// All this logic will automatically be available in application.js.

@ -0,0 +1,3 @@
// Place all the styles related to the HackUserLastestCodes controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/

@ -0,0 +1,3 @@
// Place all the styles related to the hacks controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/

@ -56,6 +56,10 @@ class Admins::LaboratoriesController < Admins::BaseController
users.update_all(laboratory_id: current_laboratory.id)
end
def update_sync_course
current_laboratory.update!(sync_course: !current_laboratory.sync_course)
@laboratory = current_laboratory
end
private

@ -395,6 +395,50 @@ class ApplicationController < ActionController::Base
end
end
# 处理返回非0就报错的请求
def interface_post(uri, params, status, message)
begin
uid_logger_dubug("--uri_exec: params is #{params}, url is #{uri}")
uri = URI.parse(URI.encode(uri.strip))
res = Net::HTTP.post_form(uri, params).body
uid_logger_dubug("--uri_exec: .....res is #{res}")
res = JSON.parse(res)
if (res && res['code'] != 0)
tip_exception(status, message)
else
res
end
rescue Exception => e
uid_logger("--uri_exec: exception #{e.message}")
raise Educoder::TipException.new("实训平台繁忙繁忙等级84")
end
end
# json格式请求
def interface_json_post(uri, params, status, message)
begin
uid_logger_dubug("--uri_exec: params is #{params}, url is #{uri}")
uri = URI.parse(URI.encode(uri.strip))
res = Net::HTTP.start(uri.host, uri.port) do |http|
req = Net::HTTP::Post.new(uri)
req['Content-Type'] = 'application/json'
req.body = params.to_json
http.request(req)
end
uid_logger_dubug("--uri_exec: .....res is #{res.body}")
res = JSON.parse(res.body)
if (res && res['code'] != 0)
tip_exception(status, message)
else
res
end
rescue Exception => e
uid_logger("--uri_exec: exception #{e.message}")
raise Educoder::TipException.new("服务器繁忙")
end
end
# 适用与已经用url_safe编码后回调字符串形式
def tran_base64_decode64(str)
s_size = str.size % 4

@ -61,7 +61,7 @@ class CoursesController < ApplicationController
@user = current_user
# 根据分类查询课堂(全部,我的,最新,最热)
@order = params[:order].present? ? params[:order] : "all"
@courses = current_laboratory.courses.not_deleted
@courses = current_laboratory.all_courses.not_deleted
if @order == "visits"
order_str = "courses.id = 1309 DESC, courses.visits DESC"
@courses = @courses.where(is_hidden: 0)

@ -767,9 +767,9 @@ class GamesController < ApplicationController
# 记录实训花费的时间
# REDO:需要添加详细的说明
def cost_time
return if @game.status >= 2
cost_time = Time.now.to_i - @game.close_browse_time.to_i
@game.update_attributes(close_browse_time: Time.now, cost_time: cost_time)
#return if @game.status >= 2
cost_time = (params[:time].to_i < 0 ? 0 : params[:time].to_i) + @game.cost_time.to_i
@game.update_attribute(:cost_time, cost_time)
end
# 同步challenge的更新时间
@ -939,7 +939,6 @@ class GamesController < ApplicationController
game.update_attributes(status: 0, open_time: Time.now) if game.open_time.blank? || game.status == 3
# 开启实训更新myshixuns的时间方便跟踪用于的学习进度。
game.myshixun.update_column(:updated_at, Time.now)
@game.update_attribute(:close_browse_time, Time.now) if game.status < 2 && @game.close_browse_time.nil?
end
# vnc连接

@ -38,7 +38,6 @@ class GitsController < ApplicationController
else
# 用户是否对对象拥有权限
system_user = User.find_by_login(input_username) || User.find_by_mail(input_username) || User.find_by_phone(input_username)
# 如果用户名密码错误
if system_user && !system_user.check_password?(input_password)
uid_logger_error("git start: password is wrong")
@ -49,7 +48,7 @@ class GitsController < ApplicationController
shixunname = git_url.split("/")[1].split(".")[0]
repo_name = username + "/" + shixunname
uid_logger("git start: repo_name is #{repo_name}")
shixun = Shixun.select([:id, :user_id, :repo_name, :identifier]).where(repo_name: repo_name, laboratory_id: nil).first
shixun = Shixun.select([:id, :user_id, :repo_name, :identifier]).where(repo_name: repo_name).first
uid_logger("git start auth: shixun identifier is #{shixun.try(:identifier)}")
uid_logger("git start auth: systemuser is #{system_user.try(:login)}")
@ -64,7 +63,7 @@ class GitsController < ApplicationController
else
uid_logger_error("shixun is not exist")
# result = false
result = true # 为了测试跳出
result = false # 为了测试跳出
end
end
end

@ -0,0 +1,170 @@
class HackUserLastestCodesController < ApplicationController
before_action :require_login, except: [:listen_result]
before_action :find_my_hack, only: [:show, :code_debug, :code_submit, :update_code, :listen_result, :result]
before_action :update_user_hack_status, only: [:code_debug, :code_submit]
before_action :require_auth_identity, only: [:update_code]
before_action :require_manager_identity, only: [:update_code]
def show
@my_hack.update_attribute(:status, 0) if @my_hack.status == 1
end
def update_code
@my_hack.update_attribute(:code, params[:code])
end
# 调试代码
def code_debug
exec_mode = "debug"
error_status = 501
error_msg = "debug_error"
oj_evaluate exec_mode, error_status, error_msg
render_ok
end
# 提交
def code_submit
exec_mode = "submit"
error_status = 502
error_msg = "submit_error"
oj_evaluate exec_mode, error_status, error_msg
render_ok
end
# 提交结果显示
def result
return if @my_hack.status == 1
end
# 接收中间件返回结果接口
# 调试模式: status 0 表示评测无错误,其他 表示错误(如编译出错,执行出错,超时等)
def listen_result
logger.info("###########listen_result#{params}")
begin
ojEvaResult = JSON.parse(params[:ojEvaResult])
testCase = ojEvaResult['testCase']
# 只有编译出错时,才正则匹配错误行数
error_line=
if params[:status] == "-4"
regular_match_error_line ojEvaResult['outPut'], @my_hack.hack.language
end
# debug 与submit 公用的参数
ds_params = {input: testCase['input'], output: testCase['output'], hack_id: @hack.id,
code: ojEvaResult['codeFileContent'], user_id: @my_hack.user_id, error_line: error_line,
status: ojEvaResult['status'], error_msg: ojEvaResult['outPut'],
execute_time: ojEvaResult['executeTime'], execute_memory: ojEvaResult['executeMem']}
ActiveRecord::Base.transaction do
# debug模式与submit模式
if ojEvaResult['execMode'] == "debug"
save_debug_data ds_params
elsif ojEvaResult['execMode'] == "submit"
save_submit_data ds_params
end
# 评测完成后,还原评测中的状态
@my_hack.update_attribute(:submit_status, 0)
end
render_ok
rescue Exception => e
logger.error("#########listen_result: #{e.message}")
end
end
private
def find_my_hack
@my_hack = HackUserLastestCode.find_by(identifier: params[:identifier])
@hack = @my_hack.hack
end
def oj_evaluate exec_mode, error_status, error_msg
request_url = "#{edu_setting('cloud_bridge')}/bridge/ojs/evaluate"
test_sets =
if exec_mode == "submit"
@hack.hack_sets.map{|set| {input: set.input, output: set.output, caseId: set.id}}
else
{input: params[:input]}
end
testCases = Base64.urlsafe_encode64(test_sets.to_json)
#codeFileContent = Base64.urlsafe_encode64(@my_hack.code)
debug_params = {execMode: exec_mode,
tpiID: @my_hack.identifier,
testCases: testCases,
platform: @my_hack.language,
codeFileContent: @my_hack.code,
timeLimit: @hack.time_limit,
sec_key: Time.now.to_i}
interface_json_post request_url, debug_params, error_status, error_msg
# 每次评测提交数增加
@hack.increment!(:submit_num)
end
# 正则错误行数
def regular_match_error_line content, language
case language
when 'Java'
content.scan(/.java.\d+/).map{|s| s.match(/\d+/)[0].to_i}.min
when 'C', 'C++'
content.scan(/\d:\d+: error/).map{|s| s.match(/\d+/)[0]}.min
when 'Python'
content.scan(/line \d+/).map{|s| s.match(/\d+/)[0].to_i}.min
end
end
# 存储debug数据
def save_debug_data debug_params
if @my_hack.hack_user_debug.present?
@my_hack.hack_user_debug.update_attributes!(debug_params)
else
@my_hack.hack_user_debug.create!(debug_params)
end
end
# 存储submit数据
def save_submit_data submit_params
# 通关
if submit_params[:status] == "0"
# 编程题已经发布,且之前未通关奖励积分
if @hack.status == 1 && !@my_hack.passed?
reward_attrs = { container_id: game.id, container_type: 'Hack', score: @hack.score }
RewardGradeService.call(@my_hack.user, reward_attrs)
RewardExperienceService.call(@my_hack.user, reward_attrs)
# 评测完成更新通过数
@hack.increment!(:pass_num)
@my_hack.update_attribute(:passed, true)
end
end
# 创建用户评测记录
logger.info("###########submit_params:#{submit_params}")
query_index = @my_hack.hack_user_codes.count +1
@my_hack.hack_user_codes.create!(submit_params.merge(query_index: query_index))
end
# 调试或提交改变状态
def update_user_hack_status
@my_hack.update_attribute(:submit_status, 1)
end
# 只有自己才能改动代码
def require_identity
if @my_hack.user_id != current_user.id
tip_exception(403, "..")
end
end
# 老师、自己、管理可以查看他人的编程题
def require_manager_identity
unless current_user.certification_teacher? || admin_or_business? || @my_hack.user_id == current_user.id
tip_exception(403, "..")
end
end
# 只有自己才能评测
def require_auth_identity
unless @my_hack.user_id == current_user.id
tip_exception(403, "..")
end
end
end

@ -0,0 +1,180 @@
class HacksController < ApplicationController
before_action :require_login, except: [:index]
before_action :require_teacher_identity, only: [:create, :edit, :update]
before_action :require_auth_identity, only: [:update, :edit, :publish]
before_action :find_hack, only: [:edit, :update, :publish, :start]
# 开启编程,如果第一次开启,创建一条记录,如果已经开启过的话,直接返回标识即可
def start
# 未发布的编程题,只能作者、或管理员访问
start_hack_auth
user_hack = @hack.hack_user_lastest_codes.mine(current_user.id)
identifier =
if user_hack.present?
user_hack.identifier
else
user_identifier = generate_identifier HackUserLastestCode, 12
user_code = {user_id: current_user.id, code: @hack.code,
identifier: user_identifier, language: @hack.language}
@hack.hack_user_lastest_codes.create!(user_code)
user_identifier
end
render_ok(data: {identifier: identifier})
end
# 首页
def index
# 筛选过滤与排序
params_filter_or_order
# 我解决的编程题数
user_codes = HackUserLastestCode.mine(current_user).passed.joins(:hack)
@simple_count = user_codes.where(hacks: {difficult: 1}).count
@medium_count = user_codes.where(hacks: {difficult: 2}).count
@diff_count = user_codes.where(hacks: {difficult: 3}).count
@pass_count = @simple_count + @medium_count + @diff_count
@hacks_count = @hacks.count("hacks.id")
@hacks = paginate @hacks
end
def create
begin
logger.info("##########{hack_params}")
hack = Hack.new(hack_params)
ActiveRecord::Base.transaction do
hack.user_id = current_user.id
hack.identifier = generate_identifier Hack, 8
hack.save!
# 创建测试集与代码
hack.hack_sets.create!(hack_sets_params)
hack.hack_codes.create!(hack_code_params)
end
render_ok({identifier: hack.identifier})
rescue Exception => e
logger.error("########create_hack_error: #{e.message}")
render_error("创建失败")
end
end
def update
begin
ActiveRecord::Base.transaction do
@hack.update_attributes!(hack_params)
set_ids = @hack.hack_sets.pluck(:id)
# 更新
param_update_sets params[:update_hack_sets], set_ids
# 新建
@hack.hack_sets.create!(hack_sets_params)
# 更新代码
@hack.hack_codes.create!(hack_code_params)
end
render_ok
rescue Exception => e
logger.error("####update_hack_error: #{e.message}")
render_error("更新失败")
end
end
# 发布功能
def publish
@hack.update_attribute(:status, 1)
render_ok
end
# 发布列表
def unpulished_list
limit = params[:limit] || 16
page = params[:page] || 1
hacks = Hack.where(user_id: current_user.id, status: 0)
@hacks_count = hacks.count
@hacks = hacks.includes(:hack_sets).page(page).per(limit)
end
def edit;end
private
# 实名认证老师,管理员与运营人员权限
def require_teacher_identity
unless current_user.certification_teacher? || admin_or_business?
tip_exception(403, "..")
end
end
# 只有自己,或者管理员才能更新
def require_auth_identity
unless @hack.user_id == current_user.id || admin_or_business?
tip_exception(403, "..")
end
end
def find_hack
@hack = Hack.find_by_identifier(params[:identifier])
end
def hack_params
params.require(:hack).permit(:name, :description, :difficult, :category, :open_or_not, :time_limit, :score)
end
def hack_sets_params
params.permit(hack_sets: [:input, :output, :position])[:hack_sets]
end
def hack_code_params
params.require(:hack_codes).permit(:code, :language)
end
def publish_params
params.require(:hack).permit(:difficult, :category, :open_or_not, :time_limit, :score)
end
def param_update_sets sets, all_sets_id
delete_set_ids = all_sets_id - sets.map{|set|set[:id]}
@hack.hack_sets.where(id: delete_set_ids).destroy_all
sets.each do |set|
if all_sets_id.include?(set[:id])
update_attrs = {input: set[:input], output: set[:output], position: set[:position]}
@hack.hack_sets.find_by!(id: set[:id]).update_attributes(update_attrs)
end
end
end
def params_filter_or_order
# 如果有来源,就不管发布公开私有
select_sql = "hacks.*, if(hacks.hack_user_lastest_codes_count=0, 0, hacks.pass_num/hacks.hack_user_lastest_codes_count) passed_rate"
if params[:come_from]
hacks = Hack.select(select_sql).mine(current_user.id)
else
hacks = Hack.select(select_sql).published.opening
end
# 搜索
if params[:search]
hacks = hacks.where("name like ?", "%#{params[:search]}%")
end
# 难度
if params[:difficult]
hacks = hacks.where(difficult: params[:difficult])
end
# 状态
if params[:status]
user_hacks = HackUserLastestCode.where(user_id: current_user.id)
if params[:status].to_i == -1
if user_hacks.present?
hacks = hacks.where.not(id: user_hacks.pluck(:hack_id))
end
else
hacks = hacks.joins(:hack_user_lastest_code).where(hack_user_lastest_code: {status: params[:status]})
end
end
# 排序
sort_by = params[:sort_by] || "hack_user_lastest_codes_count"
sort_direction = params[:sort_direction] || "desc"
@hacks = hacks.order("#{sort_by} #{sort_direction}")
end
def start_hack_auth
return true if @hack == 1
require_auth_identity
end
end

@ -489,10 +489,7 @@ class ShixunsController < ApplicationController
ShixunMirrorRepository.create(:shixun_id => @shixun.id, :mirror_repository_id => mirror)
end
end
logger.info("#########shixun_params#{shixun_params}")
@shixun.update_attributes(shixun_params)
logger.info("##########shixun_info_params: #{shixun_info_params}")
logger.info("##########params[:shixun_info][:evaluate_script]: #{params[:shixun_info][:evaluate_script]}")
@shixun.shixun_info.update_attributes(shixun_info_params)
@shixun.shixun_schools.delete_all
# scope_partment: 高校的名称
@ -980,9 +977,7 @@ class ShixunsController < ApplicationController
@courses = Course.where(:id => course_ids)
## 云上实验室过滤
unless current_laboratory.main_site?
@courses = @courses.where(laboratory_id: current_laboratory.id )
end
@courses = @courses.where(id: current_laboratory.all_courses)
@course_count = @courses.count
@courses = @courses.page(page).per(limit)

@ -218,9 +218,8 @@ class SubjectsController < ApplicationController
AND m.user_id=#{current_user.id} AND c.is_delete = 0 AND c.is_end = 0").map(&:id)
@courses = Course.where(id: course_ids)
## 云上实验室过滤
unless current_laboratory.main_site?
@courses = @courses.where(laboratory_id: current_laboratory.id )
end
@courses = @courses.where(id: current_laboratory.all_courses)
@none_shixun_ids = ShixunSchool.where("school_id != #{current_user.user_extension.try(:school_id).to_i}").pluck(:shixun_id)
end

@ -2,7 +2,7 @@ class Users::CoursesController < Users::BaseController
def index
courses = Users::CourseService.new(observed_user, query_params).call
courses = courses.where(laboratory_id: current_laboratory.id)
courses = courses.where(id: current_laboratory.all_courses)
@count = courses.count
@courses = paginate(courses.includes(teacher: { user_extension: :school }), special: observed_user.is_teacher?)

@ -1,7 +1,8 @@
class Weapps::CoursesController < Weapps::BaseController
before_action :require_login
before_action :user_course_identity, except: [:create]
before_action :teacher_allowed, except: [:create, :show, :shixun_homework_category]
before_action :teacher_allowed, only: [:edit, :update]
before_action :teacher_or_admin_allowed, only: [:change_member_roles, :delete_course_teachers]
def create
return render_error("只有老师身份才能创建课堂") unless current_user.is_teacher?
@ -48,7 +49,98 @@ class Weapps::CoursesController < Weapps::BaseController
@teacher_list = @teacher_list.preload(user: [user_extension: :school]).order("CONVERT(CONCAT(users.lastname, users.firstname) USING gbk) COLLATE gbk_chinese_ci asc")
end
# 批量删除教师或助教
def delete_course_teachers
begin
@course = current_course
course_members = @course.course_members.where(id: params[:course_member_ids], role: %i[PROFESSOR ASSISTANT_PROFESSOR])
user_ids = course_members.pluck(:user_id)
course_members.destroy_all
CourseDeleteStudentNotifyJob.perform_later(@course.id, user_ids, current_user.id)
@course.students.where(user_id: user_ids).update_all(is_active: 1)
normal_status(0, "删除成功")
rescue => e
uid_logger_error(e.message)
tip_exception("删除失败")
end
end
def students
@course = current_course
course_group_id = params[:course_group_id].present? ? params[:course_group_id].to_i : nil
@students = CourseMember.students(@course)
if course_group_id.present?
course_group = CourseGroup.find(course_group_id) if course_group_id != 0
@students = @students.where(course_group_id: course_group&.id.to_i)
end
@students_count = @students.size
@students = @students.includes(user: :user_extension)
end
# 批量修改角色
def change_member_roles
tip_exception("请至少选择一个角色") if params[:roles].blank?
tip_exception("不能具有老师、助教两种角色") if params[:roles].include?("PROFESSOR") && params[:roles].include?("ASSISTANT_PROFESSOR")
params[:user_ids].each do |user_id|
course_members = @course.course_members.where(user_id: user_id)
tip_exception("非课堂成员不能修改角色") if course_members.blank?
ActiveRecord::Base.transaction do
# 第一次修改为教师或助教身份时直接创建数据
if params[:roles].include?("CREATOR")
teacher_member = course_members.where(role: %i[CREATOR]).take
elsif (params[:roles].include?("PROFESSOR") || params[:roles].include?("ASSISTANT_PROFESSOR")) && !course_members.exists?(role: %i[CREATOR PROFESSOR ASSISTANT_PROFESSOR])
teacher_member = CourseMember.create!(course_id: @course.id, user_id: user_id, role: params[:roles].include?("PROFESSOR") ? 2 : 3)
# 如果有未审批的申请教师/助教的记录,则修改状态为已审批
apply_teacher = CourseMessage.where(course_id: @course.id, course_message_id: user_id, status: 0).last
apply_teacher.update!(status: 1, apply_user_id: current_user.id)
elsif course_members.exists?(role: %i[PROFESSOR ASSISTANT_PROFESSOR])
teacher_member = course_members.where(role: %i[PROFESSOR ASSISTANT_PROFESSOR]).take
if params[:roles].include?("PROFESSOR") || params[:roles].include?("ASSISTANT_PROFESSOR")
# 如果之前有老师身份且老师身份要调整时只需要修改role字段
if !params[:roles].include?(teacher_member.role) && params[:roles].include?("PROFESSOR")
teacher_member.PROFESSOR!
elsif !params[:roles].include?(teacher_member.role) && params[:roles].include?("ASSISTANT_PROFESSOR")
teacher_member.ASSISTANT_PROFESSOR!
end
teacher_member.save!
else
# 不含教师的参数时删除记录
teacher_member.destroy!
# CourseDeleteStudentNotifyJob.perform_later(@course.id, [teacher_member.user_id], current_user.id)
end
end
# 学生身份的处理
student_member = course_members.where(role: %i[STUDENT]).take
# 不存在则创建学生身份
if params[:roles].include?("STUDENT") && student_member.blank?
correspond_teacher_exist = CourseMember.exists?(user_id: user_id, is_active: 1, course_id: @course.id, role: %i[CREATOR PROFESSOR ASSISTANT_PROFESSOR])
new_student = CourseMember.new(user_id: user_id, course_id: @course.id, role: 4)
new_student.is_active = 0 if correspond_teacher_exist
new_student.save!
CourseAddStudentCreateWorksJob.perform_later(@course.id, user_id)
# StudentJoinCourseNotifyJob.perform_later(current_user.id, course.id)
elsif !params[:roles].include?("STUDENT") && student_member.present?
# 删除学生身份时激活老师身份
teacher_member.update_attributes!(is_active: 1) if student_member.is_active && teacher_member.present?
student_member.destroy!
CourseDeleteStudentDeleteWorksJob.perform_later(@course.id, user_id)
# CourseDeleteStudentNotifyJob.perform_later(@course.id, [params[:user_id]], current_user.id)
elsif params[:roles].include?("STUDENT") && student_member.present? && !params[:roles].include?("PROFESSOR") && !params[:roles].include?("ASSISTANT_PROFESSOR")
# 学生身份存在且学生没有教师身份时更新is_active
student_member.update_attributes!(is_active: 1)
end
end
end
normal_status(0, "修改成功")
end
private
@ -67,4 +159,11 @@ class Weapps::CoursesController < Weapps::BaseController
def teacher_allowed
return render_forbidden unless @user_course_identity < Course::STUDENT
end
# 课堂教师,课堂管理员以及超级管理员的权限判断
def teacher_or_admin_allowed
unless @user_course_identity < Course::ASSISTANT_PROFESSOR
tip_exception(403, "..")
end
end
end

@ -0,0 +1,2 @@
module HackUserLastestCodesHelper
end

@ -0,0 +1,2 @@
module HacksHelper
end

@ -3,7 +3,7 @@
# modify_time 与challenges表的modify_time联合使用2个字段一致则标识测试集未修改反之被修改
# answer_open: 查看查看答案的深度, 0: 未查看过答案, 其他数值与challenge_answer的level值相关
# answer_deduction: 查看答案扣分的百分比;如 查看答案 扣除70%
#
#play_sign 与play_time: sign记录浏览器是否正常关闭 0表示正常1表示非正常 play_time表示游玩时间
class Game < ApplicationRecord
default_scope { order("games.created_at desc") }

@ -0,0 +1,38 @@
class Hack < ApplicationRecord
# status: 0 未发布; 1已发布
# diffcult: 难度 1简单2中等 3困难
# 编程题
validates_length_of :name, maximum: 60
# 测试集
has_many :hack_sets, ->{order("position asc")}, :dependent => :destroy
# 代码
has_many :hack_codes, :dependent => :destroy
has_many :hack_user_lastest_codes, :dependent => :destroy
belongs_to :user
scope :published, -> { where(status: 1) }
scope :opening, -> {where(open_or_not: 1)}
scope :mine, -> (author_id){ where(user_id: author_id) }
def language
if hack_codes.count == 1
hack_codes.first.language
else
hack_codes.pluck(:language).first
end
end
def code
if hack_codes.count == 1
tran_base64_decode64(hack_codes.first.code)
else
tran_base64_decode64(hack_codes.pluck(:code))
end
end
# 用于用户调试的第一个测试用例
def input_test_case
hack_sets.first&.input
end
end

@ -0,0 +1,3 @@
class HackCode < ApplicationRecord
# 编程题代码相关
end

@ -0,0 +1,4 @@
class HackSet < ApplicationRecord
# 编程题测试集
belongs_to :hack
end

@ -0,0 +1,4 @@
class HackUserCode < ApplicationRecord
# 用户编程题的信息
belongs_to :hack
end

@ -0,0 +1,4 @@
class HackUserDebug < ApplicationRecord
belongs_to :hack
belongs_to :hack_user_lastest_code
end

@ -0,0 +1,13 @@
class HackUserLastestCode < ApplicationRecord
# passed 用户之前评测是否通过
# status: 最新评测状态: -1测试用例结果不匹配; 0: 评测通过; ;2 评测超时;3 创建pod失败; 4 编译失败;5 执行失败
# submit_status: 0: 可以评测, 1评测中
# 编程题最新代码
belongs_to :hack, counter_cache: true
belongs_to :user
has_many :hack_user_codes, dependent: :destroy
has_one :hack_user_debug
scope :mine, ->(author_id){ find_by(user_id: author_id) }
scope :passed, -> {where(status: 1)}
end

@ -54,6 +54,10 @@ class Laboratory < ApplicationRecord
main_site? ? Subject.all : Subject.joins(:laboratory_subjects).where(laboratory_subjects: { laboratory_id: id })
end
def all_courses
main_site? || !sync_course ? courses : courses.or(Course.where(school_id: school_id))
end
def shixun_repertoires
where_sql = ShixunTagRepertoire.where("shixun_tag_repertoires.tag_repertoire_id = tag_repertoires.id")

@ -39,7 +39,7 @@ module Searchable::Shixun
end
def should_index?
[0, 1, 2].include?(status) # published
!hidden? && [0, 1, 2].include?(status) # published
end
def to_searchable_json

@ -149,6 +149,9 @@ class User < ApplicationRecord
# 客户管理
has_many :partner_managers, dependent: :destroy
# OJ编程题
has_many :hacks, dependent: :destroy
has_many :hack_user_lastest_codes, dependent: :destroy
# Groups and active users
scope :active, lambda { where(status: STATUS_ACTIVE) }
@ -696,10 +699,6 @@ class User < ApplicationRecord
LimitForbidControl::UserLogin.new(self).clear
end
def from_sub_site?
laboratory_id.present? && laboratory_id != 1
end
protected
def validate_password_length
# 管理员的初始密码是5位

@ -5,7 +5,8 @@ class Subjects::CopySubjectService < ApplicationService
@subject = subject
@user = user
@laboratory = laboratory
subject_params = subject.attributes.dup.except('id', 'copy_subject_id', 'user_id', 'homepage_show')
subject_params = subject.attributes.dup.except('id', 'copy_subject_id', 'user_id', 'homepage_show',
'stages_count', 'shixuns_count', 'stage_shixuns_count')
@to_subject = Subject.new(subject_params)
end
@ -59,7 +60,7 @@ class Subjects::CopySubjectService < ApplicationService
shixun = stage_shixun.shixun
to_shixun = Shixun.new
to_shixun.attributes = shixun.attributes.dup.except('id', 'user_id', 'identifier', 'homepage_show',
'use_scope', 'averge_star', 'myshixuns_count')
'use_scope', 'averge_star', 'myshixuns_count', 'challenges_count')
to_shixun.identifier = Util::UUID.generate_identifier(Shixun, 8)
to_shixun.user_id = user.id
if laboratory

@ -29,6 +29,11 @@
</div>
</td>
<td><%= laboratory.created_at.strftime('%Y-%m-%d %H:%M') %></td>
<td>
<% if school.present? && laboratory.id != 1 %>
<%= check_box_tag :sync_course,!laboratory.sync_course,laboratory.sync_course,remote:true,data:{id:laboratory.id},class:"laboratory-sync-course" %>
<% end %>
</td>
<td class="action-container">
<%= link_to '定制', admins_laboratory_laboratory_setting_path(laboratory), class: 'action' %>

@ -1,11 +1,12 @@
<table class="table table-hover text-center laboratory-list-table">
<thead class="thead-light">
<tr>
<th width="20%" class="text-left">单位名称</th>
<th width="14%" class="text-left">单位名称</th>
<th width="16%" class="text-left">域名</th>
<th width="10%">统计链接</th>
<th width="22%">管理员</th>
<th width="14%"><%= sort_tag('创建时间', name: 'id', path: admins_laboratories_path) %></th>
<th width="6%" title="同步显示显示主站下该单位的课堂">同步课堂</th>
<th width="20%">操作</th>
</tr>
</thead>

@ -0,0 +1 @@
$("#laboratory-item-<%= @laboratory.id %>").html("<%= j render partial: 'admins/laboratories/shared/laboratory_item', locals: {laboratory: @laboratory} %>")

@ -0,0 +1,12 @@
json.hack do
json.(@hack, :difficult, :time_limit, :description, :score, :identifier)
json.language @hack.language
json.username @hack.user.real_name
json.code @my_hack.code
json.pass_count @hack.pass_num
json.submit_count @hack.submit_num
end
json.test_case do
json.input @hack.input_test_case
end

@ -0,0 +1,13 @@
# 编程内容
json.(@hack, :name, :description, :language, :code)
# 代码
json.language @hack.language
json.code @hack.code
# 测试集
json.hack_sets do
json.array! @hack.hack_sets do |set|
json.(set, :id, :input, :output, :position)
end
end

@ -0,0 +1,13 @@
json.top_data do
json.passed_count @pass_count
json.simple_count @simple_count
json.medium_count @medium_count
json.diff_count @diff_count
end
json.hacks_count @hacks_count
json.hacks_list do
json.array! @hacks do |hack|
json.(hack,:identifier, :name , :hack_user_lastest_codes_count, :difficult, :passed_rate, :category)
end
end

@ -0,0 +1,5 @@
json.hacks_count @hacks_count
json.hacks @hacks do |hack|
json.(hack, :name, :difficult, :updated_at)
json.sets_count hack.hack_sets.count
end

@ -1,4 +1,4 @@
json.(@course, :id, :name, :course_members_count, :credit)
json.(@course, :id, :name, :course_members_count, :credit, :invite_code_halt)
json.teachers_count @course.teachers.count
json.students_count @course.students.count
json.course_identity @current_user.course_identity(@course)

@ -0,0 +1,12 @@
json.students do
json.array! @students do |student|
json.user_id student.user_id
json.login student.user.try(:login)
json.name student.user.try(:real_name)
json.student_id student.user.try(:student_id)
json.course_member_id student.id
json.user_phone @course.excellent ? "" : student.user.hidden_phone
json.image_url url_to_avatar(student.user)
end
end
json.students_count @students_count

@ -35,6 +35,28 @@ Rails.application.routes.draw do
end
end
resources :hacks, path: :problems, param: :identifier do
collection do
get :unpulished_list
end
member do
post :publish
get :start
get :result
end
end
resources :hack_user_lastest_codes, path: :myproblems, param: :identifier do
member do
post :update_code
get :code_debug
get :code_submit
match :listen_result, :via => [:get, :post]
end
end
resources :tem_tests
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
#
@ -64,7 +86,7 @@ Rails.application.routes.draw do
resources :shixuns, only: [:index]
resources :projects, only: [:index]
resources :subjects, only: [:index]
# resources :question_banks, only: [:index]
# hacks :question_banks, only: [:index]
resource :experience_records, only: [:show]
resource :grade_records, only: [:show]
resource :watch, only: [:create, :destroy]
@ -884,6 +906,10 @@ Rails.application.routes.draw do
member do
get :shixun_homework_category
get :teachers
delete :delete_course_teachers
post :change_member_roles
get :students
delete :delete_course_students
end
end
end
@ -1051,6 +1077,7 @@ Rails.application.routes.draw do
get :shixuns_for_select
get :subjects_for_select
get :synchronize_user
post :update_sync_course
end
resource :laboratory_setting, only: [:show, :update]

@ -0,0 +1,20 @@
class CreateHacks < ActiveRecord::Migration[5.2]
def change
create_table :hacks do |t|
t.string :name
t.integer :difficult
t.integer :category
t.integer :status, default: 0
t.boolean :open_or_not, default: true
t.integer :time_limit
t.integer :memory_limit
t.string :identifier
t.references :user
t.text :description
t.timestamps
end
add_index :hacks, :identifier, unique: true
end
end

@ -0,0 +1,11 @@
class CreateHackCodes < ActiveRecord::Migration[5.2]
def change
create_table :hack_codes do |t|
t.references :hack
t.text :code
t.string :language
t.timestamps
end
end
end

@ -0,0 +1,13 @@
class CreateHackSets < ActiveRecord::Migration[5.2]
def change
create_table :hack_sets do |t|
t.references :hack
t.text :input
t.text :output
t.integer :position
t.timestamps
end
add_index :hack_sets, [:hack_id, :position], unique: true
end
end

@ -0,0 +1,18 @@
class CreateHackUserCodes < ActiveRecord::Migration[5.2]
def change
create_table :hack_user_codes do |t|
t.references :user
t.references :hack
t.text :code
t.text :output
t.text :error_msg
t.integer :error_line
t.integer :position
t.integer :status, default: 0
t.integer :query_index
t.timestamps
end
add_index :hack_user_codes, [:user_id, :hack_id, :query_index], unique: true
end
end

@ -0,0 +1,12 @@
class CreateHackUserLastestCodes < ActiveRecord::Migration[5.2]
def change
create_table :hack_user_lastest_codes do |t|
t.references :user
t.references :hack
t.text :code
t.timestamps
end
add_index :hack_user_lastest_codes, [:user_id, :hack_id], unique: true
end
end

@ -0,0 +1,14 @@
class CreateHackUserDebugs < ActiveRecord::Migration[5.2]
def change
create_table :hack_user_debugs do |t|
t.references :user
t.references :hack
t.text :output
t.text :input
t.text :error_msg
t.integer :error_line
t.timestamps
end
end
end

@ -0,0 +1,7 @@
class AddDefualtForHacks < ActiveRecord::Migration[5.2]
def change
change_column :hacks, :status, :integer, :default => 0
change_column :hacks, :open_or_not, :boolean, :default => true
change_column :hack_user_codes, :status, :integer, :default => 0
end
end

@ -0,0 +1,5 @@
class AddScoreForHacks < ActiveRecord::Migration[5.2]
def change
add_column :hacks, :score, :integer
end
end

@ -0,0 +1,27 @@
class AddStatusEndTimeForHackUserLastestCode < ActiveRecord::Migration[5.2]
def change
add_column :hack_user_lastest_codes, :passed, :boolean, :default => false
add_column :hack_user_lastest_codes, :pass_time, :timestamp
add_column :hack_user_lastest_codes, :identifier, :string
add_column :hack_user_lastest_codes, :language, :string
add_column :hack_user_debugs, :hack_user_lastest_code_id, :integer
add_column :hacks, :hack_user_lastest_codes_count, :integer, :default => 0
add_column :hacks, :pass_num, :integer, :default => 0
add_column :hacks, :submit_num, :integer, :default => 0
add_column :hack_user_codes, :input, :text
add_index :hack_user_lastest_codes, :user_id, name: "user_index"
add_index :hack_user_lastest_codes, :hack_id, name: "hack_index"
add_index :hack_user_lastest_codes, :identifier, unique: true
add_index :hack_user_debugs, :hack_user_lastest_code_id, unique: true
add_column :hack_user_lastest_codes, :submit_status, :integer, :default => 0
add_column :hack_user_lastest_codes, :status, :integer
add_column :hack_user_lastest_codes, :passed_time, :timestamp
add_column :hack_user_debugs, :status, :integer
add_column :hack_user_codes, :execute_time, :integer
add_column :hack_user_codes, :execute_memory, :integer
add_column :hack_user_debugs, :execute_time, :integer
add_column :hack_user_debugs, :execute_memory, :integer
end
end

@ -0,0 +1,7 @@
class AddHackUserLastestCodeIdForHackUserCode < ActiveRecord::Migration[5.2]
def change
add_column :hack_user_codes, :hack_user_lastest_code_id, :integer
add_index :hack_user_codes, :hack_user_lastest_code_id
end
end

@ -1,5 +0,0 @@
class AddCloseBrowserTimeForGames < ActiveRecord::Migration[5.2]
def change
add_column :games, :close_browse_time, :timestamp
end
end

@ -0,0 +1,5 @@
class AddSyncCourseToLaboratorySettings < ActiveRecord::Migration[5.2]
def change
add_column :laboratories, :sync_course, :boolean, default: 0
end
end

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -29512,7 +29512,7 @@ var Player = function (_BaseObject) {
* @prop {boolean} controls
* enabled/disables displaying controls
* @prop {boolean} crossOrigin
* enables cross-origin capability for media-resources
* enables cross-origin capability for media-hacks
* @prop {boolean} playInline
* enables in-line video elements
* @prop {boolean} audioOnly

@ -509,7 +509,7 @@ li.li-width7{width: 7%;text-align: left}
background-position: center;
background-repeat: no-repeat;
}
.invite-tip{position: absolute;top: -5px;right: 140px;color: #fff; box-sizing: border-box;width: 170px;text-align: center;border-radius: 2px;background-color: rgba(5,16,26,0.6)}
.invite-tip{position: absolute;top: -5px;right: 140px;color: #fff; box-sizing: border-box;width: 170px;text-align: center;border-radius: 2px;background-color: rgba(5,16,26,0.6);z-index: 5000;}
.inviteTipbtn a{font-size:14px;width: 100%;height: 30px;line-height: 30px;display: block;color: #747A7F;background-color: rgba(5,16,26,0.4)}
.inviteTipbtn a:hover{color: #4cacff!important;}
.top-black-trangle{display: block;border-width: 8px;position: absolute;top: -16px;right: 4px;border-style: dashed solid dashed dashed;border-color: transparent transparent rgba(5,16,26,0.6) transparent;font-size: 0;line-height: 0;}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -129,7 +129,8 @@ class TPIContextProvider extends Component {
tpm_cases_modified,
tpm_modified,
tpm_script_modified,
showUpdateDialog: false
showUpdateDialog: false,
initTime: 0
})
}
@ -145,7 +146,6 @@ class TPIContextProvider extends Component {
window.__fetchAllFlag = false;
this.fetchAll(stageId);
this.costTimeInterval = window.setInterval(()=> {
const { game } = this.state;
if (!game || game.status === 2) { // 已完成的任务不需要计时
@ -167,7 +167,7 @@ class TPIContextProvider extends Component {
}
// force 评测通过后异步执行该方法强制同步costTime到服务端
_updateCostTime(async = false, force) {
const { game, loading } = this.state;
const { game, loading, initTime = 0 } = this.state;
// TODO 还有一种情况通关后cost_time计时停止没法通过这个判断
if (!force && (loading || !game || game.status === 2)) {
return; // 已完成的任务不需要处理
@ -178,12 +178,13 @@ class TPIContextProvider extends Component {
}
// var url = `${testPath}/api/v1/games/${ game.identifier }/cost_time`
var url = `${testPath}/api/tasks/${ game.identifier }/cost_time`
const disTime = Math.abs(game.cost_time - initTime);
window.$.ajax({
type: 'get',
url: url,
async: async, //IMPORTANT, the call will be synchronous
data: {
time: game.cost_time
time: disTime
}
}).done((data) => {
console.log('complete');
@ -406,7 +407,6 @@ pop_box_new(htmlvalue, 480, 182);
}
resData.game = game;
const { tpm_cases_modified, tpm_modified, tpm_script_modified, myshixun } = resData;
if (myshixun.system_tip) {
// system_tip为true的时候 不弹框提示用户更新
@ -440,7 +440,7 @@ pop_box_new(htmlvalue, 480, 182);
this.setState({
...resData,
initTime: game.cost_time,
currentGamePassed: false,
loading: false,
testSetsExpandedArray: testSetsExpandedArrayInitVal.slice(0),
@ -519,7 +519,6 @@ pop_box_new(htmlvalue, 480, 182);
// var url = `/api/v1/games/${stageId}`
var url = `/tasks/${stageId}.json`
// {"status":1,"message":"undefined method `authenticate!' for #<Grape::Endpoint:0xc8c91c0>"}
window.__fetchAllFlag = true;

@ -22,6 +22,7 @@ import TPICodeMirror from '../component/TPICodeMirror'
import TPIMonaco from '../component/monaco/TPIMonaco'
import { loadSshScript, openTerminal } from './Webssh'
import { Modal } from 'antd';
const $ = window.$;
@ -111,11 +112,8 @@ class CodeRepositoryView extends Component {
.removeClass("codeRepoShow")
// .removeClass("fa-caret-down").addClass("fa-caret-right");
})
// $('#codetab_con_1').append(``)
// $('#codetab_con_1 .codemirrorBackground').hide()
}
onTreeSelect = (selectedKeys, info) => {
if (!info.node.isLeaf()) {
const expandedKeys = this.state.expandedKeys.slice(0)
@ -223,7 +221,7 @@ class CodeRepositoryView extends Component {
// js_min_all.js有同样的计算逻辑用来拖拽时计算ssh高宽
// TODO 结合new Terminal 时的fontSize参数来定高度
const gameCtx = $("#games_repository_contents");
gameCtx.css({'padding-bottom': '50px', 'background': '#000'});
gameCtx.css({'padding-bottom': '30px', 'background': '#000'});
var h = gameCtx.height() - 50;
var w = gameCtx.width();
var line_h = (navigator.userAgent.indexOf('Chrome') >= 0 ? 18 : 19);
@ -543,13 +541,13 @@ class CodeRepositoryView extends Component {
{/* { tabIndex === STABLE_SSH_TAB_ID && this.state.sshData && <Webssh {...this.state.sshData} ></Webssh> } */}
</div>
<div id="codetab_con_82" className="undis -relative"
style={ { color: '#fff', display: tabIndex === 82 ? 'block' : 'none', 'marginLeft': '2px', 'paddingBottom': '50px' } }>
style={ { color: '#fff', display: tabIndex === 82 ? 'block' : 'none', 'marginLeft': '2px', 'paddingBottom': '30px' } }>
</div>
<div id="codetab_con_83" className="undis -relative"
style={ { color: '#fff', display: tabIndex === 83 ? 'block' : 'none', 'marginLeft': '2px', 'paddingBottom': '50px'} }>
style={ { color: '#fff', display: tabIndex === 83 ? 'block' : 'none', 'marginLeft': '2px', 'paddingBottom': '30px'} }>
</div>
<div id="codetab_con_84" className="undis -relative"
style={ { color: '#fff', display: tabIndex === 84 ? 'block' : 'none', 'marginLeft': '2px', 'paddingBottom': '50px'} }>
style={ { color: '#fff', display: tabIndex === 84 ? 'block' : 'none', 'marginLeft': '2px', 'paddingBottom': '30px'} }>
</div>
<div id="codetab_con_3" className="undis -relative" style={{display: 'none'}}></div>
</React.Fragment>

@ -313,10 +313,11 @@ class InfosCourse extends Component{
{/* 289 */}
{
page == 1 && is_current && !category &&
this.props.current_user && this.props.current_user.user_identity != "学生" ? <Create href={"/courses/new"} name={"新建课堂"} index="1"></Create> : ""
this.props.current_user && this.props.current_user.user_identity != "学生" ?
<Create href={"/courses/new"} name={"新建课堂"} index="1"></Create> : ""
}
{
(!data || (data && data.courses.length==0)) && category && <NoneData></NoneData>
(!data || (data && data.courses.length === 0)) && (category || (!category && this.props.current_user && this.props.current_user.user_identity == "学生")) && <NoneData></NoneData>
}
{
data && data.courses && data.courses.map((item,key)=>{

@ -327,8 +327,7 @@ class InfosPath extends Component{
<div className="square-list clearfix">
{/* 295 */}
{
page == 1 && is_current && !category &&
this.props.current_user && this.props.current_user.user_identity != "学生" ? <Create href={"/paths/new"} name={"新建实践课程"} index="3"></Create>:""
page == 1 && is_current && !category ? <Create href={"/paths/new"} name={"新建实践课程"} index="3"></Create>:""
}
{
(!data || (data && data.subjects.length==0)) && category && <NoneData></NoneData>

@ -313,7 +313,7 @@ class InfosProject extends Component{
<div className="square-list clearfix">
{/* 289 */}
{
page == 1 && is_current && this.props.current_user && !category && this.props.current_user.user_identity != "学生" ?
page == 1 && is_current ?
<Create href={`${this.props.Headertop && this.props.Headertop.old_url}/projects/new`} name={"新建开发项目"} index="4" Createtype={"projects"}
{...this.props} {...this.state}
></Create>:""

@ -335,7 +335,7 @@ class InfosShixun extends Component{
<div className="square-list clearfix">
{/* 298 */}
{
page == 1 && is_current && !category && this.props.current_user && this.props.current_user.user_identity != "学生" ?
page == 1 && is_current && !category ?
<Create href={"/shixuns/new"} name={"新建实训"} index="2"></Create>:""
}
{

@ -513,7 +513,7 @@ li.li-width7{width: 7%;text-align: left}
background-position: center;
background-repeat: no-repeat;
}
.invite-tip{position: absolute;top: -5px;right: 140px;color: #fff; box-sizing: border-box;width: 170px;text-align: center;border-radius: 2px;background-color: rgba(5,16,26,0.6)}
.invite-tip{position: absolute;top: -5px;right: 140px;color: #fff; box-sizing: border-box;width: 170px;text-align: center;border-radius: 2px;background-color: rgba(5,16,26,0.6);z-index: 5000;}
.inviteTipbtn a{font-size:14px;width: 100%;height: 30px;line-height: 30px;display: block;color: #747A7F;background-color: rgba(5,16,26,0.4)}
.inviteTipbtn a:hover{color: #4cacff!important;}
.top-black-trangle{display: block;border-width: 8px;position: absolute;top: -16px;right: 4px;border-style: dashed solid dashed dashed;border-color: transparent transparent rgba(5,16,26,0.6) transparent;font-size: 0;line-height: 0;}

@ -0,0 +1,5 @@
require 'rails_helper'
RSpec.describe HackUserLastestCodesController, type: :controller do
end

@ -0,0 +1,5 @@
require 'rails_helper'
RSpec.describe HacksController, type: :controller do
end

@ -0,0 +1,15 @@
require 'rails_helper'
# Specs in this file have access to a helper object that includes
# the HackUserLastestCodesHelper. For example:
#
# describe HackUserLastestCodesHelper do
# describe "string concat" do
# it "concats two strings with spaces" do
# expect(helper.concat_strings("this","that")).to eq("this that")
# end
# end
# end
RSpec.describe HackUserLastestCodesHelper, type: :helper do
pending "add some examples to (or delete) #{__FILE__}"
end

@ -0,0 +1,15 @@
require 'rails_helper'
# Specs in this file have access to a helper object that includes
# the HacksHelper. For example:
#
# describe HacksHelper do
# describe "string concat" do
# it "concats two strings with spaces" do
# expect(helper.concat_strings("this","that")).to eq("this that")
# end
# end
# end
RSpec.describe HacksHelper, type: :helper do
pending "add some examples to (or delete) #{__FILE__}"
end

@ -0,0 +1,5 @@
require 'rails_helper'
RSpec.describe HackCode, type: :model do
pending "add some examples to (or delete) #{__FILE__}"
end

@ -0,0 +1,5 @@
require 'rails_helper'
RSpec.describe HackSet, type: :model do
pending "add some examples to (or delete) #{__FILE__}"
end

@ -0,0 +1,5 @@
require 'rails_helper'
RSpec.describe Hack, type: :model do
pending "add some examples to (or delete) #{__FILE__}"
end

@ -0,0 +1,5 @@
require 'rails_helper'
RSpec.describe HackUserCode, type: :model do
pending "add some examples to (or delete) #{__FILE__}"
end

@ -0,0 +1,5 @@
require 'rails_helper'
RSpec.describe HackUserDebug, type: :model do
pending "add some examples to (or delete) #{__FILE__}"
end

@ -0,0 +1,5 @@
require 'rails_helper'
RSpec.describe HackUserLastestCode, type: :model do
pending "add some examples to (or delete) #{__FILE__}"
end
Loading…
Cancel
Save