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/shixuns_controller.rb

1124 lines
42 KiB

6 years ago
class ShixunsController < ApplicationController
include ShixunsHelper
include ApplicationHelper
5 years ago
include ElasticsearchAble
include CoursesHelper
before_action :require_login, :check_auth, except: [:download_file, :index, :menus, :show, :show_right, :ranking_list,
:discusses, :collaborators, :fork_list, :propaedeutics]
before_action :check_account, only: [:new, :create, :shixun_exec, :jupyter_exec]
before_action :find_shixun, except: [:index, :new, :create, :menus, :get_recommend_shixuns,
:propaedeutics, :departments, :apply_shixun_mirror,
:get_mirror_script, :download_file, :shixun_list, :batch_send_to_course]
6 years ago
before_action :shixun_access_allowed, except: [:index, :new, :create, :menus, :get_recommend_shixuns,
:propaedeutics, :departments, :apply_shixun_mirror, :jupyter_exec,
:get_mirror_script, :download_file, :shixun_list, :batch_send_to_course]
before_action :find_repo_name, only: [:repository, :commits, :file_content, :update_file, :shixun_exec, :copy,
:add_file, :jupyter_exec]
6 years ago
before_action :allowed, only: [:update, :close, :update_propaedeutics, :settings, :publish, :apply_public,
6 years ago
:shixun_members_added, :change_manager, :collaborators_delete,
:cancel_apply_public, :cancel_publish, :add_collaborators, :add_file]
6 years ago
before_action :portion_allowed, only: [:copy]
before_action :special_allowed, only: [:send_to_course, :search_user_courses]
## 获取课程列表
def index
@shixuns = current_laboratory.shixuns.unhidden.publiced
6 years ago
## 方向
if params[:tag_level].present? && params[:tag_id].present?
@shixuns = @shixuns.filter_tag(params[:tag_level].to_i, params[:tag_id].to_i)
case params[:tag_level].to_i
when 1 #大类
@search_tags = Repertoire.find(params[:tag_id].to_i).name
when 2 #子类
@search_tags = SubRepertoire.find(params[:tag_id].to_i).name
when 3 #tag
tag = TagRepertoire.find(params[:tag_id].to_i)
@search_tags = "#{tag.sub_repertoire.name} / #{tag.name}"
end
end
## 搜索关键字 匹配关卡名, 用户名, 实训名 和 空格多搜索
if params[:keyword].present?
keyword = params[:keyword].strip
@shixuns = @shixuns.joins(:user, challenges: :challenge_tags).
where("challenge_tags.name like :keyword
or challenges.subject like :keyword
or concat(lastname, firstname) like :keyword
or shixuns.name like :name",
keyword: "%#{keyword}%", name: "%#{keyword.split(" ").join("%")}%").distinct
6 years ago
end
## 筛选 状态
if params[:status].present? && params[:status].to_i != 0
params[:status] = [0, 1] if params[:status].to_i == 1
@shixuns = @shixuns.where(status: params[:status])
end
## 筛选 难度
if params[:diff].present? && params[:diff].to_i != 0
6 years ago
@shixuns = @shixuns.where(trainee: params[:diff])
end
## 排序参数
bsort = params[:sort] || 'desc'
case params[:order_by] || 'publish_time'
when 'new'
@shixuns = @shixuns.order("shixuns.status = 2 desc, shixuns.created_at #{bsort}")
6 years ago
when 'hot'
@shixuns = @shixuns.order("shixuns.status = 2 desc, shixuns.myshixuns_count #{bsort}")
6 years ago
when 'mine'
@shixuns = @shixuns.order("shixuns.created_at #{bsort}")
6 years ago
else
@shixuns = @shixuns.order("shixuns.status = 2 desc, shixuns.publish_time #{bsort}")
6 years ago
end
# 用id计数会快10+MS左右,对于搜索的内容随着数据的增加,性能会提升一些。
@total_count = @shixuns.count("shixuns.id")
6 years ago
## 分页参数
page = params[:page] || 1
limit = params[:limit] || 16
@shixuns = @shixuns.includes(:tag_repertoires, :challenges).page(page).per(limit)
@tag_name_map = TagRepertoire.joins(:shixun_tag_repertoires)
.where(shixun_tag_repertoires: { shixun_id: @shixuns.map(&:id) })
.group('shixun_tag_repertoires.shixun_id')
.select('shixun_id, tag_repertoires.name')
.each_with_object({}) { |r, obj| obj[r.shixun_id] = r.name }
6 years ago
end
5 years ago
def shixun_list
# 全部实训/我的实训
5 years ago
type = params[:type] || "all"
# 状态:已发布/未发布
status = params[:status] || "all"
5 years ago
# 超级管理员用户显示所有未隐藏的实训、非管理员显示所有已发布的实训(对本单位公开且未隐藏未关闭)
if type == "mine"
@shixuns = current_user.shixuns.none_closed
5 years ago
else
if current_user.admin?
@shixuns = Shixun.none_closed.where(hidden: 0)
else
none_shixun_ids = ShixunSchool.where("school_id != #{current_user.school_id}").pluck(:shixun_id)
5 years ago
@shixuns = Shixun.where.not(id: none_shixun_ids).none_closed.where(hidden: 0)
end
end
unless status == "all"
@shixuns = status == "published" ? @shixuns.where(status: 2) : @shixuns.where(status: [0, 1])
5 years ago
end
## 筛选 难度
if params[:diff].present? && params[:diff].to_i != 0
@shixuns = @shixuns.where(trainee: params[:diff])
5 years ago
end
page = params[:page] || 1
limit = params[:limit] || 10
5 years ago
offset = (page.to_i - 1) * (limit.to_i)
order = params[:order] || "desc"
## 搜索关键字创建者、实训名称、院校名称
5 years ago
keyword = params[:keyword].to_s.strip.presence || '*'
model_options = {
index_name: [Shixun],
model_includes: Shixun.searchable_includes
}
model_options.merge(where: { id: @shixuns.pluck(:id) }).merge(order: {"myshixuns_count" => order}).merge(limit: limit, offset: offset)
model_options.merge(default_options)
@shixuns = Searchkick.search(keyword, model_options)
# @shixuns = Shixun.search keyword, where: {id: @shixuns.pluck(:id)}, order: {"myshixuns_count" => order}, limit: limit, offset: offset
5 years ago
@total_count = @shixuns.total_count
5 years ago
end
6 years ago
## 获取顶部菜单
def menus
@repertoires = current_laboratory.shixun_repertoires
6 years ago
end
## 实训详情
def show
# 当前用户开启的实训
can_fork = current_user.is_certification_teacher || current_user.admin?
unless can_fork
@can_fork = {can_fork: "已经职业认证的教师才能fork实训",
certi_url: "/account/certification"}
6 years ago
end
@current_myshixun = @shixun.current_myshixun(current_user)
if @shixun.fork_from
fork_shixun = Shixun.select(:id, :user_id, :name, :identifier).where(id: @shixun.fork_from).first
@fork_from = {name: fork_shixun.name, username: fork_shixun.owner.try(:full_name),
fork_identifier: fork_shixun.identifier} if fork_shixun
end
@power = current_user.manager_of_shixun?(@shixun)
# 更新是为了首页的排序myshixun的动静需要在实训中展现出来
if @current_myshixun && params[:exit] && !current_user.admin?
@current_myshixun.update_column(:updated_at, Time.now)
end
end
def show_right
owner = @shixun.owner
#@fans_count = owner.fan_count
#@followed_count = owner.follow_count
6 years ago
@user_own_shixuns = owner.shixuns.published.count
end
# 排行榜
def ranking_list
if @shixun.status == 2
sql = "
select m.user_id, u.login, u.lastname, m.updated_at,
(select sum(cost_time) from games g where g.myshixun_id = m.id) as time,
(select sum(final_score) from games g where g.myshixun_id = m.id) as score
6 years ago
from (users u left join myshixuns m on m.user_id = u.id) where m.shixun_id = #{@shixun.id} and m.status = 1
6 years ago
order by score desc, time asc limit 10
"
@myshixuns = Myshixun.find_by_sql(sql)
end
end
#评论
def discusses
new_params = params.merge(container_id: @shixun.id, container_type: 'Shixun')
discusses = ShixunsService.new.shixun_discuss new_params, current_user
if discusses.present?
@children_list = discusses[:children_list]
else
@children_list = []
end
end
def copy
ActiveRecord::Base.transaction do
begin
@new_shixun = Shixun.new
@new_shixun.attributes = @shixun.attributes.dup.except("id","user_id","visits","gpid","status", "identifier", "averge_star",
"homepage_show","repo_name", "myshixuns_count", "challenges_count",
"can_copy", "created_at", "updated_at", "public")
6 years ago
@new_shixun.user_id = User.current.id
@new_shixun.averge_star = 5
@new_shixun.identifier = generate_identifier Shixun, 8
@new_shixun.fork_from = @shixun.id
@new_shixun.save!
# 同步shixun_info的信息
if @shixun.shixun_info.present?
ShixunInfo.create!(shixun_id: @new_shixun.id,
description: @shixun.description,
evaluate_script: @shixun.evaluate_script)
end
5 years ago
# 同步私密版本库
if @shixun.shixun_secret_repository
5 years ago
# 源仓库的的私密版本库地址
repo_name = @shixun.shixun_secret_repository.repo_name
# 新生成的地址
fork_repository_name = "#{current_user.login}/secret_#{@new_shixun.identifier}"
5 years ago
ShixunSecretRepository.create!(shixun_id: @new_shixun.id,
5 years ago
repo_name: "#{fork_repository_name}",
5 years ago
secret_dir_path: @shixun.shixun_secret_repository.secret_dir_path)
GitService.fork_repository(repo_path: "#{repo_name}.git", fork_repository_path: (fork_repository_name + ".git"))
5 years ago
end
6 years ago
# 同步镜像
if @shixun.mirror_repositories.present?
@shixun.mirror_repositories.each do |mirror|
ShixunMirrorRepository.create!(:shixun_id => @new_shixun.id, :mirror_repository_id => mirror.id)
end
end
# 同步技术标签
@shixun.shixun_tag_repertoires.each do |str|
ShixunTagRepertoire.create!(:tag_repertoire_id => str.tag_repertoire_id, :shixun_id => @new_shixun.id)
end
# 同步配置
6 years ago
logger.info("########-shixun_service_configs_count: #{@shixun.shixun_service_configs.pluck(:id, :shixun_id)}")
@shixun.shixun_service_configs.each do |config|
ShixunServiceConfig.create!(:shixun_id => @new_shixun.id,
:cpu_limit => config.cpu_limit,
:lower_cpu_limit => config.lower_cpu_limit,
:memory_limit => config.memory_limit,
:request_limit => config.request_limit,
:mirror_repository_id => config.mirror_repository_id)
end
# 同步高校限制
@shixun.shixun_schools.each do |school|
ShixunSchool.create!(shixun_id: @new_shixun.id, school_id: school.school_id)
end
6 years ago
# fork版本库
logger.info("###########fork_repo_path: ######{@repo_path}")
project_fork(@new_shixun, @repo_path, current_user.login)
ShixunMember.create!(:user_id => User.current.id, :shixun_id => @new_shixun.try(:id), :role => 1)
# 同步复制关卡
if @shixun.challenges.present?
@shixun.challenges.each do |challenge|
new_challenge = Challenge.new
new_challenge.attributes = challenge.attributes.dup.except("id","shixun_id","user_id", "challenge_tags_count")
new_challenge.user_id = User.current.id
new_challenge.shixun_id = @new_shixun.id
new_challenge.save!
# 同步参考答案
challenge.challenge_answers.each do |answer|
6 years ago
new_answer = ChallengeAnswer.new
6 years ago
new_answer.attributes = answer.attributes.dup.except("id","challenge_id")
new_answer.challenge_id = new_challenge.id
new_answer.save!
end
6 years ago
if challenge.st == 0 # 评测题
# 同步测试集
if challenge.test_sets.present?
challenge.test_sets.each do |test_set|
new_test_set = TestSet.new
new_test_set.attributes = test_set.attributes.dup.except("id","challenge_id")
new_test_set.challenge_id = new_challenge.id
new_test_set.save!
end
end
# 同步关卡标签
challenge_tags = ChallengeTag.where("challenge_id =? and challenge_choose_id is null", challenge.id)
if challenge_tags.present?
challenge_tags.each do |challenge_tag|
ChallengeTag.create!(:challenge_id => new_challenge.id, :name => challenge_tag.try(:name))
end
end
elsif challenge.st == 1 # 选择题
if challenge.challenge_chooses.present?
challenge.challenge_chooses.each do |challenge_choose|
new_challenge_choose = ChallengeChoose.new
new_challenge_choose.attributes = challenge_choose.attributes.dup.except("id","challenge_id")
new_challenge_choose.challenge_id = new_challenge.id
new_challenge_choose.save!
# 每一题的选项
if challenge_choose.challenge_questions.present?
challenge_choose.challenge_questions.each do |challenge_question|
new_challenge_question = ChallengeQuestion.new
new_challenge_question.attributes = challenge_question.attributes.dup.except("id","challenge_choose_id")
new_challenge_question.challenge_choose_id = new_challenge_choose.id
new_challenge_question.save!
end
end
# 每一题的知识标签
st_challenge_tags = ChallengeTag.where(:challenge_id => challenge.id, :challenge_choose_id => challenge_choose.id)
if st_challenge_tags.present?
st_challenge_tags.each do |st_challenge_tag|
ChallengeTag.create!(:challenge_id => new_challenge.id, :name => st_challenge_tag.try(:name), :challenge_choose_id => new_challenge_choose.id)
end
end
end
end
end
end
end
# 将实训标志为该云上实验室建立
Laboratory.current.laboratory_shixuns.create!(shixun: @shixun, ownership: true)
6 years ago
rescue Exception => e
uid_logger_error("copy shixun failed ##{e.message}")
5 years ago
# 删除版本库
# 删除私密版本库
GitService.delete_repository(repo_path: "#{fork_repository_name}.git") if @new_shixun.shixun_secret_repository&.repo_name
GitService.delete_repository(repo_path: @new_shixun.repo_path) if @new_shixun.repo_path
6 years ago
tip_exception("实训Fork失败")
raise ActiveRecord::Rollback
end
end
end
#合作者
def collaborators
@user = current_user
@members = @shixun.shixun_members.includes(:user)
end
def fork_list
@shixuns = Shixun.where(:fork_from => @shixun.id)
@shixuns_count = @shixuns.count
## 分页参数
page = params[:page] || 1
limit = params[:limit] || 20
@shixuns = @shixuns.page(page).per(limit)
end
def new
@introduction_sample = PlatformSample.where(samples_type: ['introduction', 'knowledge']).pluck([:samples_type, :contents])
@main_type = shixun_main_type
@small_type = shixun_small_type
end
def create
5 years ago
@shixun = CreateShixunService.call(current_user, shixun_params, params)
5 years ago
end
5 years ago
# 保存jupyter到版本库
def update_jupyter
jupyter_save_with_shixun(@shixun, params[:jupyter_port])
end
5 years ago
def update
# 镜像方面
5 years ago
mirror_ids = MirrorRepository.where(id: params[:main_type])
5 years ago
.or( MirrorRepository.where(id: params[:sub_type])).pluck(:id).uniq
5 years ago
old_mirror_ids = @shixun.shixun_mirror_repositories
.where(mirror_repository_id: params[:main_type])
5 years ago
.or(@shixun.shixun_mirror_repositories.where(mirror_repository_id: params[:sub_type]))
5 years ago
.pluck(:mirror_repository_id).uniq
5 years ago
new_mirror_id = (mirror_ids - old_mirror_ids).map{|id| {mirror_repository_id: id}} # 转换成数组hash方便操作
5 years ago
logger.info("##########new_mirror_id: #{new_mirror_id}")
5 years ago
logger.info("##########old_mirror_ids: #{old_mirror_ids}")
5 years ago
logger.info("##########mirror_ids: #{mirror_ids}")
5 years ago
# 服务配置方面
5 years ago
service_create_params = service_config_params[:shixun_service_configs]
5 years ago
.select{|config| !old_mirror_ids.include?(config[:mirror_repository_id]) &&
MirrorRepository.find(config[:mirror_repository_id]).name.present?}
5 years ago
service_update_params = service_config_params[:shixun_service_configs]
.select{|config| old_mirror_ids.include?(config[:mirror_repository_id])}
logger.info("#########service_create_params: #{service_create_params}")
logger.info("#########service_update_params: #{service_update_params}")
5 years ago
begin
ActiveRecord::Base.transaction do
@shixun.update_attributes(shixun_params)
@shixun.shixun_info.update_attributes(shixun_info_params)
# 镜像变动
5 years ago
@shixun.shixun_mirror_repositories.where.not(mirror_repository_id: old_mirror_ids).destroy_all
5 years ago
@shixun.shixun_mirror_repositories.create!(new_mirror_id)
# 镜像变动要更换服务配置
5 years ago
@shixun.shixun_service_configs.where.not(mirror_repository_id: old_mirror_ids).destroy_all
5 years ago
@shixun.shixun_service_configs.create!(service_create_params)
5 years ago
service_update_params&.map do |service|
5 years ago
smr = @shixun.shixun_service_configs.find_by(mirror_repository_id: service[:mirror_repository_id])
5 years ago
smr.update_attributes(service)
end
5 years ago
# 添加第二仓库(管理员权限)
5 years ago
if params[:is_secret_repository]
add_secret_repository if @shixun.shixun_secret_repository.blank?
else
# 如果有仓库,就要删
if @shixun.shixun_secret_repository&.repo_name
@shixun.shixun_secret_repository.lock!
GitService.delete_repository(repo_path: @shixun.shixun_secret_repository.repo_path)
@shixun.shixun_secret_repository.destroy
5 years ago
end
end
6 years ago
end
5 years ago
rescue => e
uid_logger_error(e.message)
tip_exception("基本信息更新失败:#{e.message}")
6 years ago
end
5 years ago
end
6 years ago
5 years ago
# 实训权限设置
def update_permission_setting
# 查找需要增删的高校id
school_id = School.where(:name => params[:scope_partment]).pluck(:id)
old_school_ids = @shixun.shixun_schools.pluck(:school_id)
school_params = (school_id - old_school_ids).map{|id| {school_id: id}}
begin
ActiveRecord::Base.transaction do
@shixun.update_attributes!(shixun_params)
5 years ago
@shixun.shixun_schools.where.not(school_id: school_id).destroy_all if school_id.present?
5 years ago
@shixun.shixun_schools.create!(school_params)
end
rescue => e
uid_logger_error("实训权限设置失败--------#{e.message}")
tip_exception("实训权限设置失败")
end
end
6 years ago
5 years ago
# 实训学习页面设置
def update_learn_setting
begin
ActiveRecord::Base.transaction do
@shixun.update_attributes!(shixun_params)
6 years ago
end
5 years ago
rescue => e
uid_logger_error("实训学习页面设置失败--------#{e.message}")
tip_exception("实训学习页面设置失败")
6 years ago
end
end
5 years ago
# Jupyter数据集
5 years ago
def get_data_sets
5 years ago
page = params[:page] || 1
limit = params[:limit] || 10
data_sets = @shixun.data_sets.order("created_on desc")
5 years ago
@data_count = data_sets.count
@data_sets= data_sets.page(page).per(limit)
5 years ago
@absolute_folder = edu_setting('shixun_folder')
end
# 实训测试集附件
def upload_data_sets
begin
upload_file = params["file"]
raise "未上传文件" unless upload_file
folder = edu_setting('shixun_folder')
raise "存储目录未定义" unless folder.present?
rep_name = @shixun.data_sets.pluck(:filename).include?(upload_file.original_filename)
raise "文件名已经存在\"#{upload_file.original_filename}\", 请删除后再上传" if rep_name
tpm_folder = params[:identifier] # 这个是实训的identifier
save_path = File.join(folder, tpm_folder)
ext = file_ext(upload_file.original_filename)
local_path, digest = file_save_to_local(save_path, upload_file.tempfile, ext)
content_type = upload_file.content_type.presence || 'application/octet-stream'
disk_filename = local_path[save_path.size + 1, local_path.size]
@attachment = Attachment.where(disk_filename: disk_filename,
author_id: current_user.id).first
if @attachment.blank?
@attachment = Attachment.new
@attachment.filename = upload_file.original_filename
@attachment.disk_filename = local_path[save_path.size + 1, local_path.size]
@attachment.filesize = upload_file.tempfile.size
@attachment.content_type = content_type
@attachment.digest = digest
@attachment.author_id = current_user.id
@attachment.disk_directory = tpm_folder
@attachment.container_id = @shixun.id
@attachment.container_type = @shixun.class.name
@attachment.attachtype = 2
@attachment.save!
else
logger.info "文件已存在id = #{@attachment.id}, filename = #{@attachment.filename}"
end
render_ok
rescue => e
uid_logger_error(e.message)
tip_exception(e.message)
5 years ago
end
end
# 多文件删除
def destroy_data_sets
files = Attachment.where(id: params[:id])
shixun_folder= edu_setting("shixun_folder")
begin
files.each do |file|
file_path = "#{shixun_folder}/#{file.relative_path_filename}"
delete_file(file_path)
end
files.destroy_all
render_ok
rescue => e
uid_logger_error(e.message)
tip_exception(e.message)
end
5 years ago
end
6 years ago
def apply_shixun_mirror
form_params = params.permit(*%i[language runtime run_method attachment_id])
form = ApplyShixunMirrorForm.new(form_params)
form.validate!
tiding = Tiding.new(
user_id: 1,
trigger_user_id: current_user.id,
container_type: 'SendMessage',
viewed: 0,
tiding_type: 'Apply',
extra: form.to_json
)
ActiveRecord::Base.transaction do
# TODO: 由于tiding是多态,而SendMessage却没有对应的model,因此,如果不跳过验证会报 container must exist的错.
tiding.save(validate: false)
form.attachment.update!(container: tiding)
end
sucess_status
rescue ActiveModel::ValidationError => ex
tip_exception(ex.message)
rescue Exception => e
uid_logger_error(e.message)
tip_exception("申请失败")
end
# 永久关闭实训
def close
@shixun.update_attributes(status: 3, closer_id: current_user.id, end_time: Time.now)
sucess_status
end
def propaedeutics
6 years ago
@content = Shixun.find_by_identifier!(params[:identifier]).propaedeutics
6 years ago
end
# 更新背景知识
def update_propaedeutics
@shixun.shixun_info.update_column(:propaedeutics, params[:content])
6 years ago
end
# 获取推荐实训接口 2个热门实训 + 2个最新实训
def get_recommend_shixuns
hot_shixuns = Shixun.field_for_recommend.published.order("myshixuns_count desc").limit(2)
newest_shixuns = Shixun.field_for_recommend.published.order("created_at desc").limit(2)
@recommend_shixuns = hot_shixuns + newest_shixuns
end
def settings
@choice_main_type = @shixun.main_mirror_id
@choice_small_type = @shixun.small_mirror_id
@main_type = shixun_main_type
@small_type = shixun_small_type
@configs = @shixun.shixun_service_configs
6 years ago
#@mirror_script = MirrorScript.select([:id, :script_type]).find(@shixun.mirror_script_id).attributes if @shixun.mirror_script_id && @shixun.mirror_script_id != 0
# @shixun_main_mirror = @shixun.show_shixun_mirror
# @script_type = @shixun.script_tag.try(:script_type) || "无"
# @evaluate_scirpt = @shixun.evaluate_script || "无"
end
5 years ago
6 years ago
# 获取脚本内容
def get_script_contents
mirrir_script = MirrorScript.find(params[:script_id])
script = mirrir_script.try(:script)
@description = mirrir_script.try(:description)
@script = modify_shixun_script @shixun, script
end
def get_custom_script
shixun_script = PlatformSample.where(:samples_type => "script").first.try(:contents)
compile = params[:compile]
execute = params[:executive]
if shixun_script
shixun_script = compile.blank? ? shixun_script.gsub("COMPILEFUNCTION", "").gsub("CHALLENGEFIELPATH", "") :
shixun_script.gsub("COMPILEFUNCTION", "#{compile_command}").gsub("COMPILECOMMAND", "#{compile}")
shixun_script = execute.blank? ? shixun_script.gsub("EXECUTEFUNCTION", "") : shixun_script.gsub("EXECUTECOMMAND", "#{execute}")
shixun_script = modify_shixun_script @shixun, shixun_script
end
@shixun_script = shixun_script
end
def departments
@scope = []
q = params[:q]
if q && q.strip.present?
@scope = School.where("name like ?", "%#{q.strip}%").pluck(:name)
end
end
def get_mirror_script
mirror = MirrorRepository.find(params[:mirror_id])
@script = mirror.mirror_scripts
end
# TODO: 目前实训只做软删除.
def destroy
apply_records = ApplyAction.where(container_id: @shixun.id, container_type: "ApplyShixun")
apply_records.delete_all if apply_records
6 years ago
# HomeworkCommonShixuns.where(shixun_id: @shixun).delete_all
# @shixun.destroy
@shixun.update_column(:status, -1)
end
# 开启挑战
# 以前在开启挑战的时候检测实训是否更新更新则重置觉得应该放在TPI更好
# 中间需要一个过渡动画
# TODO: 第一次开启实训都会去判断是否是纯选择题类型,感觉做成在创建关卡的时候就判断该实训是否是纯选择题更加合适
def shixun_exec
if is_shixun_opening?
tip_show_exception(-3, "#{@shixun.opening_time.strftime('%Y-%m-%d %H:%M:%S')}")
end
current_myshixun = @shixun.current_myshixun(current_user.id)
min_challenges = @shixun.challenges.pluck(:id , :st)
Rails.logger.info("11111111112#{current_myshixun.try(:id)}")
Rails.logger.info("111111111102#{params[:reset] != 1}")
# 因为读写分离有延迟所以如果是重置来的请求可以先跳过重置过来的params[:reset]为1
if current_myshixun && params[:reset] != "1"
games = current_myshixun.games
# 如果TPM和TPI的管卡数不相等或者关卡顺序错了说明实训被极大的改动需要重置,实训发布前打过的实训都需要重置
if is_shixun_reset?(games, min_challenges, current_myshixun)
# 这里页面弹框要收到 当前用户myshixun的identifier.
tip_show_exception("/myshixuns/#{current_myshixun.try(:identifier)}/reset_my_game")
end
# 如果存在实训,则直接进入实训
# 如果实训允许跳关传参params[:challenge_id]跳入具体的关卡
@current_task =
if params[:challenge_id]
game = games.where(challenge_id: params[:challenge_id]).take
if @shixun.task_pass || game.status != 3
game
else
current_myshixun.current_task(games)
end
else
current_myshixun.current_task(games)
end
else
# 如果未创建关卡一定不能开启实训否则TPI没法找到当前的关卡
if @shixun.challenges_count == 0
tip_exception("开启实战前请先创建实训关卡")
end
# 判断实训是否全为选择题
is_choice_type = (min_challenges.size == min_challenges.select{|challenge| challenge.last == 1}.count)
6 years ago
if !is_choice_type
commit = GitService.commits(repo_path: @repo_path).try(:first)
uid_logger("First comit########{commit}")
tip_exception("开启实战前请先在版本库中提交代码") if commit.blank?
commit_id = commit["id"]
end
6 years ago
# 如果该实训是金课中的实训,则将当前用户加入到当期开课的课堂
if StageShixun.exists?(shixun_id: @shixun.id, subject_id: Subject.where(excellent: 1))
6 years ago
subject = Subject.find_by(id: StageShixun.where(shixun_id: @shixun.id).pluck(:subject_id), excellent: 1)
course = subject.courses.where("start_date is not null and start_date <= '#{Date.today}' and end_date is not null and end_date >= '#{Date.today}'").take
if course.present? && !CourseMember.exists?(course_id: course.id, user_id: current_user.id)
# 为了不影响后续操作用create而不是create!
CourseMember.create(course_id: course.id, user_id: current_user.id, role: 4)
CourseAddStudentCreateWorksJob.perform_later(course.id, [current_user.id])
end
end
ActiveRecord::Base.transaction do
begin
cloud_bridge = edu_setting('cloud_bridge')
myshixun_identifier = generate_identifier Myshixun, 10
myshixun = @shixun.myshixuns.create!(user_id: current_user.id, identifier: myshixun_identifier,
modify_time: @shixun.modify_time, reset_time: @shixun.reset_time,
onclick_time: Time.now, commit_id: commit_id, repo_name: (is_choice_type ? "-1" : nil))
uid_logger("myshixun_id is #{myshixun.id}")
# 其它创建关卡等操作
challenges = @shixun.challenges
# 之所以增加user_id是为了方便统计查询性能
game_attrs = %i[challenge_id myshixun_id status user_id open_time identifier modify_time created_at updated_at]
Game.bulk_insert(*game_attrs) do |worker|
base_attr = { myshixun_id: myshixun.id, user_id: myshixun.user_id }
challenges.each_with_index do |challenge, index|
status = (index == 0 ? 0 : 3)
game_identifier = generate_identifier(Game, 12)
worker.add(base_attr.merge(challenge_id: challenge.id, status: status,
identifier: game_identifier, modify_time: challenge.modify_time))
end
end
# 如果实训是纯选择题则不需要去fork仓库以及中间层的相关操作了
unless is_choice_type
# fork仓库
project_fork(myshixun, @repo_path, current_user.login)
rep_url = Base64.urlsafe_encode64(repo_ip_url @repo_path )
uid_logger("start openGameInstance")
uri = "#{cloud_bridge}/bridge/game/openGameInstance"
logger.info("end openGameInstance")
params = {tpiID: "#{myshixun.id}", tpmGitURL:rep_url, tpiRepoName: myshixun.repo_name.split("/").last}
uid_logger("openGameInstance params is #{params}")
interface_post uri, params, 83, "实训云平台繁忙繁忙等级83"
end
@current_task = myshixun.current_task(myshixun.games)
uid_logger("## shixun exec: myshixun id is #{myshixun.id}")
rescue Exception => e
uid_logger_error(e.message)
tip_exception("实训云平台繁忙繁忙等级81")
raise ActiveRecord::Rollback
end
end
end
end
# jupyter开启挑战
def jupyter_exec
begin
if is_shixun_opening?
tip_show_exception(-3, "#{@shixun.opening_time.strftime('%Y-%m-%d %H:%M:%S')}")
end
current_myshixun = @shixun.current_myshixun(current_user.id)
if current_myshixun
@myshixun = current_myshixun
else
commit = GitService.commits(repo_path: @repo_path).try(:first)
uid_logger("First comit########{commit}")
5 years ago
tip_exception("开启挑战前请先在Jupyter中填写内容") if commit.blank?
commit_id = commit["id"]
cloud_bridge = edu_setting('cloud_bridge')
myshixun_identifier = generate_identifier Myshixun, 10
ActiveRecord::Base.transaction do
@myshixun = @shixun.myshixuns.create!(user_id: current_user.id, identifier: myshixun_identifier,
modify_time: @shixun.modify_time, reset_time: @shixun.reset_time,
onclick_time: Time.now, commit_id: commit_id)
# fork仓库
project_fork(@myshixun, @repo_path, current_user.login)
rep_url = Base64.urlsafe_encode64(repo_ip_url @repo_path)
uri = "#{cloud_bridge}/bridge/game/openGameInstance"
5 years ago
params = {tpiID: "#{@myshixun.id}", tpmGitURL: rep_url, tpiRepoName: @myshixun.repo_name.split("/").last}
interface_post uri, params, 83, "实训云平台繁忙繁忙等级83"
end
end
rescue => e
uid_logger_error(e.message)
5 years ago
tip_exception("#{e.message}")
end
end
6 years ago
def publish
@status = 0
@position = []
begin
unless @shixun.is_jupyter?
if @shixun.challenges.count == 0
@status = 4
else
@shixun.challenges.each do |challenge|
if challenge.challenge_tags.count == 0
@status = 3
@position << challenge.position
end
6 years ago
end
unfinish_challenge = @shixun.challenges.where(:st => 0, :path => nil)
if unfinish_challenge.count > 0 && !@shixun.is_choice_type?
@status = 2
@pos = []
unfinish_challenge.each do |challenge|
@pos << challenge.position
end
6 years ago
end
end
end
if @status == 0
@shixun.update_attributes!(:status => 2)
6 years ago
end
rescue Exception => e
logger.error("pushlish game #{e}")
end
end
def apply_public
tip_exception(-1, "请先发布实训再申请公开") if @shixun.status != 2
ActiveRecord::Base.transaction do
@shixun.update_attributes!(public: 1)
apply = ApplyAction.where(:container_type => "ApplyShixun", :container_id => @shixun.id).order("created_at desc").first
if apply && apply.status == 0
@status = 0
else
ApplyAction.create(:container_type => "ApplyShixun", :container_id => @shixun.id, :user_id => current_user.id, :status => 0)
end
end
normal_status(0, "申请成功")
end
5 years ago
# 设置私密版本库的在tpm中的目录
def set_secret_dir
raise("设置路径不能为空") if params[:secret_dir_path].blank?
5 years ago
raise("请先配置私密版本库") if @shixun.shixun_secret_repository.blank?
5 years ago
@shixun.shixun_secret_repository.update_attributes(:secret_dir_path => params[:secret_dir_path])
5 years ago
normal_status("设置成功")
end
def secret_repository
begin
@repo_path = @shixun.shixun_secret_repository&.repo_path
@repo_url = repo_url @repo_path
@trees = GitService.file_tree(repo_path: @repo_path, path: params[:path])
logger.info("#11@@#@#@#@111#@@@@###{@trees}")
if @trees
logger.info("#@@#@#@#@#@@@@###{@trees.try(:count)}")
@latest_commit = [GitService.commits(repo_path: @repo_path).first]
Rails.logger.info("########## #{@latest_commit}")
end
rescue Exception => e
logger.error(e.message)
end
end
6 years ago
include GitCommon
def update_file
content = params[:content]
author_name = current_user.real_name
6 years ago
author_email = current_user.git_mail
6 years ago
@content = update_file_content content, @repo_path, @path, author_email, author_name, "Edit by browser"
end
def add_collaborators
member_ids = "(" + @shixun.shixun_members.map(&:user_id).join(',') + ")"
user_name = "%#{params[:user_name].to_s.strip}%"
school_name = "%#{params[:school_name].to_s.strip}%"
if user_name.present? || school_name.present?
@users = User.where("users.id not in #{member_ids} AND users.status = 1 AND
(LOWER(concat(users.lastname, users.firstname)) LIKE ? or users.phone like ?)",
user_name, user_name)
@users = @users.joins(user_extension: :school).where("schools.name like '%#{school_name}%'") if params[:school_name].present?
else
@users = User.none
end
@users = @users.where(laboratory_id: current_laboratory.id) unless current_laboratory.main_site?
page = params[:page] || 1
limit = params[:limit] || 20
@user_count = @users.count
@users = @users.page(page).per(limit)
end
6 years ago
def shixun_members_added
raise("user_ids 不能为空!") if params[:user_ids].blank?
memberships = params[:user_ids]
memberships.each do |member|
ShixunMember.create!(:user_id => member, :shixun_id => @shixun.id, :role => 2)
end
end
def change_manager
# 搜索成员
if request.get?
@collaborators = @shixun.shixun_members.where("user_id != #{@shixun.user_id}")
else
if params[:user_id]
man_member = ShixunMember.where(:shixun_id => @shixun.id, :user_id => @shixun.user_id).first
cha_member = ShixunMember.where(:user_id => params[:user_id], :shixun_id => @shixun.id).first
if man_member && cha_member
man_member.update_attribute(:role, 2)
cha_member.update_attribute(:role, 1)
@shixun.update_attribute(:user_id, cha_member.user_id)
end
end
end
end
# 刪除合作者
def collaborators_delete
raise("user_id不能为空") if params[:user_id].blank?
shixun_member = ShixunMember.where(:user_id => params[:user_id], :shixun_id => @shixun.id, :role => 2).first
shixun_member.delete
end
# 实训的发送至课堂:搜索课堂
def search_user_courses
## 分页参数
page = params[:page] || 1
limit = params[:limit] || 20
if params[:search]
search = "%#{params[:search].to_s.strip.downcase}%"
course_ids = Course.find_by_sql("SELECT c.id FROM courses c, course_members m
WHERE m.course_id = c.id AND m.role in (1,2,3)
6 years ago
AND m.user_id=#{current_user.id} AND c.is_delete = 0 AND c.is_end = 0
AND c.name like '#{search}' ").map(&:id)
else
course_ids = Course.find_by_sql("SELECT c.id, c.name FROM courses c, course_members m
WHERE m.course_id = c.id AND m.role in (1,2,3)
6 years ago
AND m.user_id=#{current_user.id} AND c.is_delete = 0 AND c.is_end = 0").map(&:id)
end
@courses = Course.where(:id => course_ids)
## 云上实验室过滤
@courses = @courses.where(id: current_laboratory.all_courses)
@course_count = @courses.count
@courses = @courses.page(page).per(limit)
6 years ago
end
# 将实训发送到课程
def send_to_course
@course = Course.find(params[:course_id])
homework = HomeworksService.new.create_homework @shixun, @course, nil, current_user
CreateStudentWorkJob.perform_later(homework.id)
6 years ago
end
# 批量发送
def batch_send_to_course
@course = Course.find_by!(id: params[:course_id])
shixuns = Shixun.where(id: params[:shixun_ids]).unhidden
shixuns.each do |shixun|
homework = HomeworksService.new.create_homework shixun, @course, nil, current_user
CreateStudentWorkJob.perform_later(homework.id)
end
end
6 years ago
# 二维码扫描下载
def download_file
file_path = params[:file_name]
send_file "#{Rails.root}/#{file_path}", :filename => "#{file_path}",
:type => 'shixun',
:disposition => 'attachment' #inline can open in browser
end
# 撤销申请公开
def cancel_apply_public
tip_exception("实训已经公开,无法撤销") if @shixun.public == 2
ActiveRecord::Base.transaction do
apply = ApplyAction.where(:container_type => "ApplyShixun", :container_id => @shixun.id).order("created_at desc").first
if apply && apply.status == 0
apply.update_attributes!(status: 3)
apply.tidings&.destroy_all
end
@shixun.update_column(:public, 0)
end
normal_status(0, "成功撤销申请")
end
6 years ago
# 撤销发布
def cancel_publish
tip_exception("请先撤销申请公开,再撤销发布") if @shixun.public == 1
tip_exception("实训已经公开,无法撤销") if @shixun.public == 2
6 years ago
@shixun.update_column(:status, 0)
6 years ago
end
# 创建实训审核
def review_shixun
validate_review_shixun_params
# 没有记录就创建记录, 如果有记录就
@shixun.shixun_reviews.create!(user_id: current_user.id, status: params[:status],
review_type: params[:review_type], evaluate_content: params[:evaluate_content])
normal_status("审核完成")
end
# 实训审核最新记录
def review_newest_record
@content_record = @shixun.shixun_reviews.where(review_type: "Content").first
@perfer_record = @shixun.shixun_reviews.where(review_type: "Performance").first
end
6 years ago
private
def shixun_params
params.require(:shixun).permit(:name, :trainee, :webssh, :can_copy, :use_scope, :vnc, :test_set_permission,
6 years ago
:task_pass, :multi_webssh, :opening_time, :mirror_script_id, :code_hidden,
5 years ago
:hide_code, :forbid_copy, :vnc_evaluate, :code_edit_permission, :is_jupyter)
end
def validate_review_shixun_params
tip_exception("只有平台管理员或运营人员才能审核") if !admin_or_business?
tip_exception("审核类型参数不对") unless ["Content", "Performance"].include?(params[:review_type])
end
def shixun_info_params
params.require(:shixun_info).permit(:description, :evaluate_script)
6 years ago
end
6 years ago
def service_config_params
params.permit(shixun_service_configs: [:cpu_limit, :lower_cpu_limit, :memory_limit, :request_limit, :mirror_repository_id])
end
6 years ago
def find_shixun
@shixun = Shixun.find_by_identifier(params[:identifier])
if @shixun.blank?
normal_status(404, "...")
return
end
end
def find_repo_name
5 years ago
# 有私密版本库的参数时,需要拿私密仓库
5 years ago
@repo_path = if params[:secret_repository]
5 years ago
@shixun.shixun_secret_repository&.repo_path
else
@shixun.try(:repo_path)
5 years ago
end
logger.info("######{@repo_path}")
6 years ago
@path = params[:path]
end
def allowed
unless current_user.manager_of_shixun?(@shixun)
tip_exception(403, "..")
end
end
def portion_allowed
if current_user.shixun_identity(@shixun) > User::EDU_CERTIFICATION_TEACHER
raise Educoder::TipException.new(403, "..")
end
end
def special_allowed
if @shixun.status != 2
tip_exception(403, "..")
end
end
# 实训是否需要开启
def is_shixun_opening?
@shixun.opening_time.present? &&
@shixun.opening_time > Time.now &&
current_user.shixun_identity(@shixun) > User::EDU_SHIXUN_MEMBER
end
# 实训是否需要重置
def is_shixun_reset?(games, min_challenges, current_myshixun)
6 years ago
# 用户在申请发布之前,是否玩过实训 TODO: 重置的字段应该迁移到myshixuns表比较合适
modify_shixun = ShixunModify.exists?(:myshixun_id => current_myshixun.id, :shixun_id => @shixun.id, :status => 1)
games.size != min_challenges.size || modify_shixun
end
5 years ago
# 添加私密仓库
5 years ago
def add_secret_repository
5 years ago
# 防止跟tpm版本库重名加了前缀secret
repo_path = repo_namespace(current_user.login, "secret_#{@shixun.identifier}")
GitService.add_repository(repo_path: repo_path)
5 years ago
ShixunSecretRepository.create!(repo_name: repo_path.split(".")[0], shixun_id: @shixun.id)
5 years ago
end
5 years ago
def file_save_to_local(save_path, temp_file, ext)
unless Dir.exists?(save_path)
FileUtils.mkdir_p(save_path) ##不成功这里会抛异常
end
digest = md5_file(temp_file)
digest = "#{digest}_#{(Time.now.to_f * 1000).to_i}"
local_file_path = File.join(save_path, digest) + ext
save_temp_file(temp_file, local_file_path)
[local_file_path, digest]
end
def save_temp_file(temp_file, save_file_path)
File.open(save_file_path, 'wb') do |f|
temp_file.rewind
while (buffer = temp_file.read(8192))
f.write(buffer)
end
end
end
5 years ago
def file_ext(file_name)
ext = ''
exts = file_name.split(".")
if exts.size > 1
ext = ".#{exts.last}"
end
ext
end
5 years ago
def delete_file(file_path)
File.delete(file_path) if File.exist?(file_path)
end
def md5_file(temp_file)
md5 = Digest::MD5.new
temp_file.rewind
while (buffer = temp_file.read(8192))
md5.update(buffer)
end
md5.hexdigest
end
6 years ago
end