Merge branch 'dev_aliyun' into dev_cxt

chromesetting
cxt 5 years ago
commit 96f43439d2

11
.gitignore vendored

@ -6,6 +6,7 @@
# Ignore bundler config. # Ignore bundler config.
/.bundle /.bundle
/bundle
# Ignore lock config file # Ignore lock config file
*.lock *.lock
@ -70,3 +71,13 @@ vendor/bundle/
/workspace /workspace
/log /log
/public/admin /public/admin
/mysql_data
.generators
.rakeTasks
db/bak/
docker/
educoder.sql
redis_data/
Dockerfile

@ -103,3 +103,6 @@ gem 'diffy'
# oauth2 # oauth2
gem 'omniauth', '~> 1.9.0' gem 'omniauth', '~> 1.9.0'
gem 'omniauth-oauth2', '~> 1.6.0' gem 'omniauth-oauth2', '~> 1.6.0'
# global var
gem 'request_store'

@ -283,7 +283,7 @@ class ApplicationController < ActionController::Base
# 测试版前端需求 # 测试版前端需求
logger.info("subdomain:#{request.subdomain}") logger.info("subdomain:#{request.subdomain}")
if request.subdomain == "test-newweb" if request.subdomain != "www"
if params[:debug] == 'teacher' #todo 为了测试,记得讲debug删除 if params[:debug] == 'teacher' #todo 为了测试,记得讲debug删除
User.current = User.find 81403 User.current = User.find 81403
elsif params[:debug] == 'student' elsif params[:debug] == 'student'
@ -303,7 +303,7 @@ class ApplicationController < ActionController::Base
current_domain_session = session[:"#{default_yun_session}"] current_domain_session = session[:"#{default_yun_session}"]
if current_domain_session if current_domain_session
# existing session # existing session
(User.active.find(current_domain_session) rescue nil) User.current = (User.active.find(current_domain_session) rescue nil)
elsif autologin_user = try_to_autologin elsif autologin_user = try_to_autologin
autologin_user autologin_user
elsif params[:format] == 'atom' && params[:key] && request.get? && accept_rss_auth? elsif params[:format] == 'atom' && params[:key] && request.get? && accept_rss_auth?

@ -28,6 +28,7 @@ class AttachmentsController < ApplicationController
def create def create
# 1. 本地存储 # 1. 本地存储
# 2. 上传到云 # 2. 上传到云
begin
upload_file = params["file"] || params["#{params[:file_param_name]}"] # 这里的file_param_name是为了方便其他插件名称 upload_file = params["file"] || params["#{params[:file_param_name]}"] # 这里的file_param_name是为了方便其他插件名称
uid_logger("#########################file_params####{params["#{params[:file_param_name]}"]}") uid_logger("#########################file_params####{params["#{params[:file_param_name]}"]}")
raise "未上传文件" unless upload_file raise "未上传文件" unless upload_file
@ -57,7 +58,6 @@ class AttachmentsController < ApplicationController
@attachment = Attachment.where(disk_filename: disk_filename, @attachment = Attachment.where(disk_filename: disk_filename,
author_id: current_user.id, author_id: current_user.id,
cloud_url: remote_path).first cloud_url: remote_path).first
if @attachment.blank? if @attachment.blank?
@attachment = Attachment.new @attachment = Attachment.new
@attachment.filename = upload_file.original_filename @attachment.filename = upload_file.original_filename
@ -74,6 +74,10 @@ class AttachmentsController < ApplicationController
end end
render_json render_json
rescue => e
uid_logger_error(e.message)
tip_exception(e.message)
end
end end
def destroy def destroy
@ -196,4 +200,5 @@ class AttachmentsController < ApplicationController
end end
end end
end end
end end

@ -13,6 +13,9 @@ class ChallengesController < ApplicationController
include ShixunsHelper include ShixunsHelper
include ChallengesHelper include ChallengesHelper
# 新建实践题 # 新建实践题
def new def new
@position = @shixun.challenges.count + 1 @position = @shixun.challenges.count + 1
@ -160,6 +163,8 @@ class ChallengesController < ApplicationController
@shixun.increment!(:visits) @shixun.increment!(:visits)
end end
def show def show
@tab = params[:tab].nil? ? 1 : params[:tab].to_i @tab = params[:tab].nil? ? 1 : params[:tab].to_i
challenge_num = @shixun.challenges_count challenge_num = @shixun.challenges_count

@ -1,15 +1,18 @@
class GamesController < ApplicationController class GamesController < ApplicationController
before_action :require_login, :check_auth before_action :require_login, :check_auth
before_action :find_game before_action :find_game, except: [:jupyter]
before_action :find_shixun, only: [:show, :answer, :rep_content, :choose_build, :game_build, :game_status] before_action :find_shixun, only: [:show, :answer, :rep_content, :choose_build, :game_build, :game_status]
before_action :allowed before_action :allowed, except: [:jupyter]
#require 'iconv' #require 'iconv'
include GamesHelper include GamesHelper
include ApplicationHelper include ApplicationHelper
def show def show
uid_logger("--games show start") uid_logger("--games show start")
# 防止评测中途ajaxE被取消;3改成0是为了处理首次进入下一关的问题 # 防止评测中途ajaxE被取消;3改成0是为了处理首次进入下一关的问题
@ -88,6 +91,22 @@ class GamesController < ApplicationController
end end
end end
def jupyter
# Jupyter没有challenge
@myshixun = Myshixun.find_by_identifier params[:identifier]
unless current_user.id == @myshixun.user_id || current_user.admin_or_business?
raise Educoder::TipException.new(403, "..")
end
@shixun = @myshixun.shixun
# 判断tpm是否修改了
begin
@tpm_modified = @myshixun.repository_is_modified(@shixun.repo_path) # 判断TPM和TPI的版本库是否被改了
rescue
uid_logger("实训平台繁忙繁忙等级81")
end
end
def reset_vnc_link def reset_vnc_link
begin begin
# 删除vnc的pod # 删除vnc的pod

@ -23,7 +23,7 @@ class HackUserLastestCodesController < ApplicationController
# 同步代码 # 同步代码
def sync_code def sync_code
@my_hack.update_attributes(code: @hack.code, modify_time: @hack.modify_time) @my_hack.update_attributes(code: @hack.code, modify_time: Time.now)
end end
# 调试代码 # 调试代码

@ -0,0 +1,46 @@
class JupytersController < ApplicationController
include JupyterService
before_action :shixun, only: [:open, :open1, :test, :save]
def save_with_tpi
myshixun = Myshixun.find_by(identifier: params[:identifier])
jupyter_save_with_game(myshixun, params[:jupyter_port])
render json: {status: 0}
end
def save_with_tpm
shixun = Shixun.find_by(identifier: params[:identifier])
jupyter_save_with_shixun(shixun, params[:jupyter_port])
render json: {status: 0}
end
def get_info_with_tpi
myshixun = Myshixun.find_by(identifier: params[:identifier])
url = jupyter_url_with_game(myshixun)
port = jupyter_port_with_game(myshixun)
render json: {status: 0, url: url, port: port}
end
def get_info_with_tpm
shixun = Shixun.find_by(identifier: params[:identifier])
url = jupyter_url_with_shixun(shixun)
port = jupyter_port_with_shixun(shixun)
render json: {status: 0, url: url, port: port}
end
def reset_with_tpi
myshixun = Myshixun.find_by(identifier: params[:identifier])
info = jupyter_tpi_reset(myshixun)
render json: {status: 0, url: info[:url], port: info[:port]}
end
def reset_with_tpm
shixun = Shixun.find_by(identifier: params[:identifier])
info = jupyter_tpm_reset(shixun)
render json: {status: 0, url: info[:url], port: info[:port]}
end
end

@ -366,6 +366,31 @@ class MyshixunsController < ApplicationController
end end
end end
def sync_code
shixun_tomcat = edu_setting('cloud_bridge')
begin
git_myshixun_url = repo_ip_url @myshixun.repo_path
git_shixun_url = repo_ip_url @myshixun.shixun.try(:repo_path)
git_myshixun_url = Base64.urlsafe_encode64(git_myshixun_url)
git_shixun_url = Base64.urlsafe_encode64(git_shixun_url)
# todo: identifier 是以前的密码,用来验证的,新版如果不需要,和中间层协调更改.
params = {tpiID: "#{@myshixun.try(:id)}", tpiGitURL: "#{git_myshixun_url}", tpmGitURL: "#{git_shixun_url}",
identifier: "xinhu1ji2qu3"}
uri = "#{shixun_tomcat}/bridge/game/resetTpmRepository"
res = uri_post uri, params
if (res && res['code'] != 0)
tip_exception("实训云平台繁忙繁忙等级95")
end
shixun_new_commit = GitService.commits(repo_path: @myshixun.shixun.repo_path).first["id"]
@myshixun.update_attributes!(commit_id: shixun_new_commit, reset_time: @myshixun.shixun.try(:reset_time))
# 更新完成后,弹框则隐藏不再提示
@myshixun.update_column(:system_tip, false)
render_ok
rescue Exception => e
tip_exception("立即更新代码失败!#{e.message}")
end
end
# -----End # -----End

@ -6,16 +6,17 @@ class ShixunsController < ApplicationController
before_action :require_login, :check_auth, except: [:download_file, :index, :menus, :show, :show_right, :ranking_list, before_action :require_login, :check_auth, except: [:download_file, :index, :menus, :show, :show_right, :ranking_list,
:discusses, :collaborators, :fork_list, :propaedeutics] :discusses, :collaborators, :fork_list, :propaedeutics]
before_action :check_account, only: [:new, :create, :shixun_exec] before_action :check_account, only: [:new, :create, :shixun_exec, :jupyter_exec]
before_action :find_shixun, except: [:index, :new, :create, :menus, :get_recommend_shixuns, before_action :find_shixun, except: [:index, :new, :create, :menus, :get_recommend_shixuns,
:propaedeutics, :departments, :apply_shixun_mirror, :propaedeutics, :departments, :apply_shixun_mirror,
:get_mirror_script, :download_file, :shixun_list, :batch_send_to_course] :get_mirror_script, :download_file, :shixun_list, :batch_send_to_course]
before_action :shixun_access_allowed, except: [:index, :new, :create, :menus, :get_recommend_shixuns, before_action :shixun_access_allowed, except: [:index, :new, :create, :menus, :get_recommend_shixuns,
:propaedeutics, :departments, :apply_shixun_mirror, :propaedeutics, :departments, :apply_shixun_mirror, :jupyter_exec,
:get_mirror_script, :download_file, :shixun_list, :batch_send_to_course] :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] before_action :find_repo_name, only: [:repository, :commits, :file_content, :update_file, :shixun_exec, :copy,
:add_file, :jupyter_exec]
before_action :allowed, only: [:update, :close, :update_propaedeutics, :settings, :publish, :apply_public, before_action :allowed, only: [:update, :close, :update_propaedeutics, :settings, :publish, :apply_public,
:shixun_members_added, :change_manager, :collaborators_delete, :shixun_members_added, :change_manager, :collaborators_delete,
@ -206,7 +207,7 @@ class ShixunsController < ApplicationController
@new_shixun = Shixun.new @new_shixun = Shixun.new
@new_shixun.attributes = @shixun.attributes.dup.except("id","user_id","visits","gpid","status", "identifier", "averge_star", @new_shixun.attributes = @shixun.attributes.dup.except("id","user_id","visits","gpid","status", "identifier", "averge_star",
"homepage_show","repo_name", "myshixuns_count", "challenges_count", "homepage_show","repo_name", "myshixuns_count", "challenges_count",
"can_copy", "created_at", "updated_at") "can_copy", "created_at", "updated_at", "public")
@new_shixun.user_id = User.current.id @new_shixun.user_id = User.current.id
@new_shixun.averge_star = 5 @new_shixun.averge_star = 5
@new_shixun.identifier = generate_identifier Shixun, 8 @new_shixun.identifier = generate_identifier Shixun, 8
@ -364,74 +365,162 @@ class ShixunsController < ApplicationController
end end
def create def create
# 评测脚本的一些操作 @shixun = CreateShixunService.call(current_user, shixun_params, params)
main_type, sub_type = params[:main_type], params[:small_type]
mirror = MirrorScript.where(:mirror_repository_id => main_type)
identifier = generate_identifier Shixun, 8
@shixun = Shixun.new(shixun_params)
@shixun.identifier = identifier
@shixun.user_id = current_user.id
@shixun.reset_time, @shixun.modify_time = Time.now, Time.now
if sub_type.blank?
shixun_script = mirror.first.try(:script)
else
main_mirror = MirrorRepository.find(main_type).type_name
sub_mirror = MirrorRepository.where(id: sub_type).pluck(:type_name)
if main_mirror == "Java" && sub_mirror.include?("Mysql")
shixun_script = mirror.last.try(:script)
else
shixun_script = mirror.first.try(:script)
shixun_script = modify_shixun_script @shixun, shixun_script
end end
# 保存jupyter到版本库
def update_jupyter
jupyter_save_with_shixun(@shixun, params[:jupyter_port])
end end
ActiveRecord::Base.transaction do def update
# 镜像方面
mirror_ids = MirrorRepository.where(id: params[:main_type])
.or( MirrorRepository.where(id: params[:sub_type])).pluck(:id).uniq
old_mirror_ids = @shixun.shixun_mirror_repositories
.where(mirror_repository_id: params[:main_type])
.or(@shixun.shixun_mirror_repositories.where(mirror_repository_id: params[:sub_type]))
.pluck(:mirror_repository_id).uniq
new_mirror_id = (mirror_ids - old_mirror_ids).map{|id| {mirror_repository_id: id}} # 转换成数组hash方便操作
logger.info("##########new_mirror_id: #{new_mirror_id}")
logger.info("##########old_mirror_ids: #{old_mirror_ids}")
logger.info("##########mirror_ids: #{mirror_ids}")
# 服务配置方面
service_create_params = service_config_params[:shixun_service_configs]
.select{|config| !old_mirror_ids.include?(config[:mirror_repository_id]) &&
MirrorRepository.find(config[:mirror_repository_id]).name.present?}
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}")
begin begin
@shixun.save! ActiveRecord::Base.transaction do
# shixun_info关联ß @shixun.update_attributes(shixun_params)
ShixunInfo.create!(shixun_id: @shixun.id, evaluate_script: shixun_script, description: params[:description]) @shixun.shixun_info.update_attributes(shixun_info_params)
# 实训的公开范围 # 镜像变动
if params[:scope_partment].present? @shixun.shixun_mirror_repositories.where.not(mirror_repository_id: old_mirror_ids).destroy_all
arr = [] @shixun.shixun_mirror_repositories.create!(new_mirror_id)
ids = School.where(:name => params[:scope_partment]).pluck(:id).uniq # 镜像变动要更换服务配置
ids.each do |id| @shixun.shixun_service_configs.where.not(mirror_repository_id: old_mirror_ids).destroy_all
arr << { :school_id => id, :shixun_id => @shixun.id } @shixun.shixun_service_configs.create!(service_create_params)
service_update_params&.map do |service|
smr = @shixun.shixun_service_configs.find_by(mirror_repository_id: service[:mirror_repository_id])
smr.update_attributes(service)
end
# 添加第二仓库(管理员权限)
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
end
end
end
rescue => e
uid_logger_error(e.message)
tip_exception("基本信息更新失败:#{e.message}")
end end
ShixunSchool.create!(arr)
end end
# 实训合作者 # 实训权限设置
@shixun.shixun_members.create!(user_id: current_user.id, role: 1) 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)
@shixun.shixun_schools.where.not(school_id: school_id).destroy_all if school_id.present?
@shixun.shixun_schools.create!(school_params)
end
rescue => e
uid_logger_error("实训权限设置失败--------#{e.message}")
tip_exception("实训权限设置失败")
end
end
# 镜像-实训关联表 # 实训学习页面设置
ShixunMirrorRepository.create!(:shixun_id => @shixun.id, :mirror_repository_id => main_type.to_i) if main_type.present? def update_learn_setting
# 实训主镜像服务配置 begin
ShixunServiceConfig.create!(:shixun_id => @shixun.id, :mirror_repository_id => main_type.to_i) ActiveRecord::Base.transaction do
if sub_type.present? @shixun.update_attributes!(shixun_params)
sub_type.each do |mirror| end
ShixunMirrorRepository.create!(:shixun_id => @shixun.id, :mirror_repository_id => mirror) rescue => e
# 实训子镜像服务配置 uid_logger_error("实训学习页面设置失败--------#{e.message}")
name = MirrorRepository.find_by(id: mirror).try(:name) #查看镜像是否有名称,如果没有名称就不用服务配置 tip_exception("实训学习页面设置失败")
ShixunServiceConfig.create!(:shixun_id => @shixun.id, :mirror_repository_id => mirror) if name.present?
end end
end end
# 创建版本库 # Jupyter数据集
repo_path = repo_namespace(User.current.login, @shixun.identifier) def get_data_sets
GitService.add_repository(repo_path: repo_path) page = params[:page] || 1
# todo: 为什么保存的时候要去除后面的.git呢?? limit = params[:limit] || 10
@shixun.update_column(:repo_name, repo_path.split(".")[0]) data_sets = @shixun.data_sets
@data_count = data_sets.count
@data_sets= data_sets.order("created_on desc").page(page).per(limit)
@absolute_folder = edu_setting('shixun_folder')
end
# 将实训标志为该云上实验室建立 # 实训测试集附件
Laboratory.current.laboratory_shixuns.create!(shixun: @shixun, ownership: true) def upload_data_sets
rescue Exception => e 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) uid_logger_error(e.message)
tip_exception("实训创建失败") tip_exception(e.message)
raise ActiveRecord::Rollback
end end
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
end end
def apply_shixun_mirror def apply_shixun_mirror
@ -462,61 +551,6 @@ class ShixunsController < ApplicationController
tip_exception("申请失败") tip_exception("申请失败")
end end
def update
ActiveRecord::Base.transaction do
begin
@shixun.shixun_mirror_repositories.destroy_all
if params[:main_type].present?
ShixunMirrorRepository.create(:shixun_id => @shixun.id, :mirror_repository_id => params[:main_type].to_i)
end
if params[:small_type].present?
params[:small_type].each do |mirror|
ShixunMirrorRepository.create(:shixun_id => @shixun.id, :mirror_repository_id => mirror)
end
end
@shixun.update_attributes(shixun_params)
@shixun.shixun_info.update_attributes(shixun_info_params)
@shixun.shixun_schools.delete_all
# scope_partment: 高校的名称
if params[:scope_partment].present?
arr = []
ids = School.where(:name => params[:scope_partment]).pluck(:id).uniq
ids.each do |id|
arr << { :school_id => id, :shixun_id => @shixun.id }
end
ShixunSchool.create!(arr)
end
# 超级管理员和运营人员才能保存 中间层服务器pod信息的配置
# 如果镜像改动了,则也需要更改
mirror = @shixun.shixun_service_configs.map(&:mirror_repository_id).sort
new_mirror = params[:shixun_service_configs].map{|c| c[:mirror_repository_id]}.sort
if current_user.admin? || current_user.business? || (mirror != new_mirror)
@shixun.shixun_service_configs.destroy_all
service_config_params[:shixun_service_configs].each do |config|
name = MirrorRepository.find_by_id(config[:mirror_repository_id])&.name
# 不保存没有镜像的配置
@shixun.shixun_service_configs.create!(config) if name.present?
end
end
# 添加第二仓库
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
end
end
rescue Exception => e
uid_logger_error("实训保存失败--------#{e.message}")
tip_exception("实训保存失败")
raise ActiveRecord::Rollback
end
end
end
# 永久关闭实训 # 永久关闭实训
def close def close
@ -552,6 +586,8 @@ class ShixunsController < ApplicationController
# @evaluate_scirpt = @shixun.evaluate_script || "无" # @evaluate_scirpt = @shixun.evaluate_script || "无"
end end
# 获取脚本内容 # 获取脚本内容
def get_script_contents def get_script_contents
mirrir_script = MirrorScript.find(params[:script_id]) mirrir_script = MirrorScript.find(params[:script_id])
@ -708,112 +744,40 @@ class ShixunsController < ApplicationController
end end
end end
# def shixun_exec # jupyter开启挑战
# if is_shixun_opening? def jupyter_exec
# tip_show_exception(-3, "#{@shixun.opening_time.strftime('%Y-%m-%d %H:%M:%S')}") begin
# end if is_shixun_opening?
# current_myshixun = @shixun.current_myshixun(current_user.id) tip_show_exception(-3, "#{@shixun.opening_time.strftime('%Y-%m-%d %H:%M:%S')}")
# end
# min_challenges = @shixun.challenges.pluck(:id , :st) current_myshixun = @shixun.current_myshixun(current_user.id)
# # 因为读写分离有延迟所以如果是重置来的请求可以先跳过重置过来的params[:reset]为1 if current_myshixun
# if current_myshixun && params[:reset] != "1" @myshixun = current_myshixun
# games = current_myshixun.games else
# # 如果TPM和TPI的管卡数不相等或者关卡顺序错了说明实训被极大的改动需要重置,实训发布前打过的实训都需要重置 commit = GitService.commits(repo_path: @repo_path).try(:first)
# if is_shixun_reset?(games, min_challenges, current_myshixun) uid_logger("First comit########{commit}")
# # 这里页面弹框要收到 当前用户myshixun的identifier. tip_exception("开启挑战前请先在Jupyter中填写内容") if commit.blank?
# tip_show_exception("/myshixuns/#{current_myshixun.try(:identifier)}/reset_my_game") commit_id = commit["id"]
# end cloud_bridge = edu_setting('cloud_bridge')
# myshixun_identifier = generate_identifier Myshixun, 10
# ActiveRecord::Base.transaction do
# if current_myshixun.repo_name.nil? @myshixun = @shixun.myshixuns.create!(user_id: current_user.id, identifier: myshixun_identifier,
# g = Gitlab.client modify_time: @shixun.modify_time, reset_time: @shixun.reset_time,
# repo_name = g.project(current_myshixun.gpid).try(:path_with_namespace) onclick_time: Time.now, commit_id: commit_id)
# current_myshixun.update_column(:repo_name, repo_name) # fork仓库
# end project_fork(@myshixun, @repo_path, current_user.login)
# rep_url = Base64.urlsafe_encode64(repo_ip_url @repo_path)
# # 如果存在实训,则直接进入实训 uri = "#{cloud_bridge}/bridge/game/openGameInstance"
# # 如果实训允许跳关传参params[:challenge_id]跳入具体的关卡 params = {tpiID: "#{@myshixun.id}", tpmGitURL: rep_url, tpiRepoName: @myshixun.repo_name.split("/").last}
# @current_task = interface_post uri, params, 83, "实训云平台繁忙繁忙等级83"
# if params[:challenge_id] end
# game = games.where(challenge_id: params[:challenge_id]).take end
# if @shixun.task_pass || game.status != 3 rescue => e
# game uid_logger_error(e.message)
# else tip_exception("#{e.message}")
# current_myshixun.current_task(games) end
# end 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)
# 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
#
# begin
# ActiveRecord::Base.transaction do
# begin
# myshixun_identifier = generate_identifier Myshixun, 10
# myshixun_params = {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}
# @myshixun = @shixun.myshixuns.create!(myshixun_params)
# # 其它创建关卡等操作
# 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, open_time: Time.now,
# identifier: game_identifier, modify_time: challenge.modify_time))
# end
# end
# @current_task = @myshixun.current_task(@myshixun.games)
# rescue Exception => e
# logger.error("------ActiveRecord::RecordInvalid: #{e.message}")
# raise("ActiveRecord::RecordInvalid")
# end
# end
# # 如果实训是纯选择题则不需要去fork仓库以及中间层的相关操作了
# ActiveRecord::Base.transaction do
# unless is_choice_type
# # fork仓库
# cloud_bridge = edu_setting('cloud_bridge')
# 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
# end
# rescue Exception => e
# logger.info("shixun_exec error: #{e.message}")
# if e.message != "ActiveRecord::RecordInvalid"
# logger.error("##########project_fork error #{e.message}")
# @myshixun.destroy!
# end
# raise "实训云平台繁忙繁忙等级81"
# end
# end
# end
# gameID 及实训ID
# status: 0 , 1 申请过, 2实训关卡路径未填 3 实训标签未填, 4 实训未创建关卡
def publish def publish
@status = 0 @status = 0
@position = [] @position = []
@ -849,7 +813,7 @@ class ShixunsController < ApplicationController
def apply_public def apply_public
tip_exception(-1, "请先发布实训再申请公开") if @shixun.status != 2 tip_exception(-1, "请先发布实训再申请公开") if @shixun.status != 2
ActiveRecord::Base.transaction do ActiveRecord::Base.transaction do
@shixun.update_attributes!(pubic: 1) @shixun.update_attributes!(public: 1)
apply = ApplyAction.where(:container_type => "ApplyShixun", :container_id => @shixun.id).order("created_at desc").first apply = ApplyAction.where(:container_type => "ApplyShixun", :container_id => @shixun.id).order("created_at desc").first
if apply && apply.status == 0 if apply && apply.status == 0
@status = 0 @status = 0
@ -1035,10 +999,9 @@ class ShixunsController < ApplicationController
private private
def shixun_params def shixun_params
raise("实训名称不能为空") if params[:shixun][:name].blank?
params.require(:shixun).permit(:name, :trainee, :webssh, :can_copy, :use_scope, :vnc, :test_set_permission, params.require(:shixun).permit(:name, :trainee, :webssh, :can_copy, :use_scope, :vnc, :test_set_permission,
:task_pass, :multi_webssh, :opening_time, :mirror_script_id, :code_hidden, :task_pass, :multi_webssh, :opening_time, :mirror_script_id, :code_hidden,
:hide_code, :forbid_copy, :vnc_evaluate, :code_edit_permission) :hide_code, :forbid_copy, :vnc_evaluate, :code_edit_permission, :is_jupyter)
end end
def validate_review_shixun_params def validate_review_shixun_params
@ -1047,8 +1010,6 @@ private
end end
def shixun_info_params def shixun_info_params
raise("实训描述不能为空") if params[:shixun_info][:description].blank?
raise("评测脚本不能为空") if params[:shixun_info][:evaluate_script].blank?
params.require(:shixun_info).permit(:description, :evaluate_script) params.require(:shixun_info).permit(:description, :evaluate_script)
end end
@ -1116,4 +1077,47 @@ private
ShixunSecretRepository.create!(repo_name: repo_path.split(".")[0], shixun_id: @shixun.id) ShixunSecretRepository.create!(repo_name: repo_path.split(".")[0], shixun_id: @shixun.id)
end end
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
def file_ext(file_name)
ext = ''
exts = file_name.split(".")
if exts.size > 1
ext = ".#{exts.last}"
end
ext
end
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
end end

@ -529,7 +529,7 @@ class StudentWorksController < ApplicationController
@echart_data = student_efficiency(@homework, @work) @echart_data = student_efficiency(@homework, @work)
@myself_eff = @echart_data[:efficiency_list].find { |item| item.last == @user.id } @myself_eff = @echart_data[:efficiency_list].find { |item| item.last == @user.id }
@myself_consume = @echart_data[:consume_list].find { |item| item.last == @user.id } @myself_consume = @echart_data[:consume_list].find { |item| item.last == @user.id }
filename_ = "#{@use&.student_id}_#{@use&.real_name}_#{@shixun&.name}_#{Time.now.strftime('%Y%m%d_%H%M%S')}.pdf" filename_ = "#{@user&.student_id}_#{@user&.real_name}_#{@shixun&.name}_#{Time.now.strftime('%Y%m%d_%H%M%S')}.pdf"
filename = filename_.strip.tr("+/", "-_") filename = filename_.strip.tr("+/", "-_")
stylesheets = %w(shixun_work/shixun_work.css shared/codemirror.css) stylesheets = %w(shixun_work/shixun_work.css shared/codemirror.css)
if params[:export].present? && params[:export] if params[:export].present? && params[:export]

@ -204,7 +204,7 @@ class SubjectsController < ApplicationController
def add_shixun_to_stage def add_shixun_to_stage
identifier = generate_identifier Shixun, 8 identifier = generate_identifier Shixun, 8
ActiveRecord::Base.transaction do ActiveRecord::Base.transaction do
@shixun = Shixun.create!(name: params[:name], user_id: current_user.id, identifier: identifier) @shixun = Shixun.create!(name: params[:name], user_id: current_user.id, identifier: identifier, is_jupyter: params[:is_jupyter])
# 添加合作者 # 添加合作者
@shixun.shixun_members.create!(user_id: current_user.id, role: 1) @shixun.shixun_members.create!(user_id: current_user.id, role: 1)
# 创建长字段 # 创建长字段

@ -4,4 +4,7 @@ module ChallengesHelper
str.gsub(/\A\r/, "\r\r") str.gsub(/\A\r/, "\r\r")
end end
end end

@ -33,6 +33,10 @@ class Attachment < ApplicationRecord
File.join(File.join(Rails.root, "files"), disk_directory.to_s, disk_filename.to_s) File.join(File.join(Rails.root, "files"), disk_directory.to_s, disk_filename.to_s)
end end
def relative_path_filename
File.join(disk_directory.to_s, disk_filename.to_s)
end
def title def title
title = filename title = filename
if container && container.is_a?(StudentWork) && author_id != User.current.id if container && container.is_a?(StudentWork) && author_id != User.current.id

@ -38,12 +38,20 @@ class Laboratory < ApplicationRecord
find_by_identifier(subdomain) find_by_identifier(subdomain)
end end
def self.current=(laboratory) # def self.current=(laboratory)
Thread.current[:current_laboratory] = laboratory # Thread.current[:current_laboratory] = laboratory
# end
#
# def self.current
# Thread.current[:current_laboratory] ||= Laboratory.find(1)
# end
def self.current=(user)
RequestStore.store[:current_laboratory] = user
end end
def self.current def self.current
Thread.current[:current_laboratory] ||= Laboratory.find(1) RequestStore.store[:current_laboratory] ||= User.anonymous
end end
def shixuns def shixuns

@ -67,6 +67,7 @@ class LaboratorySetting < ApplicationRecord
{ 'name' => '在线竞赛', 'link' => '/competitions', 'hidden' => false }, { 'name' => '在线竞赛', 'link' => '/competitions', 'hidden' => false },
{ 'name' => '教学案例', 'link' => '/moop_cases', 'hidden' => false }, { 'name' => '教学案例', 'link' => '/moop_cases', 'hidden' => false },
{ 'name' => '交流问答', 'link' => '/forums', 'hidden' => false }, { 'name' => '交流问答', 'link' => '/forums', 'hidden' => false },
{ 'name' => '开发者社区', 'link' => '/problems', 'hidden' => false },
], ],
footer: nil footer: nil
} }

@ -28,6 +28,11 @@ class Myshixun < ApplicationRecord
"#{self.repo_name}.git" "#{self.repo_name}.git"
end end
def repo_save_path
self.repo_name.split('/').last
end
def is_complete? def is_complete?
self.status == 1 self.status == 1
end end

@ -52,7 +52,8 @@ module Searchable::Shixun
challenges_count: challenges_count, challenges_count: challenges_count,
study_count: myshixuns_count, study_count: myshixuns_count,
star: averge_star, star: averge_star,
level: shixun_level level: shixun_level,
is_jupyter: is_jupyter
} }
end end

@ -10,6 +10,7 @@ class Shixun < ApplicationRecord
# webssh 0不开启webssh1开启练习模式; 2开启评测模式 # webssh 0不开启webssh1开启练习模式; 2开启评测模式
# trainee 实训的难度 # trainee 实训的难度
# vnc: VCN实训是否用于评测 # vnc: VCN实训是否用于评测
validates_presence_of :name, message: "实训名称不能为空"
has_many :challenges, -> {order("challenges.position asc")}, dependent: :destroy has_many :challenges, -> {order("challenges.position asc")}, dependent: :destroy
has_many :challenge_tags, through: :challenges has_many :challenge_tags, through: :challenges
has_many :myshixuns, :dependent => :destroy has_many :myshixuns, :dependent => :destroy
@ -55,6 +56,8 @@ class Shixun < ApplicationRecord
has_many :laboratory_shixuns, dependent: :destroy has_many :laboratory_shixuns, dependent: :destroy
belongs_to :laboratory, optional: true belongs_to :laboratory, optional: true
# Jupyter数据集,附件
has_many :data_sets, ->{where(attachtype: 2)}, class_name: 'Attachment', as: :container, dependent: :destroy
scope :search_by_name, ->(keyword) { where("name like ? or description like ? ", scope :search_by_name, ->(keyword) { where("name like ? or description like ? ",
"%#{keyword}%", "%#{keyword}%") } "%#{keyword}%", "%#{keyword}%") }

@ -1,8 +1,6 @@
class ShixunInfo < ApplicationRecord class ShixunInfo < ApplicationRecord
belongs_to :shixun belongs_to :shixun
validates_uniqueness_of :shixun_id validates_uniqueness_of :shixun_id
validates_presence_of :shixun_id
after_commit :create_diff_record after_commit :create_diff_record
private private

@ -2,4 +2,5 @@ class ShixunMirrorRepository < ApplicationRecord
belongs_to :shixun belongs_to :shixun
belongs_to :mirror_repository belongs_to :mirror_repository
validates_uniqueness_of :shixun_id, :scope => :mirror_repository_id validates_uniqueness_of :shixun_id, :scope => :mirror_repository_id
validates_presence_of :shixun_id, :mirror_repository_id
end end

@ -1,4 +1,6 @@
class ShixunServiceConfig < ApplicationRecord class ShixunServiceConfig < ApplicationRecord
belongs_to :shixun belongs_to :shixun
belongs_to :mirror_repository belongs_to :mirror_repository
validates_presence_of :shixun_id, :mirror_repository_id
end end

@ -540,12 +540,20 @@ class User < ApplicationRecord
mail.present? mail.present?
end end
# def self.current=(user)
# Thread.current[:current_user] = user
# end
#
# def self.current
# Thread.current[:current_user] ||= User.anonymous
# end
def self.current=(user) def self.current=(user)
Thread.current[:current_user] = user RequestStore.store[:current_user] = user
end end
def self.current def self.current
Thread.current[:current_user] ||= User.anonymous RequestStore.store[:current_user] ||= User.anonymous
end end
def self.anonymous def self.anonymous

@ -0,0 +1,219 @@
#coding=utf-8
module JupyterService
def _open_shixun_jupyter(shixun)
if shixun.is_jupyter?
shixun_tomcat = edu_setting('cloud_bridge')
uri = "#{shixun_tomcat}/bridge/jupyter/get"
tpiID = "tpm#{shixun.id}"
mount = shixun.data_sets.present?
params = {tpiID: tpiID, identifier: shixun.identifier, needMount: mount,
:containers => "#{Base64.urlsafe_encode64(shixun_container_limit(shixun))}"}
logger.info "test_juypter: uri->#{uri}, params->#{params}"
res = uri_post uri, params
if res && res['code'].to_i != 0
raise("实训云平台繁忙繁忙等级99")
end
logger.info "test_juypter: #{res}"
@shixun_jupyter_port = res['port']
jupyter_service = edu_setting('jupyter_service')
url =
if false
"https://#{jupyter_service}:#{res['port']}/notebooks/data/workspace/myshixun_#{tpiID}/01.ipynb"
else
"https://#{res['port']}.#{jupyter_service}/notebooks/data/workspace/myshixun_#{tpiID}/01.ipynb"
end
return url
end
end
def jupyter_url_with_shixun(shixun)
#打开tpm - juypter接口
_open_shixun_jupyter(shixun)
end
def jupyter_port_with_shixun(shixun)
if @shixun_jupyter_port.to_i <=0
_open_shixun_jupyter(shixun)
end
@shixun_jupyter_port
end
def _open_game_jupyter(myshixun)
## 打开tpi
shixun = myshixun.shixun
if shixun.is_jupyter?
shixun_tomcat = edu_setting('cloud_bridge')
uri = "#{shixun_tomcat}/bridge/jupyter/get"
tpiID = myshixun.id
mount = myshixun.shixun.data_sets.present?
params = {tpiID: tpiID, identifier: shixun.identifier, needMount: mount,
:containers => "#{Base64.urlsafe_encode64(shixun_container_limit(shixun))}"}
res = uri_post uri, params
logger.info "test_juypter: #{res}"
if res && res['code'].to_i != 0
raise("实训云平台繁忙繁忙等级99")
end
@game_jupyter_port = res['port']
repo_save_path = myshixun.repo_save_path
jupyter_service = edu_setting('jupyter_service')
if false
"https://#{jupyter_service}:#{res['port']}/notebooks/data/workspace/myshixun_#{tpiID}/#{repo_save_path}/01.ipynb"
else
"https://#{res['port']}.#{jupyter_service}/notebooks/data/workspace/myshixun_#{tpiID}/#{repo_save_path}/01.ipynb"
end
end
end
def jupyter_url_with_game(myshixun)
_open_game_jupyter(myshixun)
end
def jupyter_port_with_game(myshixun)
if @game_jupyter_port.to_i <=0
_open_game_jupyter(myshixun)
end
@game_jupyter_port
end
def jupyter_save_with_shixun(shixun,jupyter_port)
author_name = current_user.real_name
author_email = current_user.git_mail
message = "User submitted"
tpiID = "tpm#{shixun.id}"
jupyter_service = edu_setting('jupyter_service')
src_url =
if false
"https://#{jupyter_service}:#{jupyter_port}/nbconvert/notebook/data/workspace/myshixun_#{tpiID}/01.ipynb?download=true"
else
"https://#{jupyter_port}.#{jupyter_service}/nbconvert/notebook/data/workspace/myshixun_#{tpiID}/01.ipynb?download=true"
end
#https://47526.jupyter.educoder.net/nbconvert/notebook/data/workspace/myshixun_570461/f2ef5p798r20191210163135/01.ipynb?download=true
response = Faraday.get(src_url)
if response.status.to_i != 200
raise("获取文件内容失败:#{response.status}")
end
content = response.body.force_encoding('utf-8')
c = GitService.update_file(repo_path: shixun.repo_path,
file_path: "01.ipynb",
message: message,
content: content,
author_name: author_name,
author_email: author_email)
return c.size
end
def jupyter_save_with_game(myshixun,jupyter_port)
author_name = current_user.real_name
author_email = current_user.git_mail
message = "User submitted"
tpiID = myshixun.id
repo_save_path = myshixun.repo_save_path
jupyter_service = edu_setting('jupyter_service')
src_url =
if false
"https://#{jupyter_service}:#{jupyter_port}/nbconvert/notebook/data/workspace/myshixun_#{tpiID}/#{repo_save_path}/01.ipynb?download=true"
else
"https://#{jupyter_port}.#{jupyter_service}/nbconvert/notebook/data/workspace/myshixun_#{tpiID}/#{repo_save_path}/01.ipynb?download=true"
end
response = Faraday.get(src_url)
if response.status.to_i != 200
raise("获取文件内容失败:#{response.status}")
end
content = response.body.force_encoding('utf-8')
c = GitService.update_file(repo_path: myshixun.repo_path,
file_path: "01.ipynb",
message: message,
content: content,
author_name: author_name,
author_email: author_email)
return c.size
end
##重置jupyter环境
def jupyter_tpi_reset(myshixun)
jupyter_delete_tpi(myshixun)
url = jupyter_url_with_game(myshixun)
port = jupyter_port_with_game(myshixun)
{url: url, port: port}
end
## 重置tpm环境
def jupyter_tpm_reset(shixun)
jupyter_delete_tpm(shixun)
url = jupyter_url_with_shixun(shixun)
port = jupyter_port_with_shixun(shixun)
{url: url, port: port}
end
# 删除pod
def jupyter_delete_tpi(myshixun)
myshixun_id = myshixun.id
digest = myshixun.identifier + edu_setting('bridge_secret_key')
digest_key = Digest::SHA1.hexdigest("#{digest}")
begin
shixun_tomcat = edu_setting('cloud_bridge')
uri = "#{shixun_tomcat}/bridge/jupyter/delete"
Rails.logger.info("#{current_user} => cloese_jupyter digest is #{digest}")
params = {:tpiID => myshixun_id, :digestKey => digest_key, :identifier => myshixun.identifier}
res = uri_post uri, params
if res && res['code'].to_i != 0
raise("实训云平台繁忙繁忙等级110")
end
end
end
def jupyter_delete_tpm(shixun)
tpiID = "tpm#{shixun.id}"
digest = shixun.identifier + edu_setting('bridge_secret_key')
digest_key = Digest::SHA1.hexdigest("#{digest}")
begin
shixun_tomcat = edu_setting('cloud_bridge')
uri = "#{shixun_tomcat}/bridge/jupyter/delete"
Rails.logger.info("#{current_user} => cloese_jupyter digest is #{digest}")
params = {:tpiID => tpiID, :digestKey => digest_key, :identifier => shixun.identifier}
res = uri_post uri, params
if res && res['code'].to_i != 0
raise("实训云平台繁忙繁忙等级110")
end
end
end
end

@ -25,7 +25,7 @@ class ShixunSearchService < ApplicationService
else else
none_shixun_ids = ShixunSchool.where("school_id != #{User.current.school_id}").pluck(:shixun_id) none_shixun_ids = ShixunSchool.where("school_id != #{User.current.school_id}").pluck(:shixun_id)
@shixuns = @shixuns.where.not(id: none_shixun_ids).where(hidden: 0, status: 2, public: 2).or(@shixuns.where(id: current_user.shixuns)) @shixuns = @shixuns.where.not(id: none_shixun_ids).where(hidden: 0, status: 2, public: 2).or(@shixuns.where(id: User.current.shixuns))
end end
end end
@ -38,6 +38,7 @@ class ShixunSearchService < ApplicationService
@shixuns = @shixuns.where(trainee: params[:diff]) @shixuns = @shixuns.where(trainee: params[:diff])
end end
Rails.logger.info("search_shixun_ids: #{@shixuns.pluck(:id)}")
Shixun.search(keyword, search_options) Shixun.search(keyword, search_options)
end end

@ -0,0 +1,115 @@
class CreateShixunService < ApplicationService
attr_reader :user, :params, :permit_params
def initialize(user, permit_params, params)
@user = user
@params = params
@permit_params = permit_params
end
def call
shixun = Shixun.new(permit_params)
identifier = Util::UUID.generate_identifier(Shixun, 8)
shixun.identifier= identifier
shixun.user_id = user.id
main_mirror = MirrorRepository.find params[:main_type]
sub_mirrors = MirrorRepository.where(id: params[:sub_type])
begin
ActiveRecord::Base.transaction do
shixun.save!
# 获取脚本内容
shixun_script = get_shixun_script(shixun, main_mirror, sub_mirrors)
# 创建额外信息
ShixunInfo.create!(shixun_id: shixun.id, evaluate_script: shixun_script, description: params[:description])
# 创建合作者
shixun.shixun_members.create!(user_id: user.id, role: 1)
# 创建镜像
ShixunMirrorRepository.create!(:shixun_id => shixun.id, :mirror_repository_id => main_mirror.id)
# 创建主服务配置
ShixunServiceConfig.create!(:shixun_id => shixun.id, :mirror_repository_id => main_mirror.id)
# 创建子镜像相关数据(实训镜像关联表,子镜像服务配置)
sub_mirrors.each do |sub|
ShixunMirrorRepository.create!(:shixun_id => shixun.id, :mirror_repository_id => sub.id)
# 实训子镜像服务配置
name = sub.name #查看镜像是否有名称,如果没有名称就不用服务配置
ShixunServiceConfig.create!(:shixun_id => shixun.id, :mirror_repository_id => sub.id) if name.present?
end
# 创建版本库
repo_path = repo_namespace(user.login, shixun.identifier)
GitService.add_repository(repo_path: repo_path)
shixun.update_column(:repo_name, repo_path.split(".")[0])
# 如果是云上实验室,创建相关记录
if !Laboratory.current.main_site?
Laboratory.current.laboratory_shixuns.create!(shixun: shixun, ownership: true)
end
# 如果是jupyter先创建一个目录,为了挂载(因为后续数据集开启Pod后环境在没销毁前你上传数据集是挂载不上目录的因此要先创建目录方便中间层挂载)
if shixun.is_jupyter?
folder = edu_setting('shixun_folder')
path = "#{folder}/#{identifier}"
FileUtils.mkdir_p(path, :mode => 0777) unless File.directory?(dir)
end
return shixun
end
rescue => e
Rails.logger.error("shixun_create_error: #{e.message}")
raise("创建实训失败!")
end
end
private
def get_shixun_script shixun, main_mirror, sub_mirrors
if !shixun.is_jupyter?
mirror = main_mirror.mirror_scripts
if main_mirror.blank?
modify_shixun_script shixun, mirror.first&.(:script)
else
sub_name = sub_mirrors.pluck(:type_name)
if main_mirror.type_name == "Java" && sub_name.include?("Mysql")
mirror.last.try(:script)
else
shixun_script = mirror.first&.script
modify_shixun_script shixun, shixun_script
end
end
end
end
def modify_shixun_script shixun, script
if script.present?
source_class_name = []
challenge_program_name = []
shixun.challenges.map(&:exec_path).each do |exec_path|
challenge_program_name << "\"#{exec_path}\""
if shixun.main_mirror_name == "Java"
if exec_path.nil? || exec_path.split("src/")[1].nil?
source = "\"\""
else
source = "\"#{exec_path.split("src/")[1].split(".java")[0]}\""
end
logger.info("----source: #{source}")
source_class_name << source.gsub("/", ".") if source.present?
elsif shixun.main_mirror_name.try(:first) == "C#"
if exec_path.nil? || exec_path.split(".")[1].nil?
source = "\"\""
else
source = "\"#{exec_path.split(".")[0]}.exe\""
end
source_class_name << source if source.present?
end
end
script = if script.include?("sourceClassName") && script.include?("challengeProgramName")
script.gsub(/challengeProgramNames=\(.*\)/,"challengeProgramNames=\(#{challenge_program_name.reject(&:blank?).join(" ")}\)").gsub(/sourceClassNames=\(.*\)/, "sourceClassNames=\(#{source_class_name.reject(&:blank?).join(" ")}\)")
else
script.gsub(/challengeProgramNames=\(.*\)/,"challengeProgramNames=\(#{challenge_program_name.reject(&:blank?).join(" ")}\)").gsub(/sourceClassNames=\(.*\)/, "sourceClassNames=\(#{challenge_program_name.reject(&:blank?).join(" ")}\)")
end
end
return script
end
# 版本库目录空间
def repo_namespace(user, shixun_identifier)
"#{user}/#{shixun_identifier}.git"
end
end

@ -60,7 +60,7 @@ class Subjects::CopySubjectService < ApplicationService
shixun = stage_shixun.shixun shixun = stage_shixun.shixun
to_shixun = Shixun.new to_shixun = Shixun.new
to_shixun.attributes = shixun.attributes.dup.except('id', 'user_id', 'identifier', 'homepage_show', to_shixun.attributes = shixun.attributes.dup.except('id', 'user_id', 'identifier', 'homepage_show',
'use_scope', 'averge_star', 'myshixuns_count', 'challenges_count') 'use_scope', 'averge_star', 'myshixuns_count', 'challenges_count', "public")
to_shixun.identifier = Util::UUID.generate_identifier(Shixun, 8) to_shixun.identifier = Util::UUID.generate_identifier(Shixun, 8)
to_shixun.user_id = user.id to_shixun.user_id = user.id
if laboratory if laboratory

@ -44,7 +44,7 @@ class Users::ShixunService
def user_policy_filter(relations) def user_policy_filter(relations)
# 只有自己或者管理员才有过滤筛选及查看全部状态下实训功能 # 只有自己或者管理员才有过滤筛选及查看全部状态下实训功能
if self_or_admin? if self_or_admin?
relations = relations.where.not(status: -1) relations = relations.where.not(status: -1).where(hidden: false)
status_filter(relations) status_filter(relations)
else else
relations.where(status: [2, 3], hidden: false) relations.where(status: [2, 3], hidden: false)

@ -35,7 +35,7 @@ class Users::SubjectService
def user_policy_filter(relations) def user_policy_filter(relations)
# 只有自己或者管理员才有过滤筛选及查看全部状态下实训功能 # 只有自己或者管理员才有过滤筛选及查看全部状态下实训功能
if self_or_admin? if self_or_admin?
status_filter(relations) status_filter(relations.unhidden)
else else
relations.where(status: 2, hidden: false) relations.where(status: 2, hidden: false)
end end

@ -557,7 +557,6 @@ a.user_orangebg_btn{background-color:#FF6800;color: #fff;}
a.user_greybg_btn{background-color:#747A7F;color: #fff;} a.user_greybg_btn{background-color:#747A7F;color: #fff;}
/*.user_white_btn{border: 1px solid #ffffff;color: #ffffff!important;}*/ /*.user_white_btn{border: 1px solid #ffffff;color: #ffffff!important;}*/
.pointer{cursor: pointer} .pointer{cursor: pointer}
.cdefault{cursor: default} .cdefault{cursor: default}

@ -4,8 +4,10 @@ json.description @shixun.description
json.power @editable json.power @editable
json.shixun_identifier @shixun.identifier json.shixun_identifier @shixun.identifier
json.shixun_status @shixun.status json.shixun_status @shixun.status
json.is_jupyter @shixun.is_jupyter?
json.allow_skip @shixun.task_pass json.allow_skip @shixun.task_pass
# 列表 # 列表
if @challenges.present? if @challenges.present?
json.challenge_list @challenges do |challenge| json.challenge_list @challenges do |challenge|

@ -0,0 +1,7 @@
json.user do
json.partial! 'users/user', user: current_user
end
json.(@shixun, :id, :identifier, :status, :name)
json.myshixun_identifier @myshixun.identifier
json.tpm_modified @tpm_modified

@ -12,4 +12,5 @@ json.homework_type homework.homework_type
if homework.homework_type == "practice" if homework.homework_type == "practice"
json.shixun_identifier homework.shixuns.take.try(:identifier) json.shixun_identifier homework.shixuns.take.try(:identifier)
json.shixun_id homework.shixuns.take.try(:id) json.shixun_id homework.shixuns.take.try(:id)
json.shixun_status homework.shixuns.take.try(:status).to_i
end end

@ -29,6 +29,7 @@ json.homeworks @homework_commons.each do |homework|
if @user_course_identity < Course::STUDENT if @user_course_identity < Course::STUDENT
if homework.homework_type == "practice" if homework.homework_type == "practice"
json.shixun_identifier homework.shixuns.take.try(:identifier) json.shixun_identifier homework.shixuns.take.try(:identifier)
json.shixun_status homework.shixuns.take.try(:status).to_i
end end
elsif @user_course_identity == Course::STUDENT elsif @user_course_identity == Course::STUDENT
if homework.homework_type == "practice" if homework.homework_type == "practice"

@ -13,6 +13,7 @@ json.array! shixuns do |shixun|
json.identifier shixun.identifier json.identifier shixun.identifier
json.name shixun.name json.name shixun.name
json.status shixun.status json.status shixun.status
json.is_jupyter shixun.is_jupyter?
json.power (current_user.shixun_permission(shixun)) # 现在首页只显示已发布的实训 json.power (current_user.shixun_permission(shixun)) # 现在首页只显示已发布的实训
# REDO: 局部缓存 # REDO: 局部缓存
json.tag_name @tag_name_map&.fetch(shixun.id, nil) || shixun.tag_repertoires.first.try(:name) json.tag_name @tag_name_map&.fetch(shixun.id, nil) || shixun.tag_repertoires.first.try(:name)

@ -14,6 +14,7 @@ json.stu_num shixun.myshixuns_count
json.experience shixun.all_score json.experience shixun.all_score
json.diffcult diff_to_s(shixun.trainee) json.diffcult diff_to_s(shixun.trainee)
json.score_info shixun.shixun_preference_info # todo: 这块可以改成只显示实训的平均分,不用每次都去取每种星的分数了。 json.score_info shixun.shixun_preference_info # todo: 这块可以改成只显示实训的平均分,不用每次都去取每种星的分数了。
json.is_jupyter shixun.is_jupyter
# 用于是否显示导航栏中的'背景知识' # 用于是否显示导航栏中的'背景知识'
json.propaedeutics shixun.propaedeutics.present? json.propaedeutics shixun.propaedeutics.present?

@ -0,0 +1,11 @@
json.data_sets do
json.array! @data_sets do |set|
json.id set.id
json.title set.title
json.author set.author.real_name
json.created_on set.created_on
json.filesize number_to_human_size(set.filesize)
json.file_path "#{@absolute_folder}/#{set.relative_path_filename}"
end
end
json.data_sets_count @data_count

@ -0,0 +1 @@
json.identifier @myshixun.identifier

@ -2,6 +2,7 @@ json.shixun do
json.status @shixun.status json.status @shixun.status
json.name @shixun.name json.name @shixun.name
json.description @shixun.description json.description @shixun.description
json.is_jupyter @shixun.is_jupyter
# 镜像大小类别 # 镜像大小类别
json.main_type @main_type json.main_type @main_type
@ -41,6 +42,9 @@ json.shixun do
json.(config, :cpu_limit, :lower_cpu_limit, :memory_limit, :request_limit, :mirror_repository_id) json.(config, :cpu_limit, :lower_cpu_limit, :memory_limit, :request_limit, :mirror_repository_id)
end end
end end
json.jupyter_url jupyter_url(@shixun)
end end

@ -7,3 +7,4 @@ json.status shixun.status
json.human_status shixun.human_status json.human_status shixun.human_status
json.challenges_count shixun.challenges_count json.challenges_count shixun.challenges_count
json.finished_challenges_count @finished_challenges_count_map&.fetch(shixun.id, 0) || shixun.finished_challenges_count(user) json.finished_challenges_count @finished_challenges_count_map&.fetch(shixun.id, 0) || shixun.finished_challenges_count(user)
json.is_jupyter shixun.is_jupyter

@ -1,4 +1,6 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
require_relative '../config/boot' require_relative '../config/boot'
require 'rake' require 'rake'
Rake.application.run Rake.application.run

@ -9,6 +9,8 @@ Rails.application.routes.draw do
get 'auth/qq/callback', to: 'oauth/qq#create' get 'auth/qq/callback', to: 'oauth/qq#create'
get 'auth/failure', to: 'oauth/base#auth_failure' get 'auth/failure', to: 'oauth/base#auth_failure'
resources :edu_settings resources :edu_settings
scope '/api' do scope '/api' do
get 'home/index' get 'home/index'
@ -23,6 +25,17 @@ Rails.application.routes.draw do
put 'commons/unhidden', to: 'commons#unhidden' put 'commons/unhidden', to: 'commons#unhidden'
delete 'commons/delete', to: 'commons#delete' delete 'commons/delete', to: 'commons#delete'
resources :jupyters do
collection do
get :save_with_tpi
get :save_with_tpm
get :get_info_with_tpi
get :get_info_with_tpm
get :reset_with_tpi
get :reset_with_tpm
end
end
resources :memos do resources :memos do
member do member do
post :sticky_or_cancel post :sticky_or_cancel
@ -35,6 +48,9 @@ Rails.application.routes.draw do
end end
end end
resources :hacks, path: :problems, param: :identifier do resources :hacks, path: :problems, param: :identifier do
collection do collection do
get :unpulished_list get :unpulished_list
@ -171,6 +187,7 @@ Rails.application.routes.draw do
post :html_content post :html_content
get :open_webssh get :open_webssh
get :challenges get :challenges
post :sync_code
end end
collection do collection do
get :sigle_mul_test get :sigle_mul_test
@ -206,6 +223,7 @@ Rails.application.routes.draw do
get :check_test_sets get :check_test_sets
get :unlock_choose_answer get :unlock_choose_answer
get :get_choose_answer get :get_choose_answer
get :jupyter
end end
collection do collection do
@ -264,6 +282,12 @@ Rails.application.routes.draw do
get :shixun_exec get :shixun_exec
post :review_shixun post :review_shixun
get :review_newest_record get :review_newest_record
post :update_permission_setting
post :update_learn_setting
get :get_data_sets
get :jupyter_exec
post :upload_data_sets
delete :destroy_data_sets
end end
resources :challenges do resources :challenges do
@ -748,7 +772,11 @@ Rails.application.routes.draw do
resources :poll_bank_questions resources :poll_bank_questions
resources :attachments resources :attachments do
collection do
delete :destroy_files
end
end
resources :schools do resources :schools do
member do member do

@ -0,0 +1,5 @@
class AddIsJupyterForShixuns < ActiveRecord::Migration[5.2]
def change
add_column :shixuns, :is_jupyter, :boolean, :default => false
end
end

@ -0,0 +1,17 @@
class AddIndexForShixunSecretRepositories < ActiveRecord::Migration[5.2]
def change
shixun_ids = ShixunSecretRepository.pluck(:shixun_id).uniq
shixuns = Shixun.where(id: shixun_ids)
shixuns.find_each do |shixun|
id = shixun.shixun_secret_repository.id
shixun_secret_repositories = ShixunSecretRepository.where(shixun_id: shixun.id).where.not(id: id)
shixun_secret_repositories.destroy_all
end
remove_index :shixun_secret_repositories, :shixun_id
add_index :shixun_secret_repositories, :shixun_id, unique: true
end
end

@ -0,0 +1,35 @@
version: '3'
services:
mysql:
image: mysql:5.7.17
command: --sql-mode=""
restart: always
volumes:
- ./mysql_data/:/var/lib/mysql
ports:
- "3306:3306"
environment:
MYSQL_ROOT_PASSWORD: 123456789
MYSQL_DATABASE: educoder
redis:
image: redis:3.2
container_name: redis
restart: always
ports:
- "6379:6379"
volumes:
- ./redis_data:/data
web:
image: guange/educoder:latest
command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 4000 -b '0.0.0.0'"
stdin_open: true
tty: true
volumes:
- .:/app
ports:
- "4000:4000"
depends_on:
- mysql
- redis

Binary file not shown.

@ -326,7 +326,7 @@ module.exports = {
comments: false comments: false
}, },
compress: { compress: {
drop_debugger: true, drop_debugger: false,
drop_console: false drop_console: false
} }
} }

File diff suppressed because one or more lines are too long

@ -316,6 +316,11 @@ const RecordDetail = Loadable({
loader: () => import('./modules/developer/recordDetail'), loader: () => import('./modules/developer/recordDetail'),
loading: Loading loading: Loading
}); });
// jupyter tpi
const JupyterTPI = Loadable({
loader: () => import('./modules/tpm/jupyter'),
loading: Loading
});
// //个人竞赛报名 // //个人竞赛报名
// const PersonalCompetit = Loadable({ // const PersonalCompetit = Loadable({
// loader: () => import('./modules/competition/personal/PersonalCompetit.js'), // loader: () => import('./modules/competition/personal/PersonalCompetit.js'),
@ -610,12 +615,21 @@ class App extends Component {
<Route path="/shixuns/new" component={Newshixuns}> <Route path="/shixuns/new" component={Newshixuns}>
</Route> </Route>
{/* jupyter */}
<Route path="/tasks/:identifier/jupyter/"
render={
(props) => {
return (<JupyterTPI {...this.props} {...props} {...this.state}/>)
}
}
/>
<Route path="/tasks/:stageId" component={IndexWrapperComponent}/> <Route path="/tasks/:stageId" component={IndexWrapperComponent}/>
<Route path="/shixuns/:shixunId" component={TPMIndexComponent}> <Route path="/shixuns/:shixunId" component={TPMIndexComponent}>
</Route> </Route>
{/*列表页*/} {/*列表页 实训项目列表*/}
<Route path="/shixuns" component={TPMShixunsIndexComponent}/> <Route path="/shixuns" component={TPMShixunsIndexComponent}/>

@ -13,7 +13,7 @@ function locationurl(list){
if (window.location.port === "3007") { if (window.location.port === "3007") {
} else { } else {
window.location.replace(list) window.location.href=list
} }
} }
let hashTimeout let hashTimeout
@ -33,9 +33,9 @@ if (isDev) {
// 超管 // 超管
// debugType="admin"; // debugType="admin";
// 老师 // 老师
// debugType="teacher"; //ebugType="teacher";
// 学生 // 学生
//debugType="student"; // debugType="student";
window._debugType = debugType; window._debugType = debugType;
export function initAxiosInterceptors(props) { export function initAxiosInterceptors(props) {
@ -52,6 +52,7 @@ export function initAxiosInterceptors(props) {
//proxy="http://47.96.87.25:48080" //proxy="http://47.96.87.25:48080"
proxy="https://pre-newweb.educoder.net" proxy="https://pre-newweb.educoder.net"
proxy="https://test-newweb.educoder.net" proxy="https://test-newweb.educoder.net"
proxy="https://test-jupyterweb.educoder.net"
//proxy="http://192.168.2.63:3001" //proxy="http://192.168.2.63:3001"
// 在这里使用requestMap控制避免用户通过双击等操作发出重复的请求 // 在这里使用requestMap控制避免用户通过双击等操作发出重复的请求
@ -88,7 +89,6 @@ export function initAxiosInterceptors(props) {
url = `${config.url}`; url = `${config.url}`;
} }
} }
if(`${config[0]}`!=`true`){ if(`${config[0]}`!=`true`){
if (window.location.port === "3007") { if (window.location.port === "3007") {
// if (url.indexOf('.json') == -1) { // if (url.indexOf('.json') == -1) {
@ -109,15 +109,15 @@ export function initAxiosInterceptors(props) {
} }
// //
// console.log(config); // console.log(config);
if (config.method === "post") { // if (config.method === "post") {
if (requestMap[config.url] === true) { // 避免重复的请求 导致页面f5刷新 也会被阻止 显示这个方法会影响到定制信息 // if (requestMap[config.url] === true) { // 避免重复的请求 导致页面f5刷新 也会被阻止 显示这个方法会影响到定制信息
// console.log(config); // // console.log(config);
// console.log(JSON.parse(config)); // // console.log(JSON.parse(config));
// console.log(config.url); // // console.log(config.url);
// console.log("被阻止了是重复请求================================="); // // console.log("被阻止了是重复请求=================================");
return false; // return false;
} // }
} // }
// 非file_update请求 // 非file_update请求
if (config.url.indexOf('update_file') === -1) { if (config.url.indexOf('update_file') === -1) {
requestMap[config.url] = true; requestMap[config.url] = true;

@ -37,6 +37,22 @@ export function getUrl(path, goTest) {
} }
return `${path ? path: ''}`; return `${path ? path: ''}`;
} }
export function getUrlmys(path, goTest) {
// https://www.educoder.net
// https://testbdweb.trustie.net
// 如果想所有url定位到测试版可以反注释掉下面这行
//goTest = true
// testbdweb.educoder.net testbdweb.trustie.net
// const local = goTest ? 'https://testeduplus2.educoder.net' : 'http://localhost:3000'
// const local = 'https://testeduplus2.educoder.net'
const local = 'https://test-jupyterweb.educoder.net'
if (isDev) {
return `${local}${path?path:''}`
}
return `${path ? path: ''}`;
}
export function getStaticUrl() { export function getStaticUrl() {
const local = TEST_HOST; const local = TEST_HOST;
if (isDev) { if (isDev) {
@ -55,6 +71,10 @@ export function getUrl2(path, goTest) {
export function getUploadActionUrl(path, goTest) { export function getUploadActionUrl(path, goTest) {
return `${getUrl()}/api/attachments.json${isDev ? `?debug=${window._debugType || 'admin'}` : ''}` return `${getUrl()}/api/attachments.json${isDev ? `?debug=${window._debugType || 'admin'}` : ''}`
} }
export function getUploadActionUrltwo(id) {
return `${getUrlmys()}/api/shixuns/${id}/upload_data_sets.json${isDev ? `?debug=${window._debugType || 'admin'}` : ''}`
}
export function getUploadActionUrlOfAuth(id) { export function getUploadActionUrlOfAuth(id) {
return `${getUrl()}/api/users/accounts/${id}/auth_attachment.json${isDev ? `?debug=${window._debugType || 'admin'}` : ''}` return `${getUrl()}/api/users/accounts/${id}/auth_attachment.json${isDev ? `?debug=${window._debugType || 'admin'}` : ''}`
} }

@ -0,0 +1,30 @@
/*
* @Description:
* @Author: tangjiang
* @Github:
* @Date: 2019-12-13 10:28:15
* @LastEditors: tangjiang
* @LastEditTime: 2019-12-13 10:37:17
*/
import { Modal } from 'antd';
export function ModalConfirm (
title,
content,
handleOk,
handleCancel
) {
Modal.confirm({
title,
content,
okText: '确定',
cancelText: '取消',
onOk () {
handleOk && handleOk();
},
onCancel () {
handleCancel && handleCancel();
}
});
}

@ -4,12 +4,12 @@
* @Github: * @Github:
* @Date: 2019-12-10 09:03:48 * @Date: 2019-12-10 09:03:48
* @LastEditors: tangjiang * @LastEditors: tangjiang
* @LastEditTime: 2019-12-10 09:05:41 * @LastEditTime: 2019-12-12 10:53:47
*/ */
import { Icon } from 'antd'; import { Icon } from 'antd';
const MyIcon = Icon.createFromIconfontCN({ const MyIcon = Icon.createFromIconfontCN({
scriptUrl: '//at.alicdn.com/t/font_1535266_ss6796i6f6j.js' scriptUrl: '//at.alicdn.com/t/font_1535266_i4ilpm93kp.js'
}); });
export default MyIcon; export default MyIcon;

@ -2,8 +2,8 @@
// export { default as OrderStateUtil } from '../routes/Order/components/OrderStateUtil'; // export { default as OrderStateUtil } from '../routes/Order/components/OrderStateUtil';
export { getImageUrl as getImageUrl, getUrl as getUrl, getUrl2 as getUrl2, setImagesUrl as setImagesUrl export { getImageUrl as getImageUrl, getUrl as getUrl, getUrlmys as getUrlmys, getUrl2 as getUrl2, setImagesUrl as setImagesUrl
, getUploadActionUrl as getUploadActionUrl, getUploadActionUrlOfAuth as getUploadActionUrlOfAuth , getUploadActionUrl as getUploadActionUrl,getUploadActionUrltwo as getUploadActionUrltwo , getUploadActionUrlOfAuth as getUploadActionUrlOfAuth
, getTaskUrlById as getTaskUrlById, TEST_HOST ,htmlEncode as htmlEncode } from './UrlTool'; , getTaskUrlById as getTaskUrlById, TEST_HOST ,htmlEncode as htmlEncode } from './UrlTool';
export { default as queryString } from './UrlTool2'; export { default as queryString } from './UrlTool2';

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

@ -205,15 +205,16 @@ class CommonWorkItem extends Component{
{item.uncommit_count===undefined?"":<span className="mr20 fl">{item.uncommit_count} 未交</span>} {item.uncommit_count===undefined?"":<span className="mr20 fl">{item.uncommit_count} 未交</span>}
{ {
item.status_time!="" && item.status_time!="" &&
<Tooltip placement="bottom" title={ item.status.indexOf('提交中') != -1 ? '提交剩余时间' :
item.status.indexOf('补交中') != -1 ? '补交剩余时间' :
item.status.indexOf('申诉中') != -1 ? '申诉剩余时间' :
item.status.indexOf('匿评中') != -1 ? '匿评剩余时间' :
item.status.indexOf('匿评申诉中') != -1 ? '匿评申诉剩余时间' : ''}>
<span className="mr20 fl">{item.status_time}</span> <span className="mr20 fl">{item.status_time}</span>
</Tooltip>
}
}
{/*<Tooltip placement="bottom" title={ item.status.indexOf('提交中') != -1 ? '提交剩余时间' :*/}
{/* item.status.indexOf('补交中') != -1 ? '补交剩余时间' :*/}
{/* item.status.indexOf('申诉中') != -1 ? '申诉剩余时间' :*/}
{/* item.status.indexOf('匿评中') != -1 ? '匿评剩余时间' :*/}
{/* item.status.indexOf('匿评申诉中') != -1 ? '匿评申诉剩余时间' : ''}>*/}
{/*</Tooltip>*/}
{isAdmin && <div className="fr"> {isAdmin && <div className="fr">
<WordsBtn style="blue" className="fl font-16 ml28" onClick={ () => { this.props.toEditPage(this.props.match.params, item.homework_id) }}>编辑</WordsBtn> <WordsBtn style="blue" className="fl font-16 ml28" onClick={ () => { this.props.toEditPage(this.props.match.params, item.homework_id) }}>编辑</WordsBtn>
<WordsBtn style="blue" className="fl font-16 ml28" onClick={ () => { this.props.toWorkSettingPage(this.props.match.params, item.homework_id) }}>设置</WordsBtn> <WordsBtn style="blue" className="fl font-16 ml28" onClick={ () => { this.props.toWorkSettingPage(this.props.match.params, item.homework_id) }}>设置</WordsBtn>

@ -493,8 +493,8 @@ class NewShixunModel extends Component{
{this.props.type==='shixuns'? {this.props.type==='shixuns'?
<div className="clearfix sortinxdirection mt30 intermediatecenterysls"> <div className="clearfix sortinxdirection mt30 intermediatecenterysls">
<p className="nandu">筛选</p> <p className="nandu">筛选</p>
<p className={belongtoindex===0?"clickbutstwo ml13":"clickbutstwos ml13"} onClick={()=>this.belongto("all")}>全部实训</p> <p className={type==="all"?"clickbutstwo ml13":"clickbutstwos ml13"} onClick={()=>this.belongto("all")}>全部实训</p>
<p className={belongtoindex===1?"clickbutstwo ml20":"clickbutstwos ml20"} onClick={()=>this.belongto("mine")}>普通实训</p> <p className={type==="mine"?"clickbutstwo ml20":"clickbutstwos ml20"} onClick={()=>this.belongto("mine")}>我的实训</p>
</div>:"" </div>:""
} }
{/*{this.props.type==='shixuns'? <Dropdown overlay={menus}>*/} {/*{this.props.type==='shixuns'? <Dropdown overlay={menus}>*/}
@ -581,11 +581,26 @@ class NewShixunModel extends Component{
className="fl task-hide edu-txt-left mt3" className="fl task-hide edu-txt-left mt3"
name="shixun_homework[]" name="shixun_homework[]"
></Checkbox> ></Checkbox>
<a target="_blank" href={this.props.type==='shixuns'?`/shixuns/${item.identifier}/challenges`:`/paths/${item.id}`} className="ml15 fl font-16 color-dark maxwidth1100" <a target="_blank" href={this.props.type==='shixuns'?`/shixuns/${item.identifier}/challenges`:`/paths/${item.id}`} className="ml15 fl font-16 color-dark maxwidth1100"
dangerouslySetInnerHTML={{__html: item.title}} dangerouslySetInnerHTML={{__html: item.title}}
> >
</a> </a>
{
this.props.type==='shixuns'?
(
item.is_jupyter===true?
<div className="myysljupyter fl ml20 intermediatecenter">
<p className="myysljupytertest">
Jupyter
</p>
</div>
:""
)
:""
}
<div className="cl"></div> <div className="cl"></div>
<style> <style>
{ {

@ -385,6 +385,28 @@
.newshixunmodels{ .newshixunmodels{
margin: 0 auto; margin: 0 auto;
} }
.myysljupyter{
width:54px;
height:24px;
text-align: center;
border-radius:5px;
border:1px solid #FF6802;
}
.myysljupytertest{
width:54px;
height:16px;
line-height:16px;
font-size:12px;
color:#FF6802;
line-height:16px;
}
.intermediatecenter{
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
/* 中间居中 */ /* 中间居中 */
.intermediatecenter{ .intermediatecenter{

@ -529,6 +529,7 @@ class YslDetailCards extends Component{
{ {
this.props.isAdmin()===true? this.props.isAdmin()===true?
<DetailCardsEditAndEdit <DetailCardsEditAndEdit
{...this.props}
idsum={idsum} idsum={idsum}
keys={key} keys={key}
pathCardsedittype={pathCardsedittype} pathCardsedittype={pathCardsedittype}

@ -232,9 +232,9 @@ class ShixunHomeworkPage extends Component {
typelist={teacherdatapage === undefined ? [""] : teacherdatapage.homework_status} typelist={teacherdatapage === undefined ? [""] : teacherdatapage.homework_status}
/> />
<a className="color-grey-9 fr font-16 summaryname ml20 mr20" onClick={()=>this.gotohome()}>返回</a> <a className="color-grey-9 fr font-16 summaryname ml20 mr20" onClick={()=>this.gotohome()}>返回</a>
<a className="color-grey-9 fr font-16 mr20" {teacherdatapage&&teacherdatapage.shixun_status>1?<a className="color-grey-9 fr font-16 mr20"
href={`/shixuns/${teacherdatapage === undefined ? "" : teacherdatapage.shixun_identifier}/challenges`} href={`/shixuns/${teacherdatapage === undefined ? "" : teacherdatapage.shixun_identifier}/challenges`}
target={"_blank"}>实训详情</a> target={"_blank"}>实训详情</a>:""}
</div> </div>
@ -336,10 +336,10 @@ class ShixunHomeworkPage extends Component {
<a className="fr color-blue font-16" <a className="fr color-blue font-16"
href={`/courses/${this.props.match.params.coursesId}/${jobsettingsdatapage === undefined ? "" : jobsettingsdatapage.data.category.main === 1 ? "shixun_homeworks" :"shixun_homework"}/${teacherdatapage === undefined ? "" : teacherdatapage.id}/commitsummary/${this.props.match.params.homeworkid}`}>{teacherdatapage.commit_des}</a> href={`/courses/${this.props.match.params.coursesId}/${jobsettingsdatapage === undefined ? "" : jobsettingsdatapage.data.category.main === 1 ? "shixun_homeworks" :"shixun_homework"}/${teacherdatapage === undefined ? "" : teacherdatapage.id}/commitsummary/${this.props.match.params.homeworkid}`}>{teacherdatapage.commit_des}</a>
} }
{teacherdatapage === undefined ? "" : <Startshixuntask {teacherdatapage === undefined ? "" :teacherdatapage&&teacherdatapage.shixun_status>1?<Startshixuntask
{...this.props} {...this.props}
data={teacherdatapage} data={teacherdatapage}
/>} />:""}
{this.props.isStudent() ? {this.props.isStudent() ?
( (
teacherdatapage&&teacherdatapage.redo_work===true? teacherdatapage&&teacherdatapage.redo_work===true?

@ -315,6 +315,7 @@ class ShixunWorkReport extends Component {
// let showAppraiseModals=this.props&&this.props.isAdminOrTeacher()===true?work_comment===null||work_comment===undefined?false:true:work_comment===null||work_comment===undefined?false:true; // let showAppraiseModals=this.props&&this.props.isAdminOrTeacher()===true?work_comment===null||work_comment===undefined?false:true:work_comment===null||work_comment===undefined?false:true;
let showAppraiseModals=work_comment===null||work_comment===undefined?false:true; let showAppraiseModals=work_comment===null||work_comment===undefined?false:true;
document.title=data&&data.course_name; document.title=data&&data.course_name;
return ( return (
data===undefined?"":<Spin indicator={antIcon} spinning={this.state.spinning}> data===undefined?"":<Spin indicator={antIcon} spinning={this.state.spinning}>
@ -366,10 +367,15 @@ class ShixunWorkReport extends Component {
<p className=" fl color-black mt25 summaryname">{data&&data.shixun_name}</p> <p className=" fl color-black mt25 summaryname">{data&&data.shixun_name}</p>
{/*{this.props.isAdmin()?<a className=" fr font-14 ml30 mt10 mr20 color-grey-9 ">导出实训报告数据</a>:""}*/} {/*{this.props.isAdmin()?<a className=" fr font-14 ml30 mt10 mr20 color-grey-9 ">导出实训报告数据</a>:""}*/}
<a onClick={this.goback} className="color-grey-6 fr font-14 ml20 mt15">返回</a> <a onClick={this.goback} className="color-grey-6 fr font-14 ml20 mt15">返回</a>
{this.props.isAdmin() ? <a {this.props.isAdmin() ?<a
className=" color-blue font-14 fr ml20 mt15" className=" color-blue font-14 fr ml20 mt15"
onClick={()=>this.confirmysl(`/student_works/${homeworkid}/export_shixun_work_report.pdf`)} onClick={()=>this.confirmysl(`/student_works/${homeworkid}/export_shixun_work_report.pdf`)}
> <Spin size="small" spinning={this.state.isspinning}>导出实训报告数据</Spin></a> : ""} > <Spin size="small" spinning={this.state.isspinning}>导出实训报告数据</Spin></a>:
parseInt(this.props&&this.props.user.user_id)===parseInt(data&&data.user_id)?<a
className=" color-blue font-14 fr ml20 mt15"
onClick={()=>this.confirmysl(`/student_works/${homeworkid}/export_shixun_work_report.pdf`)}
> <Spin size="small" spinning={this.state.isspinning}>导出实训报告数据</Spin></a>:""
}
{/*{this.props.isAdmin() ?work_comment_hidden===true? "":<a*/} {/*{this.props.isAdmin() ?work_comment_hidden===true? "":<a*/}
{/*className=" color-blue font-14 fr ml20 mt15"*/} {/*className=" color-blue font-14 fr ml20 mt15"*/}
{/*onClick={()=>this.showAppraiseModal(1)}*/} {/*onClick={()=>this.showAppraiseModal(1)}*/}

@ -355,18 +355,22 @@ class ShixunhomeWorkItem extends Component{
.homepagePostSettingbox{ .homepagePostSettingbox{
width:139px !important; width:139px !important;
} }
.colorfff{
color:#fff !important;
}
` `
} }
</style> </style>
{this.props.isAdmin?<span onClick={(event)=>this.stopPro(event)} className={this.props.isAdminOrCreator()?"homepagePostSetting homepagePostSettingname":"homepagePostSetting homepagePostSettingbox"} style={{"right":"-2px","top":"6px","display":"block"}}> {this.props.isAdmin?<span onClick={(event)=>this.stopPro(event)} className={this.props.isAdminOrCreator()?"homepagePostSetting homepagePostSettingname":"homepagePostSetting homepagePostSettingbox"} style={{"right":"-2px","top":"6px","display":"block"}}>
<Link className="btn colorblue font-16 fontweight400" to={"/shixuns/"+discussMessage.shixun_identifier+"/challenges"} target={"_blank"}>实训详情</Link> {discussMessage&&discussMessage.shixun_status>1?<Link className="btn colorblue font-16 fontweight400" to={"/shixuns/"+discussMessage.shixun_identifier+"/challenges"} target={"_blank"}>实训详情</Link>:
<a className={"btn colorfff font-16 fontweight400"}>实训详情</a>}
{this.props.isAdminOrCreator()?<a onClick={(event)=>this.editname(discussMessage.name,discussMessage.homework_id,event)} className={"btn colorblue ml20 font-16 fontweight400"}>重命名</a>:""} {this.props.isAdminOrCreator()?<a onClick={(event)=>this.editname(discussMessage.name,discussMessage.homework_id,event)} className={"btn colorblue ml20 font-16 fontweight400"}>重命名</a>:""}
{/*<WordsBtn className="btn colorblue ml20 font-16" to={`/courses/${this.props.match.params.coursesId}/${this.state.shixuntypes}/${discussMessage.homework_id}/settings?tab=3`} > 设置</WordsBtn>*/} {/*<WordsBtn className="btn colorblue ml20 font-16" to={`/courses/${this.props.match.params.coursesId}/${this.state.shixuntypes}/${discussMessage.homework_id}/settings?tab=3`} > 设置</WordsBtn>*/}
<WordsBtn className="btn colorblue font-16 ml15 fontweight400" to={`/courses/${this.props.match.params.coursesId}/${this.state.shixuntypes}/${discussMessage.homework_id}/settings?tab=3`} > 设置</WordsBtn> <WordsBtn className="btn colorblue font-16 ml15 fontweight400" to={`/courses/${this.props.match.params.coursesId}/${this.state.shixuntypes}/${discussMessage.homework_id}/settings?tab=3`} > 设置</WordsBtn>
</span>:""} </span>:""}
{this.props.isStudent===true?this.props.course_identity===5? {this.props.isStudent===true?this.props.course_identity===5?discussMessage&&discussMessage.shixun_status>1?
<WordsBtn style="blue" className="colorblue font-16 mr20 fr mt10"> <WordsBtn style="blue" className="colorblue font-16 mr20 fr mt10">
{startbtn===false? {startbtn===false?
(discussMessage.task_operation[0] == '继续挑战' || discussMessage.task_operation[0] == '查看实战' ? (discussMessage.task_operation[0] == '继续挑战' || discussMessage.task_operation[0] == '查看实战' ?
@ -377,7 +381,7 @@ class ShixunhomeWorkItem extends Component{
<a className="btn colorblue" onClick={()=>this.taskoperationId(discussMessage.task_operation[1])}> <a className="btn colorblue" onClick={()=>this.taskoperationId(discussMessage.task_operation[1])}>
{discussMessage.task_operation[0]} {discussMessage.task_operation[0]}
</a>):<a className="btn colorblue" ></a>} </a>):<a className="btn colorblue" ></a>}
</WordsBtn>:"":"" </WordsBtn>:"":"":""
} }
</h6> </h6>
@ -393,21 +397,13 @@ class ShixunhomeWorkItem extends Component{
{ {
discussMessage.time_status===1? discussMessage.time_status===1?
<Tooltip title={"提交剩余时间"} placement="bottom">
<span className="mr15 color-grey9">{discussMessage.status_time}</span> <span className="mr15 color-grey9">{discussMessage.status_time}</span>
</Tooltip>
:discussMessage.time_status===2? :discussMessage.time_status===2?
<Tooltip title={"补交剩余时间"} placement="bottom">
<span className="mr15 color-grey9">{discussMessage.status_time}</span> <span className="mr15 color-grey9">{discussMessage.status_time}</span>
</Tooltip>
:discussMessage.time_status===3? :discussMessage.time_status===3?
<Tooltip title={"匿评剩余时间"} placement="bottom">
<span className="mr15 color-grey9">{discussMessage.status_time}</span> <span className="mr15 color-grey9">{discussMessage.status_time}</span>
</Tooltip>
:discussMessage.time_status===4? :discussMessage.time_status===4?
<Tooltip title={"申诉剩余时间"} placement="bottom">
<span className="mr15 color-grey9">{discussMessage.status_time}</span> <span className="mr15 color-grey9">{discussMessage.status_time}</span>
</Tooltip>
: :
<span className="mr15 color-grey9">{discussMessage.status_time}</span> <span className="mr15 color-grey9">{discussMessage.status_time}</span>
} }

@ -44,6 +44,13 @@ const maps = {
'value': '2' 'value': '2'
} }
], ],
'languageMenu': [
{
'key': 'c',
'name': 'C语言',
'value': 'c'
}
],
'difficultMenu': [ 'difficultMenu': [
{ {
'key': '1', 'key': '1',
@ -124,10 +131,9 @@ class DeveloperHome extends React.PureComponent {
type="danger" type="danger"
icon="close" icon="close"
size="small" size="small"
style={{ marginLeft: '10px' }} style={{ marginLeft: '10px', display: record.open_or_not ? 'none' : 'inline-block' }}
onClick={() => this.handleClickDelete(record)} onClick={() => this.handleClickDelete(record)}
> >
{/* <Link to={`/problems/${record.identifier}/edit`}></Link> */}
</Button> </Button>
</React.Fragment> </React.Fragment>
), ),
@ -197,7 +203,6 @@ class DeveloperHome extends React.PureComponent {
componentDidMount() { componentDidMount() {
// 是否是我的,如果是我的 显示编辑按钮 // 是否是我的,如果是我的 显示编辑按钮
const { isMySource } = this.props; const { isMySource } = this.props;
console.log(this.props);
if (isMySource) { if (isMySource) {
this.handleFilterSearch({come_from: 'mine'}); this.handleFilterSearch({come_from: 'mine'});
let _columns = this.columns.concat([this.options]); let _columns = this.columns.concat([this.options]);
@ -346,6 +351,14 @@ class DeveloperHome extends React.PureComponent {
}); });
this.handleFilterSearch({category: +item.key === 0 ? '' : +item.key}); this.handleFilterSearch({category: +item.key === 0 ? '' : +item.key});
} }
// 下拉语言
handleLanguageMenuClick = (item) => {
this.addShowFilterCtx({
type: 'language',
key: item.key
});
this.handleFilterSearch({language: item.key})
}
// 难度下拉 // 难度下拉
handleHardMenuClick = (item) => { handleHardMenuClick = (item) => {
this.addShowFilterCtx({ this.addShowFilterCtx({
@ -421,6 +434,7 @@ class DeveloperHome extends React.PureComponent {
// const { testReducer, handleClick } = this.props; // const { testReducer, handleClick } = this.props;
const { const {
ojListReducer: {hacks_list, top_data, hacks_count}, ojListReducer: {hacks_list, top_data, hacks_count},
user,
pagination pagination
} = this.props; } = this.props;
const {passed_count = 0, simple_count = 0, medium_count = 0, diff_count = 0} = top_data; const {passed_count = 0, simple_count = 0, medium_count = 0, diff_count = 0} = top_data;
@ -443,7 +457,11 @@ class DeveloperHome extends React.PureComponent {
>{ctx}</Tag> >{ctx}</Tag>
)}); )});
}; };
// console.log('=====>>>>>>>>>.', this.props);
const newBtnStyle = user && (user.admin || (user.is_teacher && user.professional_certification) || user.business)
? { display: 'block'}
: { display: 'none'};
return ( return (
<div className="developer-list"> <div className="developer-list">
<div className="ant-spin-container"> <div className="ant-spin-container">
@ -457,7 +475,8 @@ class DeveloperHome extends React.PureComponent {
<MultipTags type="warning" text="中等" numb={medium_count} style={{ marginRight: '20px' }}/> <MultipTags type="warning" text="中等" numb={medium_count} style={{ marginRight: '20px' }}/>
<MultipTags type="error" text="困难" numb={diff_count}/> <MultipTags type="error" text="困难" numb={diff_count}/>
</div> </div>
<Button type="primary" onClick={this.handleClickNew}>新建 {/* 认证的老师, 超级管理员, 运营人员可见 */}
<Button style={ newBtnStyle } type="primary" onClick={this.handleClickNew}>新建
{/* <Link to="/problems/new">新建</Link> */} {/* <Link to="/problems/new">新建</Link> */}
</Button> </Button>
</div> </div>
@ -468,6 +487,9 @@ class DeveloperHome extends React.PureComponent {
<Dropdown className={'dropdonw-style'} placement="bottomLeft" overlay={this.getMenuItems('categoryMenu', this.handleCategoryMenuClick)}> <Dropdown className={'dropdonw-style'} placement="bottomLeft" overlay={this.getMenuItems('categoryMenu', this.handleCategoryMenuClick)}>
<span className={'dropdown-span'}>分类 <Icon type="down"/></span> <span className={'dropdown-span'}>分类 <Icon type="down"/></span>
</Dropdown> </Dropdown>
<Dropdown className={'dropdonw-style'} placement="bottomLeft" overlay={this.getMenuItems('languageMenu', this.handleLanguageMenuClick)}>
<span className={'dropdown-span'}>语言 <Icon type="down"/></span>
</Dropdown>
<Dropdown className={'dropdonw-style'} placement="bottomLeft" overlay={this.getMenuItems('difficultMenu', this.handleHardMenuClick)}> <Dropdown className={'dropdonw-style'} placement="bottomLeft" overlay={this.getMenuItems('difficultMenu', this.handleHardMenuClick)}>
<span className={'dropdown-span'}>难度 <Icon type="down"/></span> <span className={'dropdown-span'}>难度 <Icon type="down"/></span>
</Dropdown> </Dropdown>

@ -4,7 +4,7 @@
* @Github: * @Github:
* @Date: 2019-11-27 16:02:36 * @Date: 2019-11-27 16:02:36
* @LastEditors: tangjiang * @LastEditors: tangjiang
* @LastEditTime: 2019-12-10 09:30:27 * @LastEditTime: 2019-12-13 17:32:33
*/ */
import './index.scss'; import './index.scss';
import React, { useState, useRef } from 'react'; import React, { useState, useRef } from 'react';
@ -84,7 +84,7 @@ const ControlSetting = (props) => {
<Tabs <Tabs
className={classNames} className={classNames}
activeKey={defaultActiveKey} activeKey={defaultActiveKey}
tabBarStyle={{ backgroundColor: '#000', color: '#fff' }} tabBarStyle={{ backgroundColor: 'rgba(48,48,48,1)', color: '#fff' }}
onChange={handleTabChange} onChange={handleTabChange}
> >
<TabPane tab={'自定义测试用例'} key={'1'} style={{ height: '280px', overflowY: 'auto' }}> <TabPane tab={'自定义测试用例'} key={'1'} style={{ height: '280px', overflowY: 'auto' }}>

@ -2,6 +2,7 @@
position: absolute; position: absolute;
bottom: 0; bottom: 0;
width: 100%; width: 100%;
background:rgba(30,30,30,1);
// height: 56px; // height: 56px;
.control_tab{ .control_tab{
position: absolute; position: absolute;
@ -51,7 +52,8 @@
height: 56px; height: 56px;
padding-right: 30px; padding-right: 30px;
padding-left: 10px; padding-left: 10px;
background: #000; // background: #000;
background:rgba(48,48,48,1);
} }
.setting_drawer{ .setting_drawer{

@ -4,7 +4,7 @@
* @Github: * @Github:
* @Date: 2019-11-27 19:46:14 * @Date: 2019-11-27 19:46:14
* @LastEditors: tangjiang * @LastEditors: tangjiang
* @LastEditTime: 2019-12-10 09:31:00 * @LastEditTime: 2019-12-13 17:38:42
*/ */
import './index.scss'; import './index.scss';
import React, { useState, useEffect, useRef, useImperativeHandle, forwardRef } from 'react'; import React, { useState, useEffect, useRef, useImperativeHandle, forwardRef } from 'react';
@ -50,6 +50,7 @@ function InitTabCtx (props, ref) {
], ],
initialValue: inputValue initialValue: inputValue
})(<TextArea })(<TextArea
className="input_textarea_style"
rows={8} rows={8}
placeholder="请填写测试用例的输入值,点击“调试代码”" placeholder="请填写测试用例的输入值,点击“调试代码”"
/>) />)

@ -47,4 +47,11 @@
.flex_r{ .flex_r{
padding: 0 20px 0 10px; padding: 0 20px 0 10px;
} }
.input_textarea_style{
background:rgba(30,30,30,1) !important;
color: #fff;
border-color: transparent;
outline: none;
}
} }

@ -4,11 +4,11 @@
* @Github: * @Github:
* @Date: 2019-11-27 15:02:52 * @Date: 2019-11-27 15:02:52
* @LastEditors: tangjiang * @LastEditors: tangjiang
* @LastEditTime: 2019-12-10 09:20:42 * @LastEditTime: 2019-12-13 16:16:56
*/ */
import './index.scss'; import './index.scss';
import React, { useState, useRef, useEffect } from 'react'; import React, { useState, useRef, useEffect } from 'react';
import { Icon, Drawer, Modal } from 'antd'; import { Drawer, Modal } from 'antd';
import { fromStore } from 'educoder'; import { fromStore } from 'educoder';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import MonacoEditor from '@monaco-editor/react'; import MonacoEditor from '@monaco-editor/react';

@ -1,11 +1,13 @@
.monaco_editor_area{ .monaco_editor_area{
height: 100%; height: 100%;
background-color: rgba(30,30,30,1);
.code_title{ .code_title{
display: flex; display: flex;
align-items: center; align-items: center;
// justify-content: space-between; // justify-content: space-between;
// background: #000; // background: #000;
background: #333333; // background: #333333;
background-color: rgba(48,48,48,1);
color: #fff; color: #fff;
height: 56px; height: 56px;
padding: 0 30px; padding: 0 30px;

@ -14,7 +14,7 @@ function UserInfo (props) {
const {image_url, name} = props.userInfo; const {image_url, name} = props.userInfo;
return ( return (
<div className={'avator_nicker'}> <div className={'avator_nicker'}>
<img alt="用户头像" className={'student_img'} src={getImageUrl(`images/${image_url}` || 'images/educoder/headNavLogo.png?1526520218')} /> <img style={{ display: image_url ? 'inline-block' : 'none'}} alt="用户头像" className={'student_img'} src={getImageUrl(`images/${image_url}` || 'images/educoder/headNavLogo.png?1526520218')} />
<span className={'student_nicker'}> <span className={'student_nicker'}>
{name || ''} {name || ''}
</span> </span>

@ -1,11 +1,14 @@
.banner-wrap{ .banner-wrap{
width: 100%; width: 100%;
height: 300px; height: 300px;
background-image: url(/static/media/path.e39ba7de.png); // background-image: url(/static/media/path.e39ba7de.png);
background-color: #000a4f; // background: #000a4f url(../../images/oj//oj_banner.jpg) none center;
/* background-size: cover; */ // background-color: #000a4f;
background-position: center; // /* background-size: cover; */
background-repeat: no-repeat; // background-position: center;
// background-repeat: no-repeat;
background: rgb(0, 1, 35) url(../../images/oj/oj_banner.jpg) no-repeat center;
background-size: cover;
} }
.developer-list{ .developer-list{

@ -9,7 +9,7 @@ import './index.scss';
import React, { useEffect } from 'react'; import React, { useEffect } from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import SplitPane from 'react-split-pane';// import { Form } from 'antd'; import SplitPane from 'react-split-pane';// import { Form } from 'antd';
import { Button } from 'antd'; import { Button, Modal } from 'antd';
import LeftPane from './leftpane'; import LeftPane from './leftpane';
import RightPane from './rightpane'; import RightPane from './rightpane';
import { withRouter } from 'react-router'; import { withRouter } from 'react-router';
@ -17,6 +17,7 @@ import { toStore } from 'educoder';
import UserInfo from '../components/userInfo'; import UserInfo from '../components/userInfo';
// import RightPane from './rightpane/index'; // import RightPane from './rightpane/index';
import actions from '../../../redux/actions'; import actions from '../../../redux/actions';
import {ModalConfirm} from '../../../common/components/ModalConfirm';
const NewOrEditTask = (props) => { const NewOrEditTask = (props) => {
const { const {
@ -32,17 +33,18 @@ const NewOrEditTask = (props) => {
changePublishLoadingStatus, changePublishLoadingStatus,
startProgramQuestion, startProgramQuestion,
getUserInfoForNew, getUserInfoForNew,
handleCancelPublish,
// updateTestAndValidate, // updateTestAndValidate,
} = props; } = props;
// 表单提交 // 表单提交
const handleSubmitForm = () => { const handleSubmitForm = () => {
// 改变loading状态
changeSubmitLoadingStatus(true);
// 调用输入表单验证功能 // 调用输入表单验证功能
if (props.identifier) { if (props.identifier) {
props.handleUpdateOjForm(props); props.handleUpdateOjForm(props);
} else { } else {
// 改变loading状态
changeSubmitLoadingStatus(true);
props.handleFormSubmit(props); // 提交表单 props.handleFormSubmit(props); // 提交表单
} }
}; };
@ -66,6 +68,9 @@ const NewOrEditTask = (props) => {
}, []); }, []);
// 模拟挑战 // 模拟挑战
const imitationChallenge = () => {
}
// 开始挑战
const startChallenge = () => { const startChallenge = () => {
// 调用 start 接口, 成功后跳转到模拟页面 // 调用 start 接口, 成功后跳转到模拟页面
startProgramQuestion(identifier, props); startProgramQuestion(identifier, props);
@ -84,9 +89,20 @@ const NewOrEditTask = (props) => {
// 发布 // 发布
const handleClickPublish = () => { const handleClickPublish = () => {
// console.log('public has click'); ModalConfirm('提示', (<p>发布后即可应用到自己管理的课堂<br /> 是否确认发布?</p>), () => {
changePublishLoadingStatus(true); changePublishLoadingStatus(true);
handlePublish(props, 'publish'); handlePublish(props, 'publish');
});
}
// 撤销发布
const handleClickCancelPublish = () => {
ModalConfirm('提示', (<p>是否确认撤销发布?</p>), () => {
changePublishLoadingStatus(true);
handleCancelPublish(props, identifier);
});
} }
// 取消保存/取消按钮 // 取消保存/取消按钮
@ -107,11 +123,34 @@ const NewOrEditTask = (props) => {
} }
// 发布/模拟挑战 // 发布/模拟挑战
const renderPubOrFight = () => { const renderPubOrFight = () => {
const pubButton = isPublish ? '' : (<Button const pubButton = isPublish
? (<Button
type="primary"
loading={publishLoading}
onClick={handleClickCancelPublish}
>撤销发布</Button>)
: (<Button
type="primary" type="primary"
loading={publishLoading} loading={publishLoading}
onClick={handleClickPublish} onClick={handleClickPublish}
>立即发布</Button>); >立即发布</Button>);
// 未发布: 模拟挑战 已发布: 开始挑战
const challengeBtn = isPublish ? (
<Button type="primary" onClick={startChallenge}>开始挑战</Button>
) : (
<Button type="primary" onClick={imitationChallenge}>模拟挑战</Button>
);
// 更新
// const updateBtn = isPublish
// ? ''
// : (
// <Button
// type="primary"
// loading={submitLoading}
// onClick={handleSubmitForm}
// >更新</Button>
// );
return ( return (
<React.Fragment> <React.Fragment>
<Button <Button
@ -120,7 +159,7 @@ const NewOrEditTask = (props) => {
onClick={handleSubmitForm} onClick={handleSubmitForm}
>更新</Button> >更新</Button>
{pubButton} {pubButton}
<Button type="primary" onClick={startChallenge}>模拟挑战</Button> {challengeBtn}
</React.Fragment> </React.Fragment>
) )
} }
@ -142,24 +181,9 @@ const NewOrEditTask = (props) => {
return ( return (
<div className={'new_add_task_wrap'}> <div className={'new_add_task_wrap'}>
<div className={'task_header'}> <div className={'task_header'}>
{/* <Link to="/problems" className={'header_btn'} >
<Icon type="left" style={{ marginRight: '5px'}}/>后退
</Link> */}
<UserInfo userInfo={userInfo}/> <UserInfo userInfo={userInfo}/>
<p className={'header_title'}>{props.name || ''}</p> <p className={'header_title'}>{props.name || ''}</p>
{ renderQuit() } { renderQuit() }
{/* <Link style={{
position: 'absolute',
right: '30px',
top: 0,
color: '#5091FF'
}} to="/problems">退出</Link> */}
{/* <Button
style={{ display: identifier ? 'none' : 'block'}}
loading={publishLoading}
className={`header_btn`}
type="primary"
onClick={handleClickPublish}>立即发布</Button> */}
</div> </div>
<div className="split-pane-area"> <div className="split-pane-area">
<SplitPane split="vertical" minSize={350} maxSize={-350} defaultSize="40%"> <SplitPane split="vertical" minSize={350} maxSize={-350} defaultSize="40%">
@ -206,6 +230,8 @@ const mapDispatchToProps = (dispatch) => ({
handleFormSubmit: (props) => dispatch(actions.validateOjForm(props)), handleFormSubmit: (props) => dispatch(actions.validateOjForm(props)),
// 发布表单 // 发布表单
handlePublish: (props, type) => dispatch(actions.validateOjForm(props, type)), handlePublish: (props, type) => dispatch(actions.validateOjForm(props, type)),
// 撤销发布
handleCancelPublish: (props, identifier) => dispatch(actions.handleClickCancelPublish(props, identifier)),
// 更新OJForm // 更新OJForm
handleUpdateOjForm: (props) => dispatch(actions.validateOjForm(props)), handleUpdateOjForm: (props) => dispatch(actions.validateOjForm(props)),
// 根据id号获取表单信息 // 根据id号获取表单信息

@ -4,7 +4,7 @@
* @Github: * @Github:
* @Date: 2019-11-21 09:19:38 * @Date: 2019-11-21 09:19:38
* @LastEditors: tangjiang * @LastEditors: tangjiang
* @LastEditTime: 2019-12-10 19:37:35 * @LastEditTime: 2019-12-13 11:58:46
*/ */
import './index.scss'; import './index.scss';
import React, { useState } from 'react'; import React, { useState } from 'react';
@ -16,7 +16,7 @@ const { TextArea } = Input;
const FormItem = Form.Item; const FormItem = Form.Item;
const AddTestDemo = (props) => { const AddTestDemo = (props) => {
const { const {
// key, key,
onSubmitTest, onSubmitTest,
onDeleteTest, onDeleteTest,
testCase, testCase,
@ -138,7 +138,7 @@ const AddTestDemo = (props) => {
return ( return (
<Collapse className={'collapse_area'} activeKey={isOpen?'1':''} onChange={() => handleChangeCollapse()}> <Collapse className={'collapse_area'} activeKey={isOpen?'1':''} onChange={() => handleChangeCollapse()}>
<Panel header={`测试用例${testCase.position}`} extra={genExtra()} key="1"> <Panel header={`测试用例${props.index + 1}`} extra={genExtra()} key="1">
<Form> <Form>
<FormItem <FormItem
label={<span className={'label_text'}>输入</span>} label={<span className={'label_text'}>输入</span>}

@ -4,7 +4,7 @@
* @Github: * @Github:
* @Date: 2019-11-20 10:35:40 * @Date: 2019-11-20 10:35:40
* @LastEditors: tangjiang * @LastEditors: tangjiang
* @LastEditTime: 2019-12-09 10:22:03 * @LastEditTime: 2019-12-13 11:39:52
*/ */
import 'quill/dist/quill.core.css'; import 'quill/dist/quill.core.css';
import 'quill/dist/quill.bubble.css'; import 'quill/dist/quill.bubble.css';
@ -185,6 +185,7 @@ class EditTab extends React.Component {
}; };
const renderTestCase = () => { const renderTestCase = () => {
return this.props.testCases.map((item, i) => { return this.props.testCases.map((item, i) => {
console.log(111);
return <AddTestDemo return <AddTestDemo
key={`${i}`} key={`${i}`}
isOpen={openTestCodeIndex.includes(i)} isOpen={openTestCodeIndex.includes(i)}

@ -4,10 +4,10 @@
* @Github: * @Github:
* @Date: 2019-11-23 10:53:19 * @Date: 2019-11-23 10:53:19
* @LastEditors: tangjiang * @LastEditors: tangjiang
* @LastEditTime: 2019-12-10 19:16:18 * @LastEditTime: 2019-12-13 17:19:15
*/ */
import './index.scss'; import './index.scss';
import React, { useEffect } from 'react'; import React, { useEffect, useState } from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import SplitPane from 'react-split-pane'; import SplitPane from 'react-split-pane';
import LeftPane from './leftpane'; import LeftPane from './leftpane';
@ -15,19 +15,23 @@ import RightPane from './rightpane';
// import { Link } from 'react-router-dom'; // import { Link } from 'react-router-dom';
// import { getImageUrl } from 'educoder' // import { getImageUrl } from 'educoder'
// import RightPane from '../newOrEditTask/rightpane'; // import RightPane from '../newOrEditTask/rightpane';
import { Icon } from 'antd'; import { Icon, Modal } from 'antd';
import UserInfo from '../components/userInfo'; import UserInfo from '../components/userInfo';
import actions from '../../../redux/actions'; import actions from '../../../redux/actions';
import { fromStore} from 'educoder'; import { fromStore} from 'educoder';
import { withRouter } from 'react-router'; import { withRouter } from 'react-router';
const StudentStudy = (props) => { function StudentStudy (props) {
const [hasUpdate, setHasUpdate] = useState(true);
const { const {
// hack,
userInfo, userInfo,
hack_identifier hack_identifier,
// user_program_identifier,
restoreInitialCode
} = props; } = props;
useEffect(() => {
const { const {
match: { params }, match: { params },
getUserProgramDetail, getUserProgramDetail,
@ -35,13 +39,35 @@ const StudentStudy = (props) => {
} = props; } = props;
let { id } = params; let { id } = params;
// console.log(id);
useEffect(() => {
// 保存当前的id // 保存当前的id
saveUserProgramIdentifier(id); saveUserProgramIdentifier(id);
// startProgramQuestion(id); // startProgramQuestion(id);
getUserProgramDetail(id); getUserProgramDetail(id);
}, []); }, []);
useEffect(() => {
const { hack = {} } = props;
if (hack.modify_code && hasUpdate) { // 代码更改,提示是否需要更新代码
setHasUpdate(false);
Modal.confirm({
title: '提示',
content: (
<p>
代码文件有更新啦 <br />
还未提交的代码请自行保存
</p>
),
okText: '立即更新',
cancelText: '稍后再说',
onOk () {
restoreInitialCode(id, '更新成功');
}
});
}
}, [props, hasUpdate, setHasUpdate]);
const _hack_id = hack_identifier || fromStore('hack_identifier'); const _hack_id = hack_identifier || fromStore('hack_identifier');
// 处理编辑 // 处理编辑
@ -66,11 +92,11 @@ const StudentStudy = (props) => {
</div> */} </div> */}
<UserInfo userInfo={userInfo}/> <UserInfo userInfo={userInfo}/>
<div className={'study_name'}> <div className={'study_name'}>
<span>乘积最大序列</span> <span>乘积最大序列 {hasUpdate}</span>
</div> </div>
<div className={'study_quit'}> <div className={'study_quit'}>
{/* to={`/problems/${_hack_id}/edit`} */} {/* to={`/problems/${_hack_id}/edit`} */}
<span onClick={handleClickEditor} className="quit-btn"> <span onClick={handleClickEditor} className={`quit-btn`}>
<Icon type="form" className="quit-icon"/> 编辑 <Icon type="form" className="quit-icon"/> 编辑
</span> </span>
{/* to="/problems" */} {/* to="/problems" */}
@ -102,9 +128,11 @@ const StudentStudy = (props) => {
const mapStateToProps = (state) => { const mapStateToProps = (state) => {
const { userInfo } = state.userReducer; const { userInfo } = state.userReducer;
const { hack_identifier } = state.ojForUserReducer; const { hack_identifier, user_program_identifier, hack } = state.ojForUserReducer;
return { return {
hack,
userInfo, userInfo,
user_program_identifier,
hack_identifier hack_identifier
}; };
}; };
@ -115,7 +143,9 @@ const mapDispatchToProps = (dispatch) => ({
// 调用编程题详情 // 调用编程题详情
getUserProgramDetail: (id) => dispatch(actions.getUserProgramDetail(id)), getUserProgramDetail: (id) => dispatch(actions.getUserProgramDetail(id)),
saveUserProgramIdentifier: (id) => dispatch(actions.saveUserProgramIdentifier(id)), saveUserProgramIdentifier: (id) => dispatch(actions.saveUserProgramIdentifier(id)),
saveEditorCodeForDetail: (code) => dispatch(actions.saveEditorCodeForDetail(code)) saveEditorCodeForDetail: (code) => dispatch(actions.saveEditorCodeForDetail(code)),
// 恢复初始代码
restoreInitialCode: (identifier, msg) => dispatch(actions.restoreInitialCode(identifier, msg)),
}); });
export default withRouter(connect( export default withRouter(connect(

@ -4,7 +4,7 @@
* @Github: * @Github:
* @Date: 2019-11-27 14:59:51 * @Date: 2019-11-27 14:59:51
* @LastEditors: tangjiang * @LastEditors: tangjiang
* @LastEditTime: 2019-12-10 19:00:30 * @LastEditTime: 2019-12-13 16:50:40
*/ */
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import {connect} from 'react-redux'; import {connect} from 'react-redux';
@ -66,7 +66,7 @@ const RightPane = (props) => {
} }
// 恢复初始代码 // 恢复初始代码
const handleRestoreInitialCode = () => { const handleRestoreInitialCode = () => {
restoreInitialCode(identifier); restoreInitialCode(identifier, '恢复初始代码成功');
} }
return ( return (
@ -110,7 +110,7 @@ const mapDispatchToProps = (dispatch) => ({
// 保存用户代码至后台 // 保存用户代码至后台
saveUserCodeForInterval: (identifier, code) => dispatch(actions.saveUserCodeForInterval(identifier, code)), saveUserCodeForInterval: (identifier, code) => dispatch(actions.saveUserCodeForInterval(identifier, code)),
// 恢复初始代码 // 恢复初始代码
restoreInitialCode: (identifier) => dispatch(actions.restoreInitialCode(identifier)), restoreInitialCode: (identifier, msg) => dispatch(actions.restoreInitialCode(identifier, msg)),
}); });
export default connect( export default connect(

@ -334,6 +334,33 @@ class ShixunsHome extends Component {
.square-Item:nth-child(4n+0) { .square-Item:nth-child(4n+0) {
margin-right: 25px; margin-right: 25px;
} }
.tag-org{
position: absolute;
left: 0px;
top: 20px;
}
.tag-org-name{
width:66px;
height:28px;
background:#FF6802;
width:66px;
height:28px;
border-radius:0px 20px 20px 0px;
}
.tag-org-name-test{
width:45px;
height:23px;
font-size:14px;
color:#FFFFFF;
line-height:19px;
margin-right: 6px;
}
.intermediatecenter{
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
` `
} }
</style> </style>
@ -345,7 +372,13 @@ class ShixunsHome extends Component {
<span className="tag-name"> {item.tag_name}</span> <span className="tag-name"> {item.tag_name}</span>
{/*<img style={{display:item.tag_name===null?"none":'block'}} src={require(`./tag2.png`)}/>*/} {/*<img style={{display:item.tag_name===null?"none":'block'}} src={require(`./tag2.png`)}/>*/}
</div> </div>
{
item.is_jupyter===true?
<div className="tag-org">
<p className="tag-org-name intermediatecenter"> <span className="tag-org-name-test">Jupyter</span></p>
{/*<img style={{display:'block',height: '28px'}} src={require(`./shixunCss/tag2.png`)}/>*/}
</div>
:""}
<div className={item.power === false ? "closeSquare" : "none"}> <div className={item.power === false ? "closeSquare" : "none"}>
<img src={getImageUrl("images/educoder/icon/lockclose.svg")} <img src={getImageUrl("images/educoder/icon/lockclose.svg")}
className="mt80 mb25"/> className="mt80 mb25"/>

@ -0,0 +1,54 @@
import React, {Component} from 'react';
import {
Button,
} from 'antd';
class Bottomsubmit extends Component {
constructor(props) {
super(props)
this.state = {}
}
cannelfun = () => {
// window.location.href=
this.props.history.replace(this.props.url);
}
render() {
return (
<div>
<style>
{
`
.newFooter{
display:none;
}
`
}
</style>
<div className="clearfix bor-bottom-greyE edu-back-white orderingbox newshixunbottombtn">
<div className=" edu-txt-center padding13-30">
<button type="button" className="ant-btn mr20 newshixunmode backgroundFFF" onClick={() => this.cannelfun()}>
<span> </span></button>
<Button type="button" className="ant-btn newshixunmode mr40 ant-btn-primary" type="primary"
htmlType="submit" onClick={() => this.props.onSubmits()}
loading={this.props.loadings}><span>{this.props.bottomvalue===undefined?"确 定":this.props.bottomvalue}</span></Button>
</div>
</div>
</div>
);
}
}
export default Bottomsubmit;

@ -28,6 +28,12 @@ render() {
` `
body{ body{
overflow: hidden !important; overflow: hidden !important;
}
.ant-modal-body {
padding: 20px 40px;
}
.color848282{
color:#848282;
} }
` `
} }
@ -35,8 +41,9 @@ render() {
<Spin indicator={antIcons} spinning={this.props.antIcon===undefined?false:this.props.antIcon} > <Spin indicator={antIcons} spinning={this.props.antIcon===undefined?false:this.props.antIcon} >
<div className="task-popup-content"> <div className="task-popup-content">
<p className="task-popup-text-center font-16">{this.props.modalsTopval}</p> <p className="task-popup-text-center font-16">{this.props.modalsTopval}</p>
{this.props.modalsMidval===undefined?"":<p className="task-popup-text-center font-16 mt5">{this.props.modalsMidval}</p>} {this.props.modalsMidval===undefined?"":<p className={this.props.modalstyles?"task-popup-text-center font-16 mt5 color848282":"task-popup-text-center font-16 mt5"}>{this.props.modalsMidval}</p>}
<p className="task-popup-text-center font-16 mt5">{this.props.modalsBottomval}</p> <p className={this.props.modalstyles?"task-popup-text-center font-16 mt5 color848282":"task-popup-text-center font-16 mt5"}
>{this.props.modalsBottomval}</p>
{this.props.loadtype===true? {this.props.loadtype===true?
<div className="clearfix edu-txt-center mt20"> <div className="clearfix edu-txt-center mt20">
<a className="task-btn task-btn-orange pop_close" onClick={this.props.modalSave}>知道啦</a> <a className="task-btn task-btn-orange pop_close" onClick={this.props.modalSave}>知道啦</a>

@ -1,12 +1,13 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import {getImageUrl} from 'educoder'; import {getImageUrl} from 'educoder';
import {Modal,Input} from 'antd'; import {Modal,Input,Form,Radio} from 'antd';
class Addshixuns extends Component { class Addshixuns extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
shixunname:undefined, shixunname:undefined,
shixunzero:false shixunzero:false,
is_jupyter:"1"
} }
} }
@ -52,12 +53,23 @@ class Addshixuns extends Component {
}) })
return return
} }
this.props.Setaddshixuns(shixunname);
let is_jupyter=this.state.is_jupyter==="1"?false:true
this.props.Setaddshixuns(shixunname,is_jupyter);
this.props.modalCancel(); this.props.modalCancel();
} }
GrouponChange = e => {
this.setState({
is_jupyter: e.target.value,
});
};
render() { render() {
const formItemLayout = {
labelCol: { span: 4 },
wrapperCol: { span: 14 },
};
console.log(this.props)
return( return(
<Modal <Modal
className={this.props.className} className={this.props.className}
@ -81,6 +93,14 @@ class Addshixuns extends Component {
</style>:""} </style>:""}
<div className="task-popup-content"> <div className="task-popup-content">
{/*<Form {...formItemLayout}>*/}
{/* <Form.Item label="实训类型">*/}
{/* <Radio.Group value={this.state.is_jupyter} onChange={this.GrouponChange}>*/}
{/* <Radio value="1">普通实训</Radio>*/}
{/* <Radio value="2">jupyter实训</Radio>*/}
{/* </Radio.Group>*/}
{/* </Form.Item>*/}
{/*</Form>*/}
<p className="task-popup-text-center font-16"> <p className="task-popup-text-center font-16">
<span style={{ "line-height":"30px"}}>实训名称</span> <span style={{ "line-height":"30px"}}>实训名称</span>
<span><Input style={{ width:"80%"}} className="yslzxueshisy " placeholder="请输入60字以内的实训名称" onChange={this.handleChange} addonAfter={String(this.state.shixunname===undefined?0:this.state.shixunname.length)+"/60"} maxLength={60} /> <span><Input style={{ width:"80%"}} className="yslzxueshisy " placeholder="请输入60字以内的实训名称" onChange={this.handleChange} addonAfter={String(this.state.shixunname===undefined?0:this.state.shixunname.length)+"/60"} maxLength={60} />

@ -320,7 +320,7 @@ class DetailCardsEditAndAdd extends Component{
}) })
} }
Getaddshixuns=(value)=>{ Getaddshixuns=(value,is_jupyter)=>{
let { let {
shixuns_listeditlist, shixuns_listeditlist,
shixuns_listedit, shixuns_listedit,
@ -329,7 +329,8 @@ class DetailCardsEditAndAdd extends Component{
let list=shixuns_listeditlist let list=shixuns_listeditlist
let url='/paths/add_shixun_to_stage.json'; let url='/paths/add_shixun_to_stage.json';
axios.post(url,{ axios.post(url,{
name:value name:value,
is_jupyter:is_jupyter
}).then((response) => { }).then((response) => {
if(response){ if(response){
if(response.data){ if(response.data){
@ -383,7 +384,7 @@ class DetailCardsEditAndAdd extends Component{
{this.state.Addshixunstype===true?<Addshixuns {this.state.Addshixunstype===true?<Addshixuns
modalCancel={this.cardsModalcancel} modalCancel={this.cardsModalcancel}
Setaddshixuns={(value)=>this.Getaddshixuns(value)} Setaddshixuns={(value,is_jupyter)=>this.Getaddshixuns(value,is_jupyter)}
{...this.props} {...this.props}
{...this.state} {...this.state}
/>:""} />:""}

@ -320,7 +320,7 @@ class DetailCardsEditAndEdit extends Component{
notification.open(data); notification.open(data);
} }
Getaddshixuns=(value)=>{ Getaddshixuns=(value,is_jupyter)=>{
let { let {
shixuns_listeditlist, shixuns_listeditlist,
shixuns_listedit, shixuns_listedit,
@ -329,7 +329,8 @@ class DetailCardsEditAndEdit extends Component{
let list=shixuns_listeditlist let list=shixuns_listeditlist
let url='/paths/add_shixun_to_stage.json'; let url='/paths/add_shixun_to_stage.json';
axios.post(url,{ axios.post(url,{
name:value name:value,
is_jupyter:is_jupyter
}).then((response) => { }).then((response) => {
if(response){ if(response){
if(response.data){ if(response.data){
@ -383,7 +384,7 @@ class DetailCardsEditAndEdit extends Component{
</Modals> </Modals>
{this.state.Addshixunstype===true?<Addshixuns {this.state.Addshixunstype===true?<Addshixuns
modalCancel={this.cardsModalcancel} modalCancel={this.cardsModalcancel}
Setaddshixuns={(value)=>this.Getaddshixuns(value)} Setaddshixuns={(value,is_jupyter)=>this.Getaddshixuns(value,is_jupyter)}
{...this.props} {...this.props}
{...this.state} {...this.state}
/>:""} />:""}

@ -212,6 +212,7 @@ class Audit_situationComponent extends Component {
user={user} user={user}
shixun={shixun} shixun={shixun}
{...this.props} {...this.props}
is_jupyter={this.props.is_jupyter}
></TPMNav> ></TPMNav>
<div className="padding20 edu-back-white mt20" style={{minHeight: '640px'}}> <div className="padding20 edu-back-white mt20" style={{minHeight: '640px'}}>

File diff suppressed because it is too large Load Diff

@ -8,7 +8,7 @@ import { CircularProgress } from 'material-ui/Progress';
import './TPMShixunDiscuss.css' import './TPMShixunDiscuss.css'
import Challenges from './shixunchild/Challenges/Challenges' import Challenges from './shixunchild/Challenges/Challenges'
import Challengesjupyter from './shixunchild/Challenges/Challengesjupyter'
import TPMRightSection from './component/TPMRightSection' import TPMRightSection from './component/TPMRightSection'
import TPMNav from './component/TPMNav' import TPMNav from './component/TPMNav'
@ -20,7 +20,7 @@ class TPMChallenge extends Component {
} }
render() { render() {
const { loadingContent, shixun, user, match const { loadingContent, shixun, user, match,jupyterbool,is_jupyter
} = this.props; } = this.props;
return ( return (
<React.Fragment> <React.Fragment>
@ -32,10 +32,19 @@ class TPMChallenge extends Component {
user={user} user={user}
shixun={shixun} shixun={shixun}
{...this.props} {...this.props}
is_jupyter={this.props.is_jupyter}
></TPMNav> ></TPMNav>
{
is_jupyter===true?
<Challengesjupyter
{...this.props}
/>
:
<Challenges <Challenges
{...this.props} {...this.props}
/> />
}
</div> </div>

@ -15,6 +15,8 @@ class TPMChallengeContainer extends Component {
render() { render() {
const { tpmLoading } = this.props; const { tpmLoading } = this.props;
const user = this.props.current_user; const user = this.props.current_user;
// console.log("TPMChallengeContainerTPMChallengeContainer");
// console.log(this.props);
return ( return (
<React.Fragment> <React.Fragment>
@ -22,6 +24,7 @@ class TPMChallengeContainer extends Component {
{ tpmLoading ? <div style={{ minHeight: '886px'}}></div> : { tpmLoading ? <div style={{ minHeight: '886px'}}></div> :
<TPMChallenge <TPMChallenge
{...this.props} {...this.props}
is_jupyter={this.props.is_jupyter}
> >
</TPMChallenge> </TPMChallenge>
} }

@ -31,6 +31,7 @@ class TPMCollaborators extends Component {
user={user} user={user}
shixun={shixun} shixun={shixun}
{...this.props} {...this.props}
is_jupyter={this.props.is_jupyter}
></TPMNav> ></TPMNav>
<Collaborators <Collaborators
{...this.props} {...this.props}

@ -35,7 +35,7 @@ class TPMChallengeContainer extends Component {
{...this.state} {...this.state}
user={user} user={user}
aboutFocus={this.props.aboutFocus} aboutFocus={this.props.aboutFocus}
is_jupyter={this.props.is_jupyter}
> >
</TPMCollaborators> </TPMCollaborators>
} }

@ -0,0 +1,630 @@
import React, {Component} from 'react';
import {Redirect} from 'react-router';
import {List, Typography, Tag, Modal, Radio, Checkbox, Table, Pagination,Upload,notification} from 'antd';
import { NoneData } from 'educoder'
import TPMRightSection from './component/TPMRightSection';
import TPMNav from './component/TPMNav';
import axios from 'axios';
import './tpmmodel/tpmmodel.css'
import {getUploadActionUrltwo,appendFileSizeToUploadFileAll} from 'educoder';
import moment from 'moment';
import Tpmdatasetmodel from "./tpmmodel/Tpmdatasetmodel";
const confirm = Modal.confirm;
class TPMDataset extends Component {
constructor(props) {
super(props)
this.state = {
value: undefined,
columns: [
{
title: '文件',
dataIndex: 'title',
key: 'title',
align: 'left',
className: " font-14 wenjiantit",
width: '220px',
render: (text, record) => (
<div>
{record.title}
</div>
)
},
{
title: '最后修改时间',
dataIndex: 'timedata',
key: 'timedata',
align: 'center',
className: "edu-txt-center font-14 zuihoushijian",
width: '150px',
render: (text, record) => (
<div>
{record.timedata}
</div>
)
},
{
title: '最后修改人',
dataIndex: 'author',
key: 'author',
align: 'center',
className: "edu-txt-center font-14 ",
render: (text, record) => (
<div>
{record.author}
</div>
)
},
{
title: '文件大小',
dataIndex: 'filesize',
key: 'filesize',
align: 'center',
className: "edu-txt-center font-14 ",
render: (text, record) => (
<div>
{record.filesize}
</div>
)
},
],
page: 1,
limit: 10,
selectedRowKeys: [],
mylistansum:30,
collaboratorList:[],
fileList:[],
fileListimgs:[],
file:null,
datalist:[],
data_sets_count:0,
selectedRowKeysdata:[],
loadingstate:false,
checked: false,
showmodel:false,
itemtypebool:false,
}
}
componentDidMount() {
this.setState({
loadingstate:true,
})
this.getdatas()
}
mysonChange = (e) => {
// console.log(`全选checked = ${e.target.checked}`);
if (e.target.checked === true) {
let mydata=[];
let datas=[];
for(let i=0;i<this.state.collaboratorList.data_sets.length;i++){
mydata.push(this.state.collaboratorList.data_sets[i].id);
datas.push(i);
}
this.setState({
selectedRowKeysdata:mydata,
selectedRowKeys: datas,
checked:true,
})
// console.log(mydata);
// console.log(datas);
} else {
this.setState({
selectedRowKeysdata:[],
selectedRowKeys: [],
checked:false,
})
}
}
getdatas = () => {
let id=this.props.match.params.shixunId;
let collaborators=`/shixuns/${id}/get_data_sets.json`;
axios.get(collaborators,{params:{
page:1,
limit:10,
}}).then((response)=> {
if(response.status===200){
if (response.data.status === 403||response.data.status === 401||response.data.status === 500) {
}else{
let datalists=[];
for(let i=0;i<response.data.data_sets.length;i++){
const datas=response.data.data_sets;
var timedata = moment(datas[i].created_on).format('YYYY-MM-DD HH:mm');
datalists.push({
timedata:timedata,
author:datas[i].author,
filesize:datas[i].filesize,
id:datas[i].id,
title:datas[i].title,
})
}
this.setState({
collaboratorList: response.data,
data_sets_count:response.data.data_sets_count,
datalist:datalists,
selectedRowKeysdata:[],
selectedRowKeys: [],
checked:false,
});
}
}
setTimeout(() => {
this.setState({
loadingstate:false,
})
}, 500)
}).catch((error)=>{
setTimeout(() => {
this.setState({
loadingstate:false,
})
}, 500)
console.log(error)
});
}
getdatastwo = (page,limit) => {
let id=this.props.match.params.shixunId;
let collaborators=`/shixuns/${id}/jupyter_data_sets.json`;
axios.get(collaborators,{params:{
page:page,
limit:limit,
}}).then((response)=> {
if(response.status===200){
if (response.data.status === 403||response.data.status === 401||response.data.status === 500) {
}else{
let datalists=[];
for(let i=0;i<response.data.data_sets.length;i++){
const datas=response.data.data_sets;
var timedata = moment(datas[i].created_on).format('YYYY-MM-DD HH:mm');
datalists.push({
timedata:timedata,
author:datas[i].author,
filesize:datas[i].filesize,
id:datas[i].id,
title:datas[i].title,
})
}
this.setState({
collaboratorList: response.data,
data_sets_count:response.data.data_sets_count,
datalist:datalists,
selectedRowKeysdata:[],
selectedRowKeys: [],
checked:false,
});
}
}
setTimeout(() => {
this.setState({
loadingstate:false,
})
}, 500)
}).catch((error)=>{
setTimeout(() => {
this.setState({
loadingstate:false,
})
}, 500)
console.log(error)
});
}
getdatasthree = (page,limit) => {
let id=this.props.match.params.shixunId;
let collaborators=`/shixuns/${id}/jupyter_data_sets.json`;
axios.get(collaborators,{params:{
page:page,
limit:limit,
}}).then((response)=> {
if(response.status===200){
if (response.data.status === 403||response.data.status === 401||response.data.status === 500) {
}else{
}
}
}).catch((error)=>{
});
}
paginationonChanges = (pageNumber) => {
// //console.log('Page: ');
this.setState({
page: pageNumber,
loadingstate:true,
})
this.getdatastwo(pageNumber,10);
}
onSelectChange = (selectedRowKeys, selectedRows) => {
// console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows);
this.setState(
{
selectedRowKeys
}
);
let mydata=[];
for(let i=0;i<selectedRows.length;i++){
mydata.push(selectedRows[i].id);
}
this.setState({
selectedRowKeysdata:mydata,
})
// console.log(mydata);
}
rowClassName = (record, index) => {
let className = 'light-row';
if (index % 2 === 1) className = 'dark-row';
return className;
}
handleChange = (info) => {
// console.log("handleChange123123");
// console.log(info);
// debugger
if(info.file.status == "done" || info.file.status == "uploading" || info.file.status === 'removed'){
let fileList = info.fileList;
this.setState({
fileList: appendFileSizeToUploadFileAll(fileList),
});
if(info.file.status === 'done'){
//done 成功就会调用这个方法
this.getdatas();
}
if(info.file.response){
if(info.file.response.status===-1||info.file.response.status==="-1"){
// console.log("准备显示弹框了");
// console.log(info);false
let itemtype=-1;
try {
itemtype=info.file.response.message.indexOf('文件名已经存在'.toLowerCase());
}catch (e) {
}
this.setState({
showmodel:true,
tittest:info.file.response.message,
itemtypebool:itemtype>-1?true:itemtype<=-1?false:false,
})
}
}
}
}
onAttachmentRemove = (file) => {
// debugger
if(!file.percent || file.percent == 100){
confirm({
title: '确定要删除这个附件吗?',
okText: '确定',
cancelText: '取消',
// content: 'Some descriptions',
onOk: () => {
console.log("665")
this.deleteAttachment(file)
},
onCancel() {
console.log('Cancel');
},
});
return false;
}
}
deleteRemovedata(){
if(this.state.selectedRowKeysdata===undefined || this.state.selectedRowKeysdata===null ||this.state.selectedRowKeysdata.length===0){
this.props.showNotification(`请选择要删除的文件`);
return
}
let id=this.props.match.params.shixunId;
confirm({
title: '确定要删除文件吗?',
okText: '确定',
cancelText: '取消',
// content: 'Some descriptions',
onOk: () => {
const url = `/shixuns/${id}/destroy_data_sets.json`;
axios.delete(url,
{ params: {
id:this.state.selectedRowKeysdata,
}}
)
.then((response) => {
if (response.data) {
const { status } = response.data;
if (status == 0) {
this.props.showNotification(`删除成功`);
this.getdatas()
}
}
})
.catch(function (error) {
console.log(error);
});
},
onCancel() {
console.log('Cancel');
},
});
}
deleteAttachment = (file) => {
// console.log(file);
let id=file.response ==undefined ? file.id : file.response.id
const url = `/attachements/destroy_files.json`
axios.delete(url, {
id:[id],
})
.then((response) => {
if (response.data) {
const { status } = response.data;
if (status == 0) {
// console.log('--- success')
this.setState((state) => {
const index = state.fileList.indexOf(file);
const newFileList = state.fileList.slice();
newFileList.splice(index, 1);
return {
fileList: newFileList,
deleteisnot:true
};
});
}
}
})
.catch(function (error) {
console.log(error);
});
}
ModalCancel = () => {
this.setState({
showmodel: false,
})
}
ModalSave=()=>{
this.setState({
showmodel: false,
})
}
render() {
const {tpmLoading, shixun, user, match} = this.props;
const {columns, page, limit, selectedRowKeys,mylistansum,fileList,datalist,data_sets_count,loadingstate} = this.state;
const rowSelection = {
selectedRowKeys,
onChange: this.onSelectChange,
};
// getCheckboxProps: record => ({
// disabled: record.name === 'Disabled User', // Column configuration not to be checked
// name: record.name,
// }),
let id=this.props.match.params.shixunId;
const uploadProps = {
width: 600,
fileList,
multiple: false,
//multiple 是否支持多选 查重的时候不能多选 不然弹许多框出来
// https://github.com/ant-design/ant-design/issues/15505
// showUploadList={false},然后外部拿到 fileList 数组自行渲染列表。
// showUploadList: false,
action: `${getUploadActionUrltwo(id)}`,
showUploadList:false,
onChange: this.handleChange,
onRemove: this.onAttachmentRemove,
beforeUpload: (file) => {
//上传前的操作
// console.log('beforeUpload', file.name);
const isLt150M = file.size / 1024 / 1024 < 150;
if (!isLt150M) {
this.props.showNotification('文件大小必须小于150MB!');
}
return isLt150M;
},
};
// console.log("showmodelshowmodel");
// console.log(this.state.showmodel);
return (
<React.Fragment>
<div className="tpmComment educontent clearfix mt30 mb80">
<div className="with65 fl edu-back-white commentsDelegateParent">
{
this.state.showmodel===true?
<Tpmdatasetmodel itemtypebool={this.state.itemtypebool} modalCancel={()=>this.ModalSave()} tittest={this.state.tittest} modalsType={this.state.showmodel}></Tpmdatasetmodel>
:""
}
<TPMNav
match={match}
user={user}
shixun={shixun}
{...this.props}
is_jupyter={this.props.is_jupyter}
></TPMNav>
<div className="padding20 edu-back-white mt20 " style={{minHeight: '463px'}}>
<div className="sortinxdirection">
<div className="tpmwidth">
<Checkbox checked={this.state.checked} onChange={this.mysonChange}>全选</Checkbox>
</div>
<div className="tpmwidth xaxisreverseorder">
<style>
{
`
.ant-upload-list{
display:none
}
`
}
</style>
<div className="deletebuttom intermediatecenter "> <Upload {...uploadProps}><p className="deletebuttomtest" type="upload">
上传文件</p> </Upload></div>
{
data_sets_count>0?
<div
className={selectedRowKeys.length > 0 ? "deletebutomtextcode intermediatecenter mr21" : "deletebutom intermediatecenter mr21"} onClick={()=>this.deleteRemovedata()}>
<p className="deletebutomtext" >删除</p></div>
:""
}
</div>
</div>
<div className="mt24">
<style>{`
.ant-spin-nested-loading > div > .ant-spin .ant-spin-dot {
top: 72%;}
}
.edu-table .ant-table-tbody > tr > td {
height: 42px;
}
.edu-table .ant-table-thead > tr > th{
height: 42px;
}
.ysltableowss .ant-table-thead > tr > th{
height: 42px;
}
.ysltableowss .ant-table-tbody > tr > td{
height: 42px;
}
.ysltableowss .ant-table-thead > tr > th, .ant-table-tbody > tr > td {
padding: 9px;
}
.mysjysltable4 .ant-table-thead > tr > th, .ant-table-tbody > tr > td {
padding: 0px;
}
.ant-table-thead .ant-table-selection-column span{
visibility:hidden;
}
.ant-table-thead > tr > th {
background:#FFFFFF !important;
}
.ant-table table {
width: 100%;
text-align: left;
border-radius: 4px 4px 0 0;
border-collapse: separate;
border-spacing: 0;
border-left: 1px solid #eeeeee;
border-top: 1px solid #eeeeee;
border-right: 1px solid #eeeeee;
}
`}</style>
{data_sets_count===0?
<div className="edu-table edu-back-white ysltableowss">
<style>
{
`
.ant-table-tbody{
display:none;
}
.ant-table-placeholder{
display:none;
}
.ant-table table {
border-bottom: 1px solid #eeeeee !important;
}
`
}
</style>
<Table
columns={columns}
pagination={false}
className="mysjysltable4"
rowSelection={rowSelection}
rowClassName={this.rowClassName}
/>
</div>
:
<div className="edu-table edu-back-white ysltableowss">
<Table
dataSource={datalist}
columns={columns}
pagination={false}
className="mysjysltable4"
rowSelection={rowSelection}
rowClassName={this.rowClassName}
loading={loadingstate}
/>
</div>
}
{
data_sets_count>=11?
<div className="edu-txt-center mt40 mb20">
<Pagination showQuickJumper current={page}
onChange={this.paginationonChanges} pageSize={limit}
total={data_sets_count}
></Pagination>
</div>
:""
}
{ data_sets_count===0?
<NoneData style={{width: '100%'}}></NoneData>:""
}
</div>
</div>
</div>
<div className="with35 fr pl20">
<TPMRightSection
{...this.props}
/>
</div>
</div>
</React.Fragment>
);
}
}
export default TPMDataset;

@ -38,7 +38,7 @@ class TPMRanking_listContainer extends Component {
{...this.state} {...this.state}
user={user} user={user}
aboutFocus={this.props.aboutFocus} aboutFocus={this.props.aboutFocus}
is_jupyter={this.props.is_jupyter}
> >
</TPMForklist> </TPMForklist>
} }

@ -42,6 +42,7 @@ class TPMForklist extends Component {
user={user} user={user}
shixun={shixun} shixun={shixun}
{...this.props} {...this.props}
is_jupyter={this.props.is_jupyter}
></TPMNav> ></TPMNav>
{ loadingContent ? { loadingContent ?
<CircularProgress size={40} thickness={3} style={{ marginLeft: 'auto', marginRight: 'auto', marginTop: '200px', display: 'block' }}/> : <CircularProgress size={40} thickness={3} style={{ marginLeft: 'auto', marginRight: 'auto', marginTop: '200px', display: 'block' }}/> :

@ -114,7 +114,7 @@ body>.-task-title {
/*-------------------个人主页:右侧提示区域--------------------------*/ /*-------------------个人主页:右侧提示区域--------------------------*/
.-task-sidebar{position:fixed;width:40px;height:180px;right:0;bottom:30px;z-index: 10;} .-task-sidebar{position:fixed;width:40px;height:180px;right:0;bottom:80px !important;z-index: 10;}
.-task-sidebar>div{height: 40px;line-height: 40px;box-sizing: border-box;width:40px;background:#4CACFF;color:#fff;font-size:20px;text-align:center;margin-bottom:5px;border-radius: 4px;} .-task-sidebar>div{height: 40px;line-height: 40px;box-sizing: border-box;width:40px;background:#4CACFF;color:#fff;font-size:20px;text-align:center;margin-bottom:5px;border-radius: 4px;}
.-task-sidebar>div i{ color:#fff;} .-task-sidebar>div i{ color:#fff;}
.-task-sidebar>div i:hover{color: #fff!important;} .-task-sidebar>div i:hover{color: #fff!important;}

@ -22,13 +22,16 @@ import TPMRepositoryCommits from './shixunchild/Repository/TPMRepositoryCommits'
import TPMsettings from './TPMsettings/TPMsettings'; import TPMsettings from './TPMsettings/TPMsettings';
//import TPMsettings from './TPMsettings/oldTPMsettings';
import TPMChallengeComponent from './TPMChallengeContainer'; import TPMChallengeComponent from './TPMChallengeContainer';
import TPMPropaedeuticsComponent from './TPMPropaedeuticsComponent'; import TPMPropaedeuticsComponent from './TPMPropaedeuticsComponent';
import TPMRanking_listComponent from './TPMRanking_listContainer'; import TPMRanking_listComponent from './TPMRanking_listContainer';
import TPMCollaboratorsComponent from './TPMCollaboratorsContainer'; import TPMCollaboratorsComponent from './TPMCollaboratorsContainer';
import Audit_situationComponent from './Audit_situationComponent'; import Audit_situationComponent from './Audit_situationComponent';
import TPMDataset from './TPMDataset';
import '../page/tpiPage.css' import '../page/tpiPage.css'
import TPMNav from "./component/TPMNav";
const $ = window.$ const $ = window.$
//任务 //任务
@ -142,13 +145,15 @@ class TPMIndex extends Component {
identity:undefined, identity:undefined,
TPMRightSectionData:undefined, TPMRightSectionData:undefined,
PropaedeuticsList: undefined, PropaedeuticsList: undefined,
tpmindexjupyterbool:false,
is_jupyter:false,
} }
} }
componentDidMount = () => { componentDidMount = () => {
let id = this.props.match.params.shixunId; let id = this.props.match.params.shixunId;
console.log('props', this.props); // console.log('props', this.props);
// let collaborators = `/shixuns/` + id + `/propaedeutics.json`; // let collaborators = `/shixuns/` + id + `/propaedeutics.json`;
// //
// axios.get(collaborators).then((response) => { // axios.get(collaborators).then((response) => {
@ -192,7 +197,7 @@ class TPMIndex extends Component {
propaedeutics:response.data.propaedeutics, propaedeutics:response.data.propaedeutics,
status: response.data.shixun_status, status: response.data.shixun_status,
secret_repository: response.data.secret_repository, secret_repository: response.data.secret_repository,
is_jupyter:response.data.is_jupyter=== undefined||response.data.is_jupyter===null?false:response.data.is_jupyter,
}); });
} }
}).catch((error) => { }).catch((error) => {
@ -204,7 +209,8 @@ class TPMIndex extends Component {
power: undefined, power: undefined,
identity: undefined, identity: undefined,
status: undefined, status: undefined,
propaedeutics:undefined propaedeutics:undefined,
is_jupyter:false,
}); });
}); });
@ -270,31 +276,42 @@ class TPMIndex extends Component {
// } // }
render() { render() {
let url = window.location.href; let url = window.location.href;
let flag = url.indexOf("add_file")>-1; let flag = url.indexOf("add_file")>-1;
// console.log(this.state.is_jupyter);
return ( return (
<div className="newMain clearfix"> <div className="newMain clearfix">
{/*头部*/}
{ {
!flag && !flag &&
<TPMBanner <TPMBanner
{...this.props} {...this.props}
{...this.state} {...this.state}
is_jupyter={this.state. is_jupyter}
></TPMBanner> ></TPMBanner>
} }
{/*筛选*/}
{/*{*/}
{/* tpmindexjupyterbool===false?*/}
{/* :""*/}
{/*}*/}
{/* */}
<Switch {...this.props}> <Switch {...this.props}>
<Route path="/shixuns/:shixunId/repository/:repoId/commits" render={ <Route path="/shixuns/:shixunId/repository/:repoId/commits" render={
(props) => (<TPMRepositoryCommits {...this.props} {...this.state} {...props} (props) => (<TPMRepositoryCommits {...this.props} {...this.state} {...props} is_jupyter={this.state.is_jupyter}
/>) />)
}></Route> }></Route>
<Route path="/shixuns/:shixunId/secret_repository/:repoId/commits" render={ <Route path="/shixuns/:shixunId/secret_repository/:repoId/commits" render={
(props) => (<TPMRepositoryCommits {...this.props} {...this.state} {...props} secret_repository_tab={true} (props) => (<TPMRepositoryCommits {...this.props} {...this.state} {...props} secret_repository_tab={true} is_jupyter={this.state.is_jupyter}
/>) />)
}></Route> }></Route>
{/*任务*/}
<Route exact path="/shixuns/:shixunId/challenges" render={ <Route exact path="/shixuns/:shixunId/challenges" render={
(props) => (<TPMChallengeComponent {...this.props} {...this.state} {...props} (props) => (<TPMChallengeComponent {...this.props} {...this.state} {...props} is_jupyter={this.state.is_jupyter}
/>) />)
}></Route> }></Route>
@ -304,24 +321,24 @@ class TPMIndex extends Component {
}></Route> }></Route>
<Route path="/shixuns/:shixunId/repository" render={ <Route path="/shixuns/:shixunId/repository" render={
(props) => (<TPMRepositoryComponent {...this.props} {...this.state} {...props} (props) => (<TPMRepositoryComponent {...this.props} {...this.state} {...props} is_jupyter={this.state.is_jupyter}
/>) />)
}></Route> }></Route>
<Route path="/shixuns/:shixunId/secret_repository" render={ <Route path="/shixuns/:shixunId/secret_repository" render={
(props) => (<TPMRepositoryComponent {...this.props} {...this.state} {...props} secret_repository_tab={true} (props) => (<TPMRepositoryComponent {...this.props} {...this.state} {...props} secret_repository_tab={true} is_jupyter={this.state.is_jupyter}
/>) />)
}></Route> }></Route>
{/* <Route exact path="/shixuns/:shixunId/propaedeutics" component={TPMPropaedeuticsComponent}></Route> */} {/* <Route exact path="/shixuns/:shixunId/propaedeutics" component={TPMPropaedeuticsComponent}></Route> */}
<Route exact path="/shixuns/:shixunId/propaedeutics" render={ <Route exact path="/shixuns/:shixunId/propaedeutics" render={
(props) => (<TPMPropaedeuticsComponent {...this.props} {...this.state} {...props} (props) => (<TPMPropaedeuticsComponent {...this.props} {...this.state} {...props} is_jupyter={this.state.is_jupyter}
/>) />)
}></Route> }></Route>
<Route exact path="/shixuns/:shixunId/collaborators" render={ <Route exact path="/shixuns/:shixunId/collaborators" render={
(props) => (<TPMCollaboratorsComponent {...this.props} {...this.state} {...props} (props) => (<TPMCollaboratorsComponent {...this.props} {...this.state} {...props} is_jupyter={this.state.is_jupyter}
/>) />)
}></Route> }></Route>
@ -330,7 +347,7 @@ class TPMIndex extends Component {
<Route path="/shixuns/:shixunId/shixun_discuss" render={ <Route path="/shixuns/:shixunId/shixun_discuss" render={
(props) => (<TPMShixunDiscussContainer {...this.props} {...this.state} {...props} (props) => (<TPMShixunDiscussContainer {...this.props} {...this.state} {...props} is_jupyter={this.state.is_jupyter}
initForumState={(data)=>this.initForumState(data)} initForumState={(data)=>this.initForumState(data)}
setSearchValue={this.setSearchValue} setSearchValue={this.setSearchValue}
setHotLabelIndex={this.setHotLabelIndex} setHotLabelIndex={this.setHotLabelIndex}
@ -342,14 +359,19 @@ class TPMIndex extends Component {
(props) => (<TPMsettings {...this.props} {...this.state} {...props} />) (props) => (<TPMsettings {...this.props} {...this.state} {...props} />)
}></Route> }></Route>
{/*实训项目条目塞选*/}
<Route exact path="/shixuns/:shixunId/ranking_list" render={ <Route exact path="/shixuns/:shixunId/ranking_list" render={
(props) => (<TPMRanking_listComponent {...this.props} {...this.state} {...props} (props) => (<TPMRanking_listComponent {...this.props} {...this.state} {...props} is_jupyter={this.state.is_jupyter}
/>)
}></Route>
{/*合作者*/}
<Route exact path="/shixuns/:shixunId/dataset" render={
(props) => (<TPMDataset {...this.props} {...this.state} {...props} is_jupyter={this.state.is_jupyter}
/>) />)
}></Route> }></Route>
<Route exact path="/shixuns/:shixunId/audit_situation" render={ <Route exact path="/shixuns/:shixunId/audit_situation" render={
(props) => (<Audit_situationComponent {...this.props} {...this.state} {...props} (props) => (<Audit_situationComponent {...this.props} {...this.state} {...props} is_jupyter={this.state.is_jupyter}
/>) />)
}></Route> }></Route>
@ -400,7 +422,7 @@ class TPMIndex extends Component {
}></Route> }></Route>
<Route exact path="/shixuns/:shixunId" render={ <Route exact path="/shixuns/:shixunId" render={
(props) => (<TPMChallengeComponent {...this.props} {...this.state} {...props} (props) => (<TPMChallengeComponent {...this.props} {...this.state} {...props} is_jupyter={this.state.is_jupyter}
/>) />)
}></Route> }></Route>

@ -23,7 +23,7 @@ const versionNum = '0001';
// let _url_origin = getUrl() // let _url_origin = getUrl()
let _url_origin=''; let _url_origin='';
if(window.location.port === "3007"){ if(window.location.port === "3007"){
_url_origin="http://pre-newweb.educoder.net"; _url_origin="https://test-newweb.educoder.net";
} }
// let _url_origin=`https://www.educoder.net`; // let _url_origin=`https://www.educoder.net`;

@ -52,6 +52,7 @@ class TPMPropaedeutics extends Component {
shixun={shixun} shixun={shixun}
{...this.state} {...this.state}
{...this.props} {...this.props}
is_jupyter={this.props.is_jupyter}
/> />
<Propaedeutics <Propaedeutics

@ -26,6 +26,7 @@ class TPMPropaedeuticsComponent extends Component {
{ tpmLoading ? <div style={{ minHeight: '886px'}}></div> : { tpmLoading ? <div style={{ minHeight: '886px'}}></div> :
<TPMPropaedeutics <TPMPropaedeutics
{...this.props} {...this.props}
is_jupyter={this.props.is_jupyter}
> >
</TPMPropaedeutics> </TPMPropaedeutics>
} }

@ -38,6 +38,7 @@ class TPMRanking_list extends Component {
user={user} user={user}
shixun={shixun} shixun={shixun}
{...this.props} {...this.props}
is_jupyter={this.props.is_jupyter}
></TPMNav> ></TPMNav>
<Ranking_list <Ranking_list

@ -6,6 +6,7 @@ import PropTypes from 'prop-types';
import TPMRanking_list from './TPMRanking_list' import TPMRanking_list from './TPMRanking_list'
import axios from 'axios'; import axios from 'axios';
import TPMNav from "./component/TPMNav";
class TPMRanking_listContainer extends Component { class TPMRanking_listContainer extends Component {
constructor(props) { constructor(props) {
@ -26,6 +27,8 @@ class TPMRanking_listContainer extends Component {
{...this.state} {...this.state}
user={user} user={user}
aboutFocus={this.props.aboutFocus} aboutFocus={this.props.aboutFocus}
is_jupyter={this.props.is_jupyter}
> >
</TPMRanking_list> </TPMRanking_list>
} }

@ -35,6 +35,7 @@ class TPMRepository extends Component {
user={user} user={user}
shixun={shixun} shixun={shixun}
{...this.props} {...this.props}
is_jupyter={this.props.is_jupyter}
></TPMNav> ></TPMNav>
{/* <RepositoryChooseModal {...this.props}></RepositoryChooseModal> */} {/* <RepositoryChooseModal {...this.props}></RepositoryChooseModal> */}
{ loadingContent ? { loadingContent ?

@ -200,6 +200,7 @@ class TPMRepositoryComponent extends Component {
{...this.state} {...this.state}
nameTypeMap={this.nameTypeMap} nameTypeMap={this.nameTypeMap}
fetchRepo={this.fetchRepo} fetchRepo={this.fetchRepo}
is_jupyter={this.props.is_jupyter}
> >
</TPMRepository> </TPMRepository>
: :

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save