chromesetting
tangjiang 5 years ago
commit 0af155697a

11
.gitignore vendored

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

@ -68,6 +68,9 @@ class AttachmentsController < ApplicationController
@attachment.author_id = current_user.id
@attachment.disk_directory = month_folder
@attachment.cloud_url = remote_path
@attachment.container_id = conversion_container_id(params[:container_id], params[:container_type])
@attachment.container_type = params[:container_type]
@attachment.attachtype = params[:attachtype]
@attachment.save!
else
logger.info "文件已存在id = #{@attachment.id}, filename = #{@attachment.filename}"
@ -91,6 +94,22 @@ class AttachmentsController < ApplicationController
end
end
# 多文件删除
def destroy_files
files = Attachment.where(id: params[:id])
begin
files.each do |file|
file_path = absolute_path(local_path(file))
file.destroy!
delete_file(file_path)
end
render_ok
rescue => e
uid_logger_error(e.message)
tip_exception(e.message)
end
end
private
def find_file
@file =
@ -196,4 +215,12 @@ class AttachmentsController < ApplicationController
end
end
end
def conversion_container_id id, type
if id.is_a?(String) && type == 'Shixun'
Shixun.find_by_identifier(id).id
else
id
end
end
end

@ -13,6 +13,9 @@ class ChallengesController < ApplicationController
include ShixunsHelper
include ChallengesHelper
# 新建实践题
def new
@position = @shixun.challenges.count + 1
@ -160,6 +163,8 @@ class ChallengesController < ApplicationController
@shixun.increment!(:visits)
end
def show
@tab = params[:tab].nil? ? 1 : params[:tab].to_i
challenge_num = @shixun.challenges_count
@ -173,6 +178,7 @@ class ChallengesController < ApplicationController
# tab 0:过关任务的更新; 1:评测设置的更新; 2:表示参考答案的更新;
def update
begin
ActiveRecord::Base.transaction do
tab = params[:tab].to_i
@challenge.update_attributes(challenge_params)
@ -231,6 +237,11 @@ class ChallengesController < ApplicationController
end
end
rescue => e
logger_error("##update_challenges: ##{e.message}")
tip_exception("更新失败!")
end
end
# 参考答案的'增,删,改'

@ -0,0 +1,92 @@
require 'net/http'
class JupytersController < ApplicationController
include JupyterService
before_action :shixun
def open
#打开tpm - juypter接口
shixun = @shixun
shixun_tomcat = edu_setting('cloud_bridge')
uri = "#{shixun_tomcat}/bridge/jupyter/get"
tpiID = "tpm#{shixun.id}"
params = {tpiID: tpiID, :containers => "#{Base64.urlsafe_encode64(shixun_container_limit(shixun))}"}
logger.info "test_juypter: uri->#{uri}, params->#{params}"
res = uri_post uri, params
logger.info "test_juypter: #{res}"
render plain: "https://#{res['port']}.jupyter.educoder.net/notebooks/data/workspace/myshixun_#{tpiID}/01.ipynb"
end
def open1
## 打开tpi
game = Game.find(2170158)
shixun = game.myshixun.shixun
shixun_tomcat = edu_setting('cloud_bridge')
uri = "#{shixun_tomcat}/bridge/jupyter/get"
tpiID = game.myshixun.id
params = {tpiID: tpiID, :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
repo_save_path = game.myshixun.repo_save_path
render plain: "https://#{res['port']}.jupyter.educoder.net/notebooks/data/workspace/myshixun_#{tpiID}/#{repo_save_path}/01.ipynb"
end
def test
render plain: 'test'
end
def save()
# 保存01.ipy
author_name = current_user.real_name
author_email = current_user.git_mail
message = "User submitted"
#https://47526.jupyter.educoder.net/nbconvert/notebook/data/workspace/myshixun_570461/f2ef5p798r20191210163135/01.ipynb?download=true
src_url = URI("https://47519.jupyter.educoder.net/nbconvert/notebook/data/workspace/myshixun_tpm3575/01.ipynb?download=true")
response = Net::HTTP.get_response(src_url)
if response.code.to_i != 200
raise("获取文件内容失败:#{response.code}")
end
content = response.body
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)
render plain: 'save: #{c.size}'
end
private
def shixun
@shixun = Shixun.find(3575)
end
end

@ -3,6 +3,7 @@ class ShixunsController < ApplicationController
include ApplicationHelper
include ElasticsearchAble
include CoursesHelper
include JupyterService
before_action :require_login, :check_auth, except: [:download_file, :index, :menus, :show, :show_right, :ranking_list,
:discusses, :collaborators, :fork_list, :propaedeutics]
@ -24,6 +25,10 @@ class ShixunsController < ApplicationController
before_action :special_allowed, only: [:send_to_course, :search_user_courses]
helper_method :jupyter_url_with_shixun, :jupyter_port_with_shixun
## 获取课程列表
def index
@shixuns = current_laboratory.shixuns.unhidden
@ -364,76 +369,112 @@ class ShixunsController < ApplicationController
end
def create
# 评测脚本的一些操作
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
begin
@shixun = CreateShixunService.call(current_user, shixun_params, params)
rescue => e
logger_error("shixun_create_error: #{e.message}")
tip_exception("创建实训失败!")
end
end
ActiveRecord::Base.transaction do
# 保存jupyter到版本库
def update_jupyter
jupyter_save_with_shixun(@shixun, params[:jupyter_port])
end
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
@shixun.save!
# shixun_info关联ß
ShixunInfo.create!(shixun_id: @shixun.id, evaluate_script: shixun_script, description: params[:description])
# 实训的公开范围
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 }
ActiveRecord::Base.transaction do
@shixun.update_attributes(shixun_params)
@shixun.shixun_info.update_attributes(shixun_info_params)
# 镜像变动
@shixun.shixun_mirror_repositories.where.not(mirror_repository_id: old_mirror_ids).destroy_all
@shixun.shixun_mirror_repositories.create!(new_mirror_id)
# 镜像变动要更换服务配置
@shixun.shixun_service_configs.where.not(mirror_repository_id: old_mirror_ids).destroy_all
@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 current_user.admin_or_business?
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
ShixunSchool.create!(arr)
end
# 实训合作者
@shixun.shixun_members.create!(user_id: current_user.id, role: 1)
# 镜像-实训关联表
ShixunMirrorRepository.create!(:shixun_id => @shixun.id, :mirror_repository_id => main_type.to_i) if main_type.present?
# 实训主镜像服务配置
ShixunServiceConfig.create!(:shixun_id => @shixun.id, :mirror_repository_id => main_type.to_i)
if sub_type.present?
sub_type.each do |mirror|
ShixunMirrorRepository.create!(:shixun_id => @shixun.id, :mirror_repository_id => mirror)
# 实训子镜像服务配置
name = MirrorRepository.find_by(id: mirror).try(:name) #查看镜像是否有名称,如果没有名称就不用服务配置
ShixunServiceConfig.create!(:shixun_id => @shixun.id, :mirror_repository_id => mirror) if name.present?
end
end
rescue => e
uid_logger_error(e.message)
tip_exception("基本信息更新失败:#{e.message}")
end
end
# 创建版本库
repo_path = repo_namespace(User.current.login, @shixun.identifier)
GitService.add_repository(repo_path: repo_path)
# todo: 为什么保存的时候要去除后面的.git呢??
@shixun.update_column(:repo_name, repo_path.split(".")[0])
# 实训权限设置
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
# 将实训标志为该云上实验室建立
Laboratory.current.laboratory_shixuns.create!(shixun: @shixun, ownership: true)
rescue Exception => e
uid_logger_error(e.message)
tip_exception("实训创建失败")
raise ActiveRecord::Rollback
# 实训学习页面设置
def update_learn_setting
begin
ActiveRecord::Base.transaction do
@shixun.update_attributes!(shixun_params)
end
rescue => e
uid_logger_error("实训学习页面设置失败--------#{e.message}")
tip_exception("实训学习页面设置失败")
end
end
# Jupyter数据集
def jupyter_data_sets
page = params[:page] || 1
limit = params[:limit] || 10
data_sets = @shixun.jupyter_data_sets
@data_count = data_sets.count
@data_sets= data_sets.page(page).per(limit)
end
def apply_shixun_mirror
form_params = params.permit(*%i[language runtime run_method attachment_id])
form = ApplyShixunMirrorForm.new(form_params)
@ -462,61 +503,6 @@ class ShixunsController < ApplicationController
tip_exception("申请失败")
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
@ -552,6 +538,8 @@ class ShixunsController < ApplicationController
# @evaluate_scirpt = @shixun.evaluate_script || "无"
end
# 获取脚本内容
def get_script_contents
mirrir_script = MirrorScript.find(params[:script_id])
@ -1021,10 +1009,9 @@ class ShixunsController < ApplicationController
private
def shixun_params
raise("实训名称不能为空") if params[:shixun][:name].blank?
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,
:hide_code, :forbid_copy, :vnc_evaluate, :code_edit_permission)
:hide_code, :forbid_copy, :vnc_evaluate, :code_edit_permission, :is_jupyter)
end
def validate_review_shixun_params
@ -1033,8 +1020,6 @@ private
end
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)
end

@ -204,7 +204,7 @@ class SubjectsController < ApplicationController
def add_shixun_to_stage
identifier = generate_identifier Shixun, 8
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)
# 创建长字段

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

@ -163,9 +163,12 @@ module ExportHelper
count_2 = common_homeworks.size
count_3 = group_homeworks.size
count_4 = tasks.size
all_user_ids = all_members.pluck(:user_id)
#实训作业
shixun_homeworks.each_with_index do |s,index|
all_student_works = s.score_student_works #该实训题的全部用户回答
all_student_works = s.score_student_works.select{|work| all_user_ids.include?(work.user_id)} #该实训题的全部用户回答
title_no = index.to_i + 1
student_work_to_xlsx(all_student_works,s)
shixun_work_display_name = format_sheet_name (title_no.to_s + "." + s.name).strip.first(30)
@ -175,7 +178,7 @@ module ExportHelper
#普通作业
common_homeworks.each_with_index do |c,index|
all_student_works = c.score_student_works #当前用户的对该作业的回答
all_student_works = c.score_student_works.select{|work| all_user_ids.include?(work.user_id)} #当前用户的对该作业的回答
title_no = count_1 + index.to_i + 1
student_work_to_xlsx(all_student_works,c)
@ -187,7 +190,7 @@ module ExportHelper
#分组作业
group_homeworks.each_with_index do |c,index|
all_student_works = c.score_student_works #当前用户的对该作业的回答
all_student_works = c.score_student_works.select{|work| all_user_ids.include?(work.user_id)} #当前用户的对该作业的回答
title_no = count_1 + count_2 + index.to_i + 1
student_work_to_xlsx(all_student_works,c)
work_name = format_sheet_name (title_no.to_s + "." + c.name).strip.first(30)
@ -197,7 +200,7 @@ module ExportHelper
#毕设任务
tasks.each_with_index do |c,index|
all_student_works = c.score_graduation_works #当前用户的对该作业的回答
all_student_works = c.score_graduation_works.select{|work| all_user_ids.include?(work.user_id)} #当前用户的对该作业的回答
title_no = count_1 + count_2 + count_3 + index.to_i + 1
graduation_work_to_xlsx(all_student_works,c,current_user)
work_name = format_sheet_name (title_no.to_s + "." + c.name).strip.first(30)
@ -207,7 +210,7 @@ module ExportHelper
#试卷的导出
exercises.each_with_index do |c,index|
all_student_works = c.score_exercise_users #当前用户的对该作业的回答
all_student_works = c.score_exercise_users.select{|work| all_user_ids.include?(work.user_id)} #当前用户的对该作业的回答
title_no = count_1 + count_2 + count_3 + count_4 + index.to_i + 1
get_export_users(c,course,all_student_works)
work_name = format_sheet_name (title_no.to_s + "." + c.exercise_name).strip.first(30)

@ -17,10 +17,17 @@ class SyncTrustieJob < ApplicationJob
"number": count
}
uri = URI.parse(url)
# http = Net::HTTP.new(uri.hostname, uri.port)
if api_host
http = Net::HTTP.new(uri.hostname, uri.port)
http.send_request('PUT', uri.path, sync_json.to_json, {'Content-Type' => 'application/json'})
Rails.logger.info("#######_________response__sync__end_____#########")
if api_host.include?("https://")
http.use_ssl = true
end
response = http.send_request('PUT', uri.path, sync_json.to_json, {'Content-Type' => 'application/json'})
Rails.logger.info("#######_________response__sync__end_____#########{response.body}")
end
end
end

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

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

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

@ -1,7 +1,7 @@
class ShixunInfo < ApplicationRecord
belongs_to :shixun
validates_uniqueness_of :shixun_id
validates_presence_of :shixun_id
validates_presence_of :shixun_id, :description
after_commit :create_diff_record

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

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

@ -0,0 +1,119 @@
#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}"
params = {tpiID: tpiID, :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']
return "https://#{res['port']}.jupyter.educoder.net/notebooks/data/workspace/myshixun_#{tpiID}/01.ipynb"
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 jupyter_url_with_game(game)
## 打开tpi
shixun = game.myshixun.shixun
if shixun.is_jupyter?
shixun_tomcat = edu_setting('cloud_bridge')
uri = "#{shixun_tomcat}/bridge/jupyter/get"
tpiID = game.myshixun.id
params = {tpiID: tpiID, :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
repo_save_path = game.myshixun.repo_save_path
"https://#{res['port']}.jupyter.educoder.net/notebooks/data/workspace/myshixun_#{tpiID}/#{repo_save_path}/01.ipynb"
end
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}"
#https://47526.jupyter.educoder.net/nbconvert/notebook/data/workspace/myshixun_570461/f2ef5p798r20191210163135/01.ipynb?download=true
src_url = URI("https://#{jupyter_port}.jupyter.educoder.net/nbconvert/notebook/data/workspace/myshixun_#{tpiID}/01.ipynb?download=true")
response = Net::HTTP.get_response(src_url)
if response.code.to_i != 200
raise("获取文件内容失败:#{response.code}")
end
content = response.body
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(game,jupyter_port)
author_name = current_user.real_name
author_email = current_user.git_mail
message = "User submitted"
tpiID = game.myshixun.id
repo_save_path = game.myshixun.repo_save_path
src_url = URI("https://#{jupyter_port}.jupyter.educoder.net/nbconvert/notebook/data/workspace/myshixun_#{tpiID}/#{repo_save_path}/01.ipynb?download=true")
response = Net::HTTP.get_response(src_url)
if response.code.to_i != 200
raise("获取文件内容失败:#{response.code}")
end
content = response.body
c = GitService.update_file(repo_path: game.myshixun.repo_path,
file_path: "01.ipynb",
message: message,
content: content,
author_name: author_name,
author_email: author_email)
return c.size
end
end

@ -0,0 +1,104 @@
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])
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
return shixun
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

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

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

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

@ -0,0 +1,10 @@
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)
end
end
json.data_sets_count @data_count

@ -2,6 +2,7 @@ json.shixun do
json.status @shixun.status
json.name @shixun.name
json.description @shixun.description
json.is_jupyter @shixun.is_jupyter
# 镜像大小类别
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)
end
end
json.jupyter_url jupyter_url(@shixun)
end

@ -3,3 +3,5 @@ json.identity User.current.shixun_identity(@shixun)
json.power @power
json.partial! 'shixuns/top', locals: { shixun: @shixun, current_myshixun: @current_myshixun }
json.secret_repository @shixun.shixun_secret_repository.present?
json.jupyter_url jupyter_url_with_shixun(@shixun)
json.jupyter_port jupyter_port_with_shixun(@shixun)

@ -7,3 +7,4 @@ json.status shixun.status
json.human_status shixun.human_status
json.challenges_count shixun.challenges_count
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
require_relative '../config/boot'
require 'rake'
Rake.application.run

@ -9,6 +9,16 @@ Rails.application.routes.draw do
get 'auth/qq/callback', to: 'oauth/qq#create'
get 'auth/failure', to: 'oauth/base#auth_failure'
resources :jupyters do
collection do
get :open
get :open1
get :test
get :save
end
end
resources :edu_settings
scope '/api' do
get 'home/index'
@ -35,6 +45,9 @@ Rails.application.routes.draw do
end
end
resources :hacks, path: :problems, param: :identifier do
collection do
get :unpulished_list
@ -229,6 +242,7 @@ Rails.application.routes.draw do
end
member do
post :update_jupyter
post :copy
get :propaedeutics
get :show_right
@ -261,6 +275,9 @@ Rails.application.routes.draw do
get :shixun_exec
post :review_shixun
get :review_newest_record
post :update_permission_setting
post :update_learn_setting
get :jupyter_data_sets
end
resources :challenges do
@ -744,7 +761,11 @@ Rails.application.routes.draw do
resources :poll_bank_questions
resources :attachments
resources :attachments do
collection do
delete :destroy_files
end
end
resources :schools 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,7 @@
class ModifyTypeForTestSets < ActiveRecord::Migration[5.2]
def change
change_column :test_sets, :input, :longtext
change_column :test_sets, :output, :longtext
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.

@ -541,12 +541,16 @@ class NewShixunModel extends Component{
></Checkbox>
{
this.props.type==='shixuns'?
(
item.is_jupyter===true?
<div className="myysljupyter fl ml15 mt3 intermediatecenter">
<p className="myysljupytertest">
Jupyter
</p>
</div>
:""
)
:""
}

@ -38,9 +38,9 @@ class VNCContainer extends Component {
}
shouldComponentUpdate () {
return false;
}
// shouldComponentUpdate () {
// return false;
// }
getSecondDrawerWidth = () => {
return $('#game_right_contents').width() - firstDrawerWidth
@ -337,11 +337,11 @@ class VNCContainer extends Component {
width={firstDrawerWidth}
closable={false}
onClose={this.onBottomDrawerClose}
visible={this.state.bottomDrawer}
visible={this.state.bottomDrawer===undefined?false:this.state.bottomDrawer}
className={'codeEvaluateDrawer'}
placement="bottom"
getContainer={false}
style={{ position: 'absolute', bottom: '25px', zIndex: 1 }}
style={{ position: 'absolute', bottom: '-25px', zIndex: 1 }}
afterVisibleChange={(visible) => {
if (visible) {
const canvas = $('.vncDisply canvas')[0]

@ -1,12 +1,13 @@
import React, { Component } from 'react';
import {getImageUrl} from 'educoder';
import {Modal,Input} from 'antd';
import {Modal,Input,Form,Radio} from 'antd';
class Addshixuns extends Component {
constructor(props) {
super(props);
this.state = {
shixunname:undefined,
shixunzero:false
shixunzero:false,
is_jupyter:"1"
}
}
@ -52,12 +53,22 @@ class Addshixuns extends Component {
})
return
}
this.props.Setaddshixuns(shixunname);
let is_jupyter=this.state.is_jupyter==="1"?false:true
this.props.Setaddshixuns(shixunname,is_jupyter);
this.props.modalCancel();
}
GrouponChange = e => {
this.setState({
is_jupyter: e.target.value,
});
};
render() {
const formItemLayout = {
labelCol: { span: 4 },
wrapperCol: { span: 14 },
};
return(
<Modal
className={this.props.className}
@ -81,6 +92,14 @@ class Addshixuns extends Component {
</style>:""}
<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">
<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} />

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

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

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

@ -98,6 +98,7 @@ class TPMBanner extends Component {
return -1;//不是ie浏览器
}
}
componentDidMount() {
let thiisie = this.IEVersion();
if (thiisie != -1) {
@ -110,6 +111,7 @@ class TPMBanner extends Component {
})
}
}
/*
* Fork
* */
@ -192,7 +194,8 @@ class TPMBanner extends Component {
params: {
page: 1,
limit: 10
}}).then((response) => {
}
}).then((response) => {
this.setState({
courses_count: response.data.courses_count,
course_list: response.data.course_list
@ -209,7 +212,8 @@ class TPMBanner extends Component {
params: {
page: 1,
limit: 10
}}).then((response) => {
}
}).then((response) => {
this.setState({
courses_count: response.data.courses_count,
course_list: response.data.course_list,
@ -234,7 +238,8 @@ class TPMBanner extends Component {
params: {
page: pageNumber,
limit: 10
}}).then((response) => {
}
}).then((response) => {
this.setState({
courses_count: response.data.courses_count,
course_list: response.data.course_list,
@ -379,7 +384,8 @@ class TPMBanner extends Component {
// message.success('重置成功,正在进入实训!');
// this.startshixunCombat();
}}
}
}
).catch((error) => {
this.setState({
startbtn: false,
@ -394,6 +400,79 @@ class TPMBanner extends Component {
//开始实战按钮
startshixunCombat = (id, reset) => {
if(this.props.shixunsDetails&&this.props.shixunsDetails.is_jupyter===true){
if (this.props.checkIfLogin() === false) {
this.props.showLoginDialog()
return
}
if (this.props.checkIfProfileCompleted() === false) {
this.setState({
AccountProfiletype: true
})
return
}
// if(this.props.checkIfProfessionalCertification()===false){
// this.setState({
// AccountProfiletype:true
// })
// return
// }
let {shixunsDetails} = this.props
if (shixunsDetails.shixun_status > 1) {
this.setState({
startbtn: true,
hidestartshixunsreplacevalue: ""
})
} else {
this.setState({
hidestartshixunsreplacevalue: ""
})
}
let url = "/shixuns/" + id + "/jupyter_exec.json";
if (reset) {
url += '?reset=' + reset
}
axios.get(url).then((response) => {
if (response.status === 200) {
if (response.data.status === -2) {
// this.resetshixunCombat(response.data.message);
this.setState({
startbtn: false,
shixunsreplace: true,
hidestartshixunsreplacevalue: response.data.message + ".json"
})
// this.shixunexec(response.data.message+".json")
} else if (response.data.status === -1) {
console.log(response)
} else if (response.data.status === -3) {
this.setState({
shixunsmessage: response.data.message,
startshixunCombattype: true,
startbtn: false
})
} else {
// let path="/tasks/"+response.data.game_identifier;
// this.props.history.push(path);
// this.context.router.history.push(path);
if (response.data.status != 401) {
window.location.href = "/tasks/" + response.data.identifier+`/jupyter`;
}
}
}
}).catch((error) => {
this.setState({
startbtn: false
})
});
}else{
if (this.props.checkIfLogin() === false) {
this.props.showLoginDialog()
return
@ -467,6 +546,9 @@ class TPMBanner extends Component {
});
}
}
tocertification = () => {
let {certi_url} = this.state;
this.setState({
@ -532,7 +614,8 @@ class TPMBanner extends Component {
hidestartshixunsreplacevalue,
Forkvisibletype,
AccountProfiletype,
isIE} = this.state;
isIE
} = this.state;
let {shixunsDetails, shixunId, star_info, star_infos} = this.props;
let challengeBtnTipText = '';
let challengeBtnText = '模拟实战';
@ -594,6 +677,9 @@ class TPMBanner extends Component {
return <Rating {...rest} value={myValue}/>;
};
console.log(this.props.shixunsDetails&&this.props.shixunsDetails.is_jupyter)
return (
shixunsDetails === undefined ? "" :
@ -663,12 +749,14 @@ class TPMBanner extends Component {
</li>
</ul>
<div className="pr fl" id="commentsStar" onMouseOver={()=>this.showonMouseOver()} onMouseOut={()=>this.hideonMouseOut()}>
<div className="pr fl" id="commentsStar" onMouseOver={() => this.showonMouseOver()}
onMouseOut={() => this.hideonMouseOut()}>
<div className={"color-grey-c ml15"} style={{color: "#Fff", textAlign: "center"}}>学员评分</div>
<div className="rateYo">
<MyRate allowHalf defaultValue={star_info[0]} disabled/>
</div>
<div id="ratePanel" className="showratePanel" style={{"width":"530px"}} onMouseOut={()=>this.hideonMouseOut()}>
<div id="ratePanel" className="showratePanel" style={{"width": "530px"}}
onMouseOut={() => this.hideonMouseOut()}>
<div className="pr">
<span className="rateTrangle"></span>
<div className="pr clearfix ratePanelContent" style={{height: '177px'}}>
@ -978,7 +1066,8 @@ class TPMBanner extends Component {
}
{this.props.identity < 8&&shixunsDetails.shixun_status != -1 ?<div className="fr user_default_btn user_blue_btn mr20"
{this.props.identity < 8 && shixunsDetails.shixun_status != -1 ?
<div className="fr user_default_btn user_blue_btn mr20"
style={{display: shixunsDetails.can_copy === false || shixunsDetails.can_copy === null ? "none" : "flex"}}>
<Tooltip placement="bottom" title={"基于这个实训修改形成新的实训"}>
<span className="flex1 edu-txt-center fl font-18"
@ -1046,7 +1135,8 @@ class TPMBanner extends Component {
</div>
<div className="alert alert-orange mb15 mt15 clearfix"
style={{display: shixunsDetails.shixun_status === 1 ? "block" : "none"}}
>正在等待管理员的审核在审核通过前可以随时撤销发布</div>
>正在等待管理员的审核在审核通过前可以随时撤销发布
</div>
</div>
);

@ -20,7 +20,7 @@ class TPMChallenge extends Component {
}
render() {
const { loadingContent, shixun, user, match,jupyterbool
const { loadingContent, shixun, user, match,jupyterbool,is_jupyter
} = this.props;
return (
<React.Fragment>
@ -32,13 +32,15 @@ class TPMChallenge extends Component {
user={user}
shixun={shixun}
{...this.props}
is_jupyter={this.props.is_jupyter}
></TPMNav>
{
jupyterbool===true?
is_jupyter===true?
<Challengesjupyter
{...this.props}
/>
: <Challenges
:
<Challenges
{...this.props}
/>
}

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

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

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

@ -1,12 +1,16 @@
import React, {Component} from 'react';
import {Redirect} from 'react-router';
import {List, Typography, Tag, Modal, Radio, Checkbox, Table, Pagination} from 'antd';
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 {getUploadActionUrl} from 'educoder';
import moment from 'moment';
const confirm = Modal.confirm;
class TPMDataset extends Component {
constructor(props) {
@ -21,10 +25,10 @@ class TPMDataset extends Component {
key: 'number',
align: 'left',
className: " font-14 wenjiantit",
width: '300px',
width: '220px',
render: (text, record) => (
<div>
文件名字
{record.title}
</div>
)
},
@ -34,10 +38,10 @@ class TPMDataset extends Component {
key: 'number',
align: 'center',
className: "edu-txt-center font-14 zuihoushijian",
width: '125px',
width: '150px',
render: (text, record) => (
<div>
2019-08-12 12:30
{record.timedata}
</div>
)
},
@ -49,7 +53,7 @@ class TPMDataset extends Component {
className: "edu-txt-center font-14 ",
render: (text, record) => (
<div>
张大大
{record.author}
</div>
)
},
@ -61,15 +65,21 @@ class TPMDataset extends Component {
className: "edu-txt-center font-14 ",
render: (text, record) => (
<div>
1.88kb
{record.filesize}
</div>
)
},
],
page: 1,
limit: 5,
limit: 10,
selectedRowKeys: [],
mylistansum:30,
collaboratorList:[],
fileList:[],
file:null,
datalist:[],
data_sets_count:0,
selectedRowKeysdata:[],
}
}
@ -79,11 +89,24 @@ class TPMDataset extends Component {
}
mysonChange = (e) => {
console.log(`全选checked = ${e.target.checked}`);
// 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({
selectedRowKeys: this.state.datas,
selectedRowKeysdata:mydata,
selectedRowKeys: datas,
})
// console.log(mydata);
// console.log(datas);
} else {
this.setState({
selectedRowKeys: [],
@ -93,7 +116,78 @@ class TPMDataset extends Component {
getdatas = () => {
let id=this.props.match.params.shixunId;
let collaborators=`/shixuns/${id}/jupyter_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,
});
}
}
}).catch((error)=>{
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,
});
}
}
}).catch((error)=>{
console.log(error)
});
}
@ -113,6 +207,7 @@ class TPMDataset extends Component {
this.setState({
page: pageNumber,
})
this.getdatastwo(pageNumber,10);
}
onSelectChange = (selectedRowKeys, selectedRows) => {
console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows);
@ -121,6 +216,17 @@ class TPMDataset extends Component {
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) => {
@ -129,9 +235,105 @@ class TPMDataset extends Component {
return className;
}
// 附件相关 START
handleChange = (info) => {
debugger
if(info.file.status === 'uploading' || info.file.status === 'done' || info.file.status === 'removed') {
let {fileList} = this.state;
if (info.file.status === 'uploading' || info.file.status === 'done' || info.file.status === 'removed') {
console.log("handleChange1");
// if(fileList.length===0){
let fileLists = info.fileList;
this.setState({
// fileList:appendFileSizeToUploadFileAll(fileList),
fileList: fileLists,
deleteisnot: false
});
this.getdatas();
// }
}
}
}
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(){
debugger
console.log("删除");
console.log(this.state.selectedRowKeysdata);
const url = `/attachments/destroy_files.json`;
axios.delete(url, {
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);
});
}
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);
});
}
render() {
const {tpmLoading, shixun, user, match} = this.props;
const {columns, datas, page, limit, selectedRowKeys,mylistansum} = this.state;
const {columns, datas, page, limit, selectedRowKeys,mylistansum,fileList,datalist,data_sets_count} = this.state;
const rowSelection = {
selectedRowKeys,
onChange: this.onSelectChange,
@ -140,6 +342,55 @@ class TPMDataset extends Component {
// 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,
data:{
attachtype: 2,
container_id:this.props.match.params.shixunId,
container_type: "Shixun",
},
multiple: true,
// https://github.com/ant-design/ant-design/issues/15505
// showUploadList={false},然后外部拿到 fileList 数组自行渲染列表。
// showUploadList: false,
action: `${getUploadActionUrl()}`,
onChange: this.handleChange,
onRemove: this.onAttachmentRemove,
beforeUpload: (file, fileList) => {
debugger
if (this.state.fileList.length >= 1) {
return false
}
// console.log('beforeUpload', file.name);
const isLt150M = file.size / 1024 / 1024 < 50;
if (!isLt150M) {
// this.props.showNotification(`文件大小必须小于50MB`);
notification.open(
{
message: '提示',
description:
'文件大小必须小于50MB',
}
)
}
if(this.state.file !== undefined){
console.log("763")
this.setState({
file:file
})
}else {
this.setState({
file:file
})
}
console.log("handleChange2");
return isLt150M;
},
}
return (
<React.Fragment>
<div className="tpmComment educontent clearfix mt30 mb80">
@ -151,17 +402,31 @@ class TPMDataset extends Component {
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 onChange={this.mysonChange}>全选</Checkbox></div>
<div className="tpmwidth">
<Checkbox onChange={this.mysonChange}>全选</Checkbox>
</div>
<div className="tpmwidth xaxisreverseorder">
<div className="deletebuttom intermediatecenter "><p className="deletebuttomtest">上传文件</p></div>
<style>
{
mylistansum>0?
`
.ant-upload-list{
display:none
}
`
}
</style>
<div className="deletebuttom intermediatecenter "> <Upload {...uploadProps}><p className="deletebuttomtest">
上传文件</p> </Upload></div>
{
data_sets_count>0?
<div
className={selectedRowKeys.length > 0 ? "deletebutomtextcode intermediatecenter mr21" : "deletebutom intermediatecenter mr21"}>
className={selectedRowKeys.length > 0 ? "deletebutomtextcode intermediatecenter mr21" : "deletebutom intermediatecenter mr21"} onClick={()=>this.deleteRemovedata()}>
<p className="deletebutomtext" >删除</p></div>
:""
}
@ -207,13 +472,17 @@ class TPMDataset extends Component {
border-right: 1px solid #eeeeee;
}
`}</style>
{mylistansum===0?
{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;
}
@ -221,12 +490,18 @@ class TPMDataset extends Component {
`
}
</style>
:""
}
<Table
columns={columns}
pagination={false}
className="mysjysltable4"
rowSelection={rowSelection}
rowClassName={this.rowClassName}
/>
</div>
:
<div className="edu-table edu-back-white ysltableowss">
<Table
dataSource={datas}
dataSource={datalist}
columns={columns}
pagination={false}
className="mysjysltable4"
@ -234,18 +509,20 @@ class TPMDataset extends Component {
rowClassName={this.rowClassName}
/>
</div>
}
{
mylistansum>5?
data_sets_count>=11?
<div className="edu-txt-center mt40 mb20">
<Pagination showQuickJumper current={page}
onChange={this.paginationonChanges} pageSize={limit}
total={mylistansum}
total={data_sets_count}
></Pagination>
</div>
:""
}
{ mylistansum===0?
{ data_sets_count===0?
<NoneData style={{width: '100%'}}></NoneData>:""
}

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

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

@ -20,7 +20,9 @@ import TPMRepositoryComponent from './TPMRepositoryComponent';
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 TPMPropaedeuticsComponent from './TPMPropaedeuticsComponent';
@ -29,6 +31,7 @@ import TPMCollaboratorsComponent from './TPMCollaboratorsContainer';
import Audit_situationComponent from './Audit_situationComponent';
import TPMDataset from './TPMDataset';
import '../page/tpiPage.css'
import TPMNav from "./component/TPMNav";
const $ = window.$
//任务
@ -143,6 +146,7 @@ class TPMIndex extends Component {
TPMRightSectionData:undefined,
PropaedeuticsList: undefined,
tpmindexjupyterbool:false,
is_jupyter:false,
}
}
@ -193,7 +197,7 @@ class TPMIndex extends Component {
propaedeutics:response.data.propaedeutics,
status: response.data.shixun_status,
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) => {
@ -205,7 +209,8 @@ class TPMIndex extends Component {
power: undefined,
identity: undefined,
status: undefined,
propaedeutics:undefined
propaedeutics:undefined,
is_jupyter:false,
});
});
@ -274,6 +279,7 @@ class TPMIndex extends Component {
let url = window.location.href;
let flag = url.indexOf("add_file")>-1;
console.log(this.state.is_jupyter);
return (
<div className="newMain clearfix">
{/*头部*/}
@ -295,16 +301,16 @@ class TPMIndex extends Component {
<Switch {...this.props}>
<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 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 exact path="/shixuns/:shixunId/challenges" render={
(props) => (<TPMChallengeComponent {...this.props} jupyterbool={true} {...this.state} {...props}
(props) => (<TPMChallengeComponent {...this.props} {...this.state} {...props} is_jupyter={this.state.is_jupyter}
/>)
}></Route>
@ -314,24 +320,24 @@ class TPMIndex extends Component {
}></Route>
<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 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 exact path="/shixuns/:shixunId/propaedeutics" component={TPMPropaedeuticsComponent}></Route> */}
<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 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>
@ -340,7 +346,7 @@ class TPMIndex extends Component {
<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)}
setSearchValue={this.setSearchValue}
setHotLabelIndex={this.setHotLabelIndex}
@ -354,17 +360,17 @@ class TPMIndex extends Component {
{/*实训项目条目塞选*/}
<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}
(props) => (<TPMDataset {...this.props} {...this.state} {...props} is_jupyter={this.state.is_jupyter}
/>)
}></Route>
<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>
@ -415,7 +421,7 @@ class TPMIndex extends Component {
}></Route>
<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>

@ -23,7 +23,7 @@ const versionNum = '0001';
// let _url_origin = getUrl()
let _url_origin='';
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`;

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

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

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

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

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

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

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

@ -33,7 +33,7 @@ class TPMShixunDiscussContainer extends Component {
{...this.state}
user={user}
aboutFocus={this.props.aboutFocus}
is_jupyter={this.props.is_jupyter}
>
</TPMShixunDiscuss>
}

@ -1,13 +1,56 @@
import React, { Component } from 'react';
import {Input, Select, Radio, Checkbox, Popconfirm, message, Modal,Icon,DatePicker,Breadcrumb,Upload,Button,notification, Tooltip,Tabs} from 'antd';
import MonacoEditor from 'react-monaco-editor';
//MonacoDiffEditor 对比模式
import {Input, Select, Radio, Checkbox, Popconfirm, message, Modal,Icon,DatePicker,Breadcrumb,Upload,Button,notification, Tooltip} from 'antd';
// import "antd/dist/antd.css";
import locale from 'antd/lib/date-picker/locale/zh_CN';
import moment from 'moment';
import axios from 'axios';
import './css/TPMsettings.css';
import { getImageUrl, toPath, getUrl ,appendFileSizeToUploadFileAll, getUploadActionUrl} from 'educoder';
import {handleDateStrings} from "./oldTPMsettings";
let origin = getUrl();
let path = getUrl("/editormd/lib/")
const $ = window.$;
let timeout;
let currentValue;
const Option = Select.Option;
const RadioGroup = Radio.Group;
const confirm = Modal.confirm;
function range(start, end) {
const result = [];
for (let i = start; i < end; i++) {
result.push(i);
}
return result;
}
function disabledDateTime() {
return {
// disabledHours: () => range(0, 24).splice(4, 20),
disabledMinutes: () => range(1, 30).concat(range(31, 60)),
// disabledSeconds: () => [0, 60],
};
}
function disabledDate(current) {
return current && current < moment().endOf('day').subtract(1, 'days');
}
export default class Shixuninformation extends Component {
@ -18,13 +61,115 @@ export default class Shixuninformation extends Component {
}
}
onChangeTimePicker =(value, dateString)=> {
this.setState({
opening_time: dateString=== ""?"":moment(handleDateStrings(dateString))
})
}
render() {
let {can_copy}=this.state;
let options;
if (this.props.departmentslist != undefined) {
options = this.props.departmentslist.map((d, k) => {
return (
<Option key={d} id={k}>{d}</Option>
)
})
}
return (
<div>
1111
<div className="educontent mb50 edu-back-white padding10-20">
<div className="clearfix ml30">
<span className="color-grey-6 mt5 fl" style={{minWidth: '95px'}}>复制:</span>
<span className="fl mt5">
<Checkbox checked={this.props.data&&this.props.data.shixun.can_copy === undefined ? false : this.props.data&&this.props.data.shixun.can_copy} onChange={this.props.data&&this.props.data.shixun.can_copy}></Checkbox>
<label style={{top:'6px'}} className="color-grey-9 ml10">(勾选则允许已认证的教师复制该实训)</label>
</span>
</div>
<div className="edu-back-white mb10 padding40-20" style={{display:this.props.identity===1?"block":this.props.data&&this.props.data.shixun.status===2&&this.props.data&&this.props.data.shixun.use_scope===0||this.props.data&&this.props.data.shixun.status===1&&this.props.data&&this.props.data.shixun.use_scope===0?"none":"block"}}>
<p className="color-grey-6 font-16 mb30">公开程度</p>
<RadioGroup onChange={this.SelectOpenpublic} value={this.props.data&&this.props.data.use_scope}>
<Radio className="radioStyle" value={0}><span>对所有公开</span> <span className="color-grey-9">()</span></Radio>
<Radio className="radioStyle" value={1}><span>对指定单位公开</span> <span className="color-grey-9">()</span></Radio>
</RadioGroup>
<div className="clearfix none" id="unit-all" style={{display: this.props.scopetype === false ? 'none' : 'block'}}>
<div className="fl ml25">
<div className="fl" id="unit-input-part" style={{width:'100%'}}>
<div id="person-unit" className="fl pr mr10">
<div className="shixunScopeInput fl" >
<Select
style={{width:'200px'}}
placeholder="请输入并选择单位名称"
onChange={(value)=>this.shixunScopeInput(value)}
onSearch={this.shixunHandleSearch}
showSearch
defaultActiveFirstOption={false}
showArrow={false}
filterOption={false}
notFoundContent={null}
className={this.props.scope_partmenttype===true?"bor-red":""}
>
{options}
</Select>
</div>
<span className="color-grey-9">(搜索并选中添加单位名称)</span>
</div>
</div>
<div style={{width:'100%'}}>
<div className="mt20 clearfix" id="task_tag_content">
{
this.props.scope_partment===undefined?"":this.props.scope_partment.map((item,key)=>{
return(
<li className="task_tag_span" key={key}><span>{item}</span>
<a style={{ color: 'rgba(0,0,0,.25)' }}
onClick={(key)=>this.deleteScopeInput(key)}
>
{this.props.identity===1?"x":this.state.status===2&&this.props.scope_partment===this.props.scope_partments||this.state.status===1&&this.props.scope_partment===this.props.scope_partments?"":"×"}
</a>
</li>
)
})
}
</div>
</div>
<span className={this.props.scope_partmenttype===true?"color-orange ml20 fl":"color-orange ml20 fl none"} id="public_unit_notice">
<i className="fa fa-exclamation-circle mr3"></i>
请选择需要公开的单位
</span>
<div className="clearfix mt20 ml30">
<span className="color-grey-6 mt5 fl" style={{minWidth: '95px'}}>开启时间:</span>
<span className="fl mt5">
<DatePicker
showToday={false}
showTime={{ format: 'HH:mm' }}
format="YYYY-MM-DD HH:mm"
width={178}
locale={locale}
disabledTime={disabledDateTime}
disabledDate={disabledDate}
placeholder="请选择开启时间"
value={this.props.shixun.opening_time===null||this.props.shixun.opening_time===""?"":moment(this.props.shixun.opening_time, dateFormat)}
onChange={this.onChangeTimePicker}
dropdownClassName="hideDisable"
/>
<label style={{top:'6px'}} className="color-grey-9 ml10" >为空则学员在实训发布后能随时开启实训挑战否则学员在开启时间后才能开启实训挑战</label>
</span>
</div>
</div>
</div>
</div>
</div>
);
}

@ -23,7 +23,7 @@ export default class Shixuninformation extends Component {
render() {
return (
<div>
<div className="educontent mb50 edu-back-white padding10-20">
1111
</div>
);

@ -25,14 +25,15 @@ import axios from 'axios';
import TPMMDEditor from "../challengesnew/TPMMDEditor";
import Bottomsubmit from "../../modals/Bottomsubmit";
import {getImageUrl, toPath, getUrl, appendFileSizeToUploadFileAll, getUploadActionUrl} from 'educoder';
import {TPMIndexHOC} from "../TPMIndexHOC";
import {getUploadActionUrl} from 'educoder';
import './css/TPMsettings.css';
import '../newshixuns/css/Newshixuns.css';
const Option = Select.Option;
class Shixuninformation extends Component {
constructor(props) {
@ -42,9 +43,37 @@ class Shixuninformation extends Component {
NAME_COUNT: 60,
shixunmemoMDvalue: "",
language: "java",
testscripttiptype:false,
shixunName:'',
trainee:undefined,
choice_small_type:[]
}
}
componentDidMount() {
}
componentDidUpdate(prevProps, prevState) {
if(prevProps.data!=this.props.data){
console.log(this.props.data)
if(this.props.data){
this.setState({
shixunName:this.props.data.shixun.name,
trainee:this.props.data.shixun.trainee,
choice_main_type:this.props.data.shixun.choice_main_type,
choice_small_type:this.props.data.shixun.choice_small_type,
choice_standard_scripts:this.props.data.shixun.choice_standard_scripts,
shixunmemoMDvalue:this.props.data.shixun.evaluate_script,
})
this.props.form.setFieldsValue({
name:this.props.data.shixun.name,
})
this.contentMdRef.current.setValue(this.props.data.shixun.description);
}
}
}
getshixunmemoMDvalue = (value, e) => {
@ -53,12 +82,246 @@ class Shixuninformation extends Component {
})
}
testscripttip=(val)=>{
if(val===0){
this.setState({
testscripttiptype:true
})
}else if(val===1){
this.setState({
testscripttiptype:false
})
}
}
post_apply = () => {
this.setState({
postapplyvisible: true
})
}
sendhideModaly = () => {
this.setState({
postapplyvisible: false,
})
if (this.state.file !== undefined) {
// this.deleteAttachment(this.state.file);
this.setState({
file: undefined,
deleteisnot: true,
language: "",
runtime: "",
run_method: "",
fileList: []
})
} else {
this.setState({
file: undefined,
deleteisnot: true,
language: "",
runtime: "",
run_method: "",
fileList: []
})
}
}
sendsure_apply = () => {
let {language, runtime, run_method} = this.state;
if (!language || language === "") {
// this.props.showNotification(`请填写该镜像是基于什么语言`);
this.setState({
languagewritetype: true
})
return
}
if (!runtime || runtime === "") {
// this.props.showNotification(`请填写该镜像是基于什么语言系统环境`);
this.setState({
systemenvironmenttype: true
})
return;
}
if (!run_method || run_method === "") {
// this.props.showNotification(`请填写该镜像中测试代码运行方式`);
this.setState({
testcoderunmodetype: true
})
return;
}
var attachment_ids = undefined;
if (this.state.fileList) {
attachment_ids = this.state.fileList.map(item => {
return item.response ? item.response.id : item.id
})
}
if (attachment_ids === undefined || attachment_ids.length === 0) {
this.setState({
attachmentidstype: true
})
return;
}
var data = {
language: language,
runtime: runtime,
run_method: run_method,
attachment_id: attachment_ids[0],
}
var url = `/shixuns/apply_shixun_mirror.json`;
axios.post(url, data
).then((response) => {
try {
if (response.data) {
if (this.state.file !== undefined) {
this.setState({
file: undefined,
deleteisnot: true,
language: "",
runtime: "",
run_method: "",
fileList: []
})
} else {
this.setState({
file: undefined,
deleteisnot: true,
language: "",
runtime: "",
run_method: "",
fileList: []
})
}
notification.open(
{
message: '提示',
description:
'提交成功!',
}
)
this.sendhideModaly()
}
} catch (e) {
}
})
}
bigClass = (value) => {
let list=[]
list.push(this.props.data.shixun.choice_main_type)
this.props.data.shixun.choice_small_type.map((item,key)=>{
list.push(item)
})
let newshixun_service_configs=this.props.data.shixun.shixun_service_configs;
let newshixun_service_configsagin=[];
newshixun_service_configs.map((item,key)=>{
list.map((its,index)=>{
if(item.mirror_repository_id===its){
newshixun_service_configsagin.push(item)
}
})
})
this.props.data.shixun.main_type.some((item,key)=> {
if (item.id === value) {
newshixun_service_configsagin[0]={
mirror_repository_id:value,
name:item.type_name,
cpu_limit:1,
lower_cpu_limit:0.1,
memory_limit:1024,
request_limit:10
}
return true
}
}
)
let url = `/shixuns/get_mirror_script.json?mirror_id=`+value;
axios.get(url).then((response) => {
if (response.status === 200) {
// console.log(response.data)
this.setState({
choice_main_type: value,
standard_scripts:response.data,
choice_standard_scripts:null,
shixun_service_configs:newshixun_service_configsagin,
shixun_service_configlist:newshixun_service_configsagin,
})
}
}).catch((error) => {
console.log(error)
});
}
littleClass = (value) => {
let newshixun_service_configs=this.props.data.shixun.shixun_service_configs;
let newchoice_small_type=this.props.data.shixun.choice_small_type;
let list=[]
list.push(this.props.data.shixun.choice_main_type)
newchoice_small_type.map((item,key)=>{
list.push(item)
})
let newshixun_service_configsagin=[]
newshixun_service_configs.map((item,key)=>{
list.map((its,index)=>{
if(item.mirror_repository_id===its){
newshixun_service_configsagin.push(item)
}
})
})
this.props.data.shixun.small_type.some((items,keys)=> {
if (items.id === value) {
newshixun_service_configsagin.push({
mirror_repository_id:value,
name:items.type_name,
cpu_limit:1,
lower_cpu_limit:0.1,
memory_limit:1024,
request_limit:10
})
return true
}
}
)
newchoice_small_type.push(value)
this.setState({
choice_small_type: newchoice_small_type,
shixun_service_configs:newshixun_service_configsagin,
shixun_service_configlist:newshixun_service_configsagin,
})
}
render() {
let operateauthority=this.props.identity===1?true:this.props.identity<5&&this.props.data.shixun.status==0?true:false;
console.log(operateauthority)
const {getFieldDecorator} = this.props.form;
const {newshixunlist, languagewrite, systemenvironment, testcoderunmode, fileList, postapplytitle, postapplyvisible, shixunmemoMDvalue} = this.state;
const {languagewrite, systemenvironment, testcoderunmode, fileList, choice_standard_scripts, postapplyvisible, shixunmemoMDvalue} = this.state;
console.log("1222")
console.log(choice_standard_scripts)
const {shixun_service_configs}=this.props;
let operateauthority = this.props.identity === 1 ? true : this.props.identity < 5 && this.state.status == 0 ? true : false;
const uploadProps = {
width: 600,
fileList,
@ -102,9 +365,14 @@ class Shixuninformation extends Component {
return isLt150M;
},
}
return (
<div>
<Form onSubmit={this.handleSubmit}>
<div className="educontent mb50 edu-back-white padding10-20">
<Form>
<Form.Item
label="名称"
style={{"borderBottom": 'none'}}
@ -145,13 +413,14 @@ class Shixuninformation extends Component {
style={{"borderBottom": 'none'}}
className="chooseDes pr"
>
{getFieldDecorator('select', {
{getFieldDecorator('trainee', {
rules: [{required: true, message: '请选择难易度'}],
})(
<div className="with15 fl pr">
<Select placeholder="请选择难易度"
style={{width: 180}}
onChange={this.Selectthestudent}
value={this.state.trainee}
>
<Option value={1}>初级</Option>
<Option value={2}>中级</Option>
@ -175,18 +444,20 @@ class Shixuninformation extends Component {
rules: [{required: true, message: '请选择主类别'}],
})(
<div className="width100 fl mr20">
<Select placeholder="请选择主类别"
<Select placeholder="请选择主类别" value={this.state.choice_main_type === "" ? undefined : this.state.choice_main_type}
style={{width: 180}}
onChange={this.selectleft}
defaultOpen={false}
onChange={operateauthority?this.bigClass:""}
optionFilterProp="children"
filterOption={(input, option) =>
option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
}
>
{
newshixunlist === undefined ? "" : newshixunlist.main_type.map((item, key) => {
this.props.data === undefined ? "" : this.props.data.shixun.main_type.map((item, key) => {
return (
<Option value={item.id} key={key} >
<Tooltip placement="right"
title={item.description === "" ? "无描述" : item.description}>
<Tooltip placement="right" title={item.description=== ""?"无描述":item.description} >
{item.type_name}
</Tooltip>
</Option>
@ -211,16 +482,20 @@ class Shixuninformation extends Component {
})(
<div className=" fl pr mr20">
<Select placeholder="请选择小类别"
style={{width: 180}}
mode="multiple"
onChange={this.selectright}
value={this.state.choice_small_type.length===0||this.state.choice_small_type[0]===""||this.state.choice_small_type===[]?undefined:this.state.choice_small_type}
style={{width: 180,height:30}}
disabled={operateauthority?false:true}
onDeselect={operateauthority?this.Deselectlittle:""}
onSelect={operateauthority?this.littleClass:""}
defaultOpen={false}
>
{
newshixunlist === undefined ? "" : newshixunlist.small_type.map((item, key) => {
this.props.data === undefined ? "" : this.props.data.shixun.small_type.map((item, key) => {
return(
<Option value={item.id} key={key}>
<Tooltip placement="right"
title={item.description === "" ? "无描述" : item.description}>
<Tooltip placement="right" title={item.description=== ""?"无描述":item.description} >
{item.type_name}
</Tooltip>
</Option>
@ -255,27 +530,70 @@ class Shixuninformation extends Component {
style={{"borderBottom": 'none'}}
className="chooseDes pr"
>
{getFieldDecorator('select', {
{getFieldDecorator('select123', {
rules: [{required: true, message: '请选择评测脚本'}],
})(
<div className="with15 fl pr">
<Select placeholder="请选择评测脚本"
<Select placeholder="请选择选择脚本"
style={{width: 180}}
onChange={this.Selectthestudent}
>
<Option value={1}>初级</Option>
<Option value={2}>中级</Option>
<Option value={3}>中高级</Option>
<Option value={4}>高级</Option>
</Select>
className="fl"
disabled={operateauthority?false:true}
value={choice_standard_scripts === undefined||choice_standard_scripts === null ? undefined :choice_standard_scripts.id===undefined?choice_standard_scripts:choice_standard_scripts.id}
onChange={operateauthority?this.SelectScput:""}>
{
this.props.data === undefined ? "" : this.props.data.shixun.standard_scripts.map((item, key) => {
return (
<Option value={parseInt(item.id)} name={item.scptname} key={key}>{item.scptname}</Option>
)
})
}
</Select>
</div>
)}
<span className="fl ml20 color-blue">
使用自定义脚本
<span className={"color-grey ml10"}>
<span className={"color-grey ml10"} onClick={()=>this.testscripttip(0)}>
<Icon type="exclamation-circle"/>
</span>
<div className="invite-tip clearfix none " id="test_script_tip"
style={{top: '33px', left: '-15px',width: '450px',zIndex: '10',display: this.state.testscripttiptype===true?'block':"none"}}>
<style>
{
`
.top-black-trangle{
right: auto;
left: 0px;
}
`
}
</style>
<span className="top-black-trangle"></span>
<div className="padding20 invitecontent clearfix">
<p className="font-12 edu-txt-left">
使用自定义模板平台无法自动更新脚本请在关卡创建完后手动更新脚本中的必填参<br/>
数和以下2个数组元素<br/>
challengeProgramNames<br/>
sourceClassNames<br/><br/>
示例有2个关卡的实训<br/><br/>
各关卡的待编译文件为<br/>
src/step1/HelloWorld.java<br/>
src/step2/Other.java<br/><br/>
各关卡的编译后生成的执行文件为<br/>
step1.HelloWorld<br/>
step2.Other<br/><br/>
则数组元素更新如下<br/>
challengeProgramNames=("src/step1/<br/>
HelloWorld.java" "src/step2/Other.java")<br/>
sourceClassNames=("step1.HelloWorld<br/>
" "step2.Other")<br/><br/>
其它参数可按实际需求定制
</p>
</div>
<p className="inviteTipbtn with100 fl">
<a onClick={()=>this.testscripttip(1)}>知道了</a>
</p>
</div>
</span>
</Form.Item>
@ -287,7 +605,7 @@ class Shixuninformation extends Component {
<div className="fl" style={{border: '1px solid #ccc'}}>
<MonacoEditor
height="450"
width="1100"
width="1150"
language={this.state.language}
value={shixunmemoMDvalue}
options={{
@ -299,12 +617,12 @@ class Shixuninformation extends Component {
</div>
</div>
</div>
</Form>
<Form.Item label="私密版本库:">
<span className="ant-form-text"><Checkbox>若需要对学员隐藏部分版本库内容时请选中选中即启用私密版本库请将需要对学员隐藏的文件存储在私密版本库</Checkbox></span>
</Form.Item>
</Form>
<span className="ant-form-text mt20" >私密版本库
<Checkbox>若需要对学员隐藏部分版本库内容时请选中选中即启用私密版本库请将需要对学员隐藏的文件存储在私密版本库</Checkbox>
</span>
{this.props.identity<3?<div className="edu-back-white padding40-20 mb20">
<p className="color-grey-6 font-16 mb30">服务配置</p>
@ -446,30 +764,10 @@ class Shixuninformation extends Component {
</div>
{/*</Form>*/}
</Modal>
<Modal
keyboard={false}
title="提示"
visible={postapplytitle}
closable={false}
footer={null}
>
<div>
<div className="task-popup-content"><p
className="task-popup-text-center font-16"><span
className="font-17 mt10">新建申请已提交请等待管理员的审核</span></p>
<li className="font-14 mt15 color-grey-6 edu-txt-center">我们将在1-2个工作日内与您联系
</li>
</div>
<div className="task-popup-OK clearfix">
<a className="task-btn task-btn-orange"
onClick={this.yeshidemodel}>知道啦</a>
</div>
<Bottomsubmit url={`/shixuns/${this.props.match.params.shixunId}/challenges`} onSubmits={ this.sendsure_apply}/>
</div>
</Modal>
</div>
);
}
}

@ -1,20 +1,7 @@
import React, {Component} from 'react';
import {
Input,
Select,
Radio,
Checkbox,
Popconfirm,
message,
Modal,
Icon,
DatePicker,
Breadcrumb,
Upload,
Button,
notification,
Tooltip,
Tabs
} from 'antd';
@ -24,17 +11,10 @@ import Configuration from './Configuration';
import LearningSettings from './LearningSettings';
import Bottomsubmit from "../../modals/Bottomsubmit";
import moment from 'moment';
import axios from 'axios';
import './css/TPMsettings.css';
import {getImageUrl, toPath, getUrl, appendFileSizeToUploadFileAll, getUploadActionUrl} from 'educoder';
const {TabPane} = Tabs;
// 处理整点 半点
@ -62,51 +42,8 @@ export default class TPMsettings extends Component {
// alert(response.data.shixun.choice_standard_scripts)
if (response.status === 200) {
this.setState({
shixunsID: id,
settingsData: response.data,
webssh: response.data.shixun.webssh,
use_scope: response.data.shixun.use_scope,
shixunsstatus: response.data.shixun.status,
exec_time: response.data.shixun.exec_time,
trainee: response.data.shixun.trainee,
can_copy: response.data.shixun.can_copy,
task_pass: response.data.shixun.task_pass,
test_set_permission: response.data.shixun.test_set_permission,
hide_code: response.data.shixun.hide_code,
code_edit_permission: response.data.shixun.code_edit_permission,
code_hidden: response.data.shixun.code_hidden,
is_secret_repository: response.data.shixun.is_secret_repository,
init_is_secret_repository: response.data.shixun.is_secret_repository,
forbid_copy: response.data.shixun.forbid_copy,
vnc: response.data.shixun.vnc,
vnc_evaluate: response.data.shixun.vnc_evaluate,
name: response.data.shixun.name,
scope_partment: response.data.shixun.scope_partment,
description: response.data.shixun.description,
evaluate_script: response.data.shixun.evaluate_script,
choice_main_type: response.data.shixun.choice_main_type,
choice_small_type: response.data.shixun.choice_small_type,
choice_standard_scripts: response.data.shixun.choice_standard_scripts,
standard_scripts: response.data.shixun.standard_scripts,
multi_webssh: response.data.shixun.multi_webssh,
status: response.data.shixun.status,
opening_time: response.data.shixun.opening_time,
newuse_scope: response.data.shixun.use_scope,
scope_partments: response.data.shixun.scope_partment.length,
shixunmemoMDvalue: response.data.shixun.evaluate_script,
shixun_service_configs: response.data.shixun.shixun_service_configs,
shixun_service_configlist: response.data.shixun.shixun_service_configs,
data:response.data
})
// if(response.data.status===403){
// message: "您没有权限进行该操作"
// this.setState({
// :true
// message403:response.data.message
// })
// }
if (response.data.shixun.multi_webssh === true) {
this.setState({
SelectTheCommandtype: true
@ -121,24 +58,6 @@ export default class TPMsettings extends Component {
scopetype: true
})
}
// console.log(response.data.shixun.description)
// console.log(response.data.shixun.evaluate_script)
// console.log(response.data.shixun.description)
// this.props.identity<4&&this.props.status==0||this.props.identity===1&&this.props.status==2
// this.evaluate_scriptMD(response.data.shixun.evaluate_script, "shixunmemoMD");
// this.descriptionMD(response.data.shixun.description, "shixundescription");
// this.bigClass()
// if (response.data.shixun.status === 2) {
//
// } else if (response.data.shixun.status === 1) {
// this.props.showSnackbar("这个实训已发布不能修改!");
// } else if (response.data.shixun.status === 3) {
// this.props.showSnackbar("这个实训已关闭不能修改!");
// }
}
});
@ -164,11 +83,23 @@ export default class TPMsettings extends Component {
render() {
return (
<div>
<div className="educontent mt30 mb50 edu-back-white padding10-20">
<div>
<style>
{
`
.ant-tabs-bar{
width: 1200px;
margin: 0 auto;
background: #fff;
border-bottom: 0px;
margin-top: 30px!important;
}
`
}
</style>
<Tabs tabBarExtraContent={
<div className={"mb20"}>
<div className={"mb20 mr20"}>
<Button type="primary" ghost className={"Permanentban"}>
永久关闭
</Button>
@ -198,9 +129,6 @@ export default class TPMsettings extends Component {
</Tabs>
</div>
<Bottomsubmit/>
</div>
);
}
}

@ -5,7 +5,7 @@ import { BrowserRouter as Router, Route, Link } from "react-router-dom";
class TPMNav extends Component {
render() {
const { user, match, shixun, secret_repository } = this.props;
const { user, match, shixun, secret_repository,is_jupyter} = this.props;
let isAdminOrCreator = false;
if (user) {
isAdminOrCreator = user.admin || user.manager
@ -15,6 +15,8 @@ class TPMNav extends Component {
// console.log(this.props.propaedeutics)
const challengesPath = `/shixuns/${shixunId}/challenges`;
// console.log(match.path)
console.log("TPMNavTPMNavTPMNav");
console.log(is_jupyter);
return (
<div className="bor-bottom-greyE clearfix pl20 pr20 pt40 pb20 edu-back-white challengeNav">
<Link
@ -28,8 +30,15 @@ class TPMNav extends Component {
>背景知识</Link>
}
{ this.props.identity >4||this.props.identity===undefined ?"":<Link to={`/shixuns/${shixunId}/repository`}
className={`${match.url.indexOf('/repository') != -1 ? 'active' : ''} fl mr40`}>版本库</Link>}
{ this.props.identity >4||this.props.identity===undefined ?"":
(this.props.is_jupyter===false?
<Link to={`/shixuns/${shixunId}/repository`}
className={`${match.url.indexOf('/repository') != -1 ? 'active' : ''} fl mr40`}>版本库</Link>
:"")
}
{this.props.identity >4||this.props.identity===undefined ?"": secret_repository && <Link to={`/shixuns/${shixunId}/secret_repository`}
className={`${match.url.indexOf('secret_repository') != -1 ? 'active' : ''} fl mr40`}>私密版本库</Link>}
@ -37,8 +46,13 @@ class TPMNav extends Component {
className={`${match.url.indexOf('collaborators') != -1 ? 'active' : ''} fl mr40`}>合作者</Link>
{/*jupyter*/}
{
this.props.is_jupyter===true?
<Link to={`/shixuns/${shixunId}/dataset`}
className={`${match.url.indexOf('dataset') != -1 ? 'active' : ''} fl mr40`}>数据集</Link>
:""
}
<Link to={`/shixuns/${shixunId}/shixun_discuss`}
className={`${match.url.indexOf('shixun_discuss') != -1 ? 'active' : ''} fl mr40`}>评论</Link>

@ -4,7 +4,7 @@ import {TPMIndexHOC} from '../TPMIndexHOC';
import {SnackbarHOC} from 'educoder';
import {Select, Radio, Input, Modal, Button, Form, Tooltip, Upload, Icon} from 'antd';
import {Select, Radio, Input, Modal, Button, Form, Tooltip, Upload, Icon,notification} from 'antd';
import axios from 'axios';
@ -31,6 +31,7 @@ class Newshixuns extends Component {
runtime: undefined,
run_method: undefined,
postapplyvisible: undefined,
fileList: [],
}
}
@ -92,21 +93,19 @@ class Newshixuns extends Component {
if (!err) {
console.log('Received values of form: ', values);
let Url = `/api/shixuns.json`;
let Url = `/shixuns.json`;
axios.post(Url, {
description: mdContnet,
main_type: values.main_type,
is_jupyter: values.is_jupyter === "2" ? true : false,
sub_type: values.sub_type,
shixun: {
name: values.name,
trainee: values.select,
task_pass: 1
is_jupyter: values.is_jupyter === "2" ? true : false,
}
}
).then((response) => {
if (response.status === 200) {
debugger
window.location.href = "/shixuns/" + response.data.shixun_identifier + "/challenges";
// window.open("/shixuns/"+response.data.shixun_identifier+"/challenges");
} else {
@ -131,16 +130,28 @@ class Newshixuns extends Component {
});
}
main_type = (value) => {
main_type = (value, e) => {
this.props.form.setFieldsValue({
main_type: value,
});
this.setState({
mainvalues: e.props.name
})
}
sub_type = (value) => {
sub_type = (value, e) => {
this.props.form.setFieldsValue({
sub_type: value,
});
let newlist = ""
e.map((item, key) => {
if(item.props.name!=""){
newlist = newlist + `${item.props.name}`
}
})
this.setState({
subvalues: newlist
})
}
post_apply = () => {
@ -254,7 +265,7 @@ class Newshixuns extends Component {
{
message: '提示',
description:
'提交成功!',
'新建申请已提交,请等待管理员审核。',
}
)
@ -302,6 +313,76 @@ class Newshixuns extends Component {
}
}
// 附件相关 START
handleChange = (info) => {
if(info.file.status === 'uploading' || info.file.status === 'done' || info.file.status === 'removed') {
let {fileList} = this.state;
if (info.file.status === 'uploading' || info.file.status === 'done' || info.file.status === 'removed') {
console.log("handleChange1");
// if(fileList.length===0){
let fileLists = info.fileList;
this.setState({
// fileList:appendFileSizeToUploadFileAll(fileList),
fileList: fileLists,
deleteisnot: false
});
// }
}
}
}
onAttachmentRemove = (file) => {
if(!file.percent || file.percent == 100){
Modal.confirm({
title: '确定要删除这个附件吗?',
okText: '确定',
cancelText: '取消',
// content: 'Some descriptions',
onOk: () => {
console.log("665")
this.deleteAttachment(file)
},
onCancel() {
console.log('Cancel');
},
});
return false;
}
}
deleteAttachment = (file) => {
console.log(file);
let id=file.response ==undefined ? file.id : file.response.id
const url = `/attachments/${id}.json`
axios.delete(url, {
})
.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);
});
}
render() {
const {getFieldDecorator} = this.props.form;
const {newshixunlist, fileList, postapplytitle, postapplyvisible} = this.state;
@ -344,7 +425,15 @@ class Newshixuns extends Component {
}
return (
<div className="newMain clearfix">
<style>
{
`
.ant-form-item{
margin-bottom:5px;
}
`
}
</style>
<div className="educontent mt20 mb60 clearfix">
<div className="new_shixun">
@ -444,7 +533,7 @@ class Newshixuns extends Component {
{
newshixunlist === undefined ? "" : newshixunlist.main_type.map((item, key) => {
return (
<Option value={item.id} key={key}>
<Option value={item.id} key={key} name={item.description}>
<Tooltip placement="right"
title={item.description === "" ? "无描述" : item.description}>
{item.type_name}
@ -460,25 +549,33 @@ class Newshixuns extends Component {
</div>
</Form.Item>
<style>
{
`
.Selectlittle .ant-select-selection__rendered{
line-height:25px;
}
`
}
</style>
<Form.Item
style={{"borderBottom": 'none', 'width': '61%', 'float': 'left', 'marginTop': '40px'}}
style={{"borderBottom": 'none', 'width': '82%', 'float': 'left', 'marginTop': '40px'}}
className="chooseDes pr"
>
<div className=" fl pr mr20">
{getFieldDecorator('sub_type', {
rules: [{required: true, message: '请选择小类别'}],
})(
{getFieldDecorator('sub_type')(
<div className=" fl pr mr20">
<Select placeholder="请选择小类别"
mode="multiple"
style={{width: 180}}
onChange={this.sub_type}
defaultOpen={false}
className={"Selectlittle"}
>
{
newshixunlist === undefined ? "" : newshixunlist.small_type.map((item, key) => {
return (
<Option value={item.id} key={key}>
<Option value={item.id} key={key} name={item.description}>
<Tooltip placement="right"
title={item.description === "" ? "无描述" : item.description}>
{item.type_name}
@ -492,12 +589,14 @@ class Newshixuns extends Component {
)}
<span className="fl ml20 color-grey lineh-20">
<div>
<div className={"font-12"}>
已安装软件hadoop3.1.0jdk1.8
</div>
<div className={"font-12"}>
说明添加了hadoop3.1.0jdk1.8的源码包添加了hadoop3.1.0jdk1.8的源码包
</div>
{this.state.mainvalues === undefined && this.state.subvalues === undefined||this.state.mainvalues === "" && this.state.subvalues === "" ? "" :
<div className={"font-12"} style={{'max-width':'700px'}}>
{`${this.state.mainvalues===undefined||this.state.mainvalues=== ""?"":`已安装软件:`+this.state.mainvalues}`}
{`${this.state.subvalues===undefined||this.state.subvalues=== ""?"":this.state.mainvalues===undefined||this.state.mainvalues=== ""?`已安装软件:`+this.state.subvalues:this.state.subvalues}`}
{`${this.state.mainvalues===undefined||this.state.mainvalues=== ""?"":`说明:添加了`+this.state.mainvalues}${this.state.subvalues===undefined||this.state.subvalues=== ""?"":
this.state.mainvalues===undefined||this.state.mainvalues=== ""?`说明:添加了`+this.state.subvalues:this.state.subvalues}`}
</div>}
</div>
</span>
</div>
@ -600,27 +699,6 @@ class Newshixuns extends Component {
{/*</Form>*/}
</Modal>
<Modal
keyboard={false}
title="提示"
visible={postapplytitle}
closable={false}
footer={null}
>
<div>
<div className="task-popup-content"><p
className="task-popup-text-center font-16"><span
className="font-17 mt10">新建申请已提交请等待管理员的审核</span></p>
<li className="font-14 mt15 color-grey-6 edu-txt-center">我们将在1-2个工作日内与您联系
</li>
</div>
<div className="task-popup-OK clearfix">
<a className="task-btn task-btn-orange"
onClick={this.yeshidemodel}>知道啦</a>
</div>
</div>
</Modal>
</div>
</div>

@ -19,9 +19,7 @@ import 'antd/lib/pagination/style/index.css';
import '../shixunchildCss/Challenges.css'
import ReactDOM from 'react-dom';
import axios from 'axios';
import AccountProfile from"../../../user/AccountProfile";
const $ = window.$;
@ -32,7 +30,9 @@ class Challengesjupyter extends Component {
ChallengesDataList: undefined,
operate: true,
startbtns: false,
iFrameHeight: '0px'
iFrameHeight: '0px',
jupyter_port:0,
jupyter_url:null,
}
}
@ -59,23 +59,38 @@ class Challengesjupyter extends Component {
componentDidMount() {
setTimeout(this.ChallengesList(), 1000);
// var iframe =
// document.getElementById('ifr1');
// console.log("iframe||||||||");
// console.log(iframe);
// if(iframe){
// // var headertest=test.contentWindow.document.getElementById('header');
// // console.log(headertest);
// var iwindow = iframe.contentWindow;
// var idoc = iwindow.document;
// console.log("window",iwindow);//获取iframe的window对象
// console.log("windowwindow",iwindow.document.getElementById('header'));//获取iwindow.document对象
// console.log("document",idoc); //获取iframe的document
// console.log("documentdocument",idoc.getElementById('header'));
// console.log("html",idoc.documentElement);//获取iframe的html
// console.log("head",idoc.head); //获取head
// console.log("body",idoc.body); //获取body
// }
// console.log("componentDidMount");
// console.log("Challengesjupyter");
// console.log(this.props);
let id = this.props.match.params.shixunId;
let ChallengesURL = `/jupyters/get_info_with_tpm.json`;
let datas={
identifier:id,
}
axios.get(ChallengesURL, {params: datas}).then((response) => {
if (response.status === 200) {
if (response.data.status === 403||response.data.status === 401||response.data.status === 500) {
}else{
// console.log("componentDidMountChallengesjupyter");
// console.log(response.data);
if(response.data.status===0){
this.setState({
jupyter_url:response.data.url,
jupyter_port:response.data.port,
})
}else{
}
}
}
}).catch((error) => {
console.log(error)
});
}
@ -131,105 +146,38 @@ class Challengesjupyter extends Component {
};
modifyjupyter=()=>{
// var ifr =window.parent.frames["frame"].document;
// console.log(ifr);
var ifr = window.document.getElementById("ifr1");
console.log("modifyjupyter");
console.log(ifr);
const iframe = window.document.getElementById('ifr1');
console.log("modifyjupyter");
const frameWindow = iframe.contentWindow;
console.log(frameWindow);
const frameDocument = frameWindow.document;
console.log(frameDocument);
// window.addEventListener('load', () => {
// console.log('12313页面加载好了');
// const iframe = document.getElementById('ifr1');
// if (iframe && iframe.contentWindow && this.props.iframe_src) {
// console.log('32131231yes mounted');
// console.log(iframe.contentWindow);
// } else {
// console.log('3242242not mounted')
// }
// });
// var editor;
// editor = document.getElementById("ifr1").contentWindow;
// console.log(editor);
// window.onload = () => {
// var testwindow =
// document.getElementById('ifr1').contentWindow;
//
// }
modifyjupyter=(propsysl)=>{
// console.log("propsysl");
// console.log(propsysl);
let id=this.props.match.params.shixunId;
var jupyter_port="";
try{
jupyter_port= parseInt(this.state.jupyter_port);
}catch (e) {
jupyter_port=this.state.jupyter_port;
// var iframe=window.frames["ifr1"];
// // var title =${'ifr1'}
// console.log("iframe");
// console.log(iframe);
// var frames = window.frames; // 或 // var frames = window.parent.frames;
// for (var i = 0; i < frames.length; i++) {
// // 在这对frames的一个frame做点什么
// // frames[i].document.body.style.background = "red";
// console.log(frames[i]);
//
// }
// var idoc = iframe.document;
// console.log(idoc);
// var iwindow = title.contentWindow;
// console.log("iwindow");
// console.log(iwindow);
// var idoc = iwindow.document;
// console.log(idoc);
// window.addEventListener('message', receiveMessageFromParent, false);
//
// let messageButton = document.getElementById('message_button');
// /* console.log('msg button', messageButton); annoataion cannot use \/\/ */
// messageButton.addEventListener('click', function (e) {
// console.log('iframe send msg');
// console.log('parent', window.parent);
// console.log('top container', window.top);
// window.parent.postMessage('This is child', '*');
// }, false);
// var iframe = $('#ifr1');
// console.log(iframe.context.find('#save-notbook'));
// console.log("iframe");
// console.log(iframe)
// title.window.say(); //myFrame.say();也可
//
// title.window.document.getElementById("button").value="保存并检查";
//find button inside iframe
// let button = iframe.contents().find('#save-notbook');
// //trigger button click
// button.trigger("click");
// console.log("iframe||||||||");
// if(iframe){
// var iwindow = iframe.contentWindow;
// var idoc = iwindow.document;
// console.log("window",iwindow);//获取iframe的window对象
// console.log("document",idoc); //获取iframe的document
// console.log("html",idoc.documentElement);//获取iframe的html
// console.log("head",idoc.head); //获取head
// console.log("body",idoc.body); //获取body
// }
// var test =
// document.getElementById('ifr1').contentWindow.document.getElementById('header');
// test.style.background = "#333";
}
const url=`/jupyters/save_with_tpm.json`;
const data={
identifier:id,
jupyter_port:jupyter_port,
}
axios.post(url, data)
.then((result) => {
if (result.data.status === 0) {
this.props.showNotification(`应用成功`);
}
}).catch((error) => {
})
}
sendToken=()=>{
// console.log("sendToken");
// const iframe = document.getElementById('iframe');
// console.log("modifyjupyter");
// const frameWindow = iframe.contentWindow;
// console.log("frameWindow");
// console.log(frameWindow);
}
render() {
@ -304,13 +252,22 @@ class Challengesjupyter extends Component {
`
}
</style>
{
this.props.jupyter_url === null || this.props.jupyter_url === undefined ?
""
:
<div className="sortinxdirection mt60">
<div className="renwuxiangssi sortinxdirection">
<div><p className="renwuxiangqdiv">任务详情</p></div> <div><p className="renwuxiangqdivtest ml24"></p></div>
<div><p className="renwuxiangqdiv">任务详情</p></div>
<div><p className="renwuxiangqdivtest ml24">请将实训题目写在下方</p></div>
</div>
<div className="renwuxiangssit xaxisreverseorder">
<div className="challenbaocun" onClick={() => this.modifyjupyter(this.props)}><p
className="challenbaocuntest">应用到实训</p></div>
</div>
<div className="renwuxiangssit xaxisreverseorder"><div className="challenbaocun" onClick={()=>this.modifyjupyter()}><p className="challenbaocuntest">保存</p></div></div>
</div>
}
<style>
{
`
@ -336,10 +293,15 @@ class Challengesjupyter extends Component {
{/*https://48888.jupyter.educoder.net/tree?*/}
<div className="pb47">
<iframe src="http://121.41.4.83:48888/tree?" scrolling="no" id="ifr1" name="frame" width="100%" height="700" frameborder="0"
{
this.props.jupyter_url===null || this.props.jupyter_url===undefined?
""
:
<iframe src={this.props.jupyter_url}
sandbox="allow-same-origin allow-scripts allow-top-navigation " scrolling="no" id="frame"
name="framename" width="100%" height="700" frameBorder="0"
></iframe>
}
</div>
</div>
</div>

@ -3,6 +3,11 @@
line-height: 30px;
}
.height28 {
height: 30px;
line-height:28px;
}
.line27{
line-height: 27px;
vertical-align: 1px;

@ -457,7 +457,7 @@ class Collaborators extends Component {
style={{display:this.props.identity<5?"flex":"none"}}
>
<div className="yslwushiwidth">
<p className="edu-default-btn edu-greenback-btn ml20 height40 yslwushiwidthcolortest">共12</p>
<p className="edu-default-btn edu-greenback-btn ml20 height28 yslwushiwidthcolortest">{collaboratorList&&collaboratorList.length}</p>
</div>
<div className="yslwushiwidth xaxisreverseorder">
@ -469,7 +469,13 @@ class Collaborators extends Component {
<a onClick={() => this.showCollaboratorsvisible("admin")}
style={{display:this.props.identity===1?"flex":"none"}}
data-remote="true"
className="edu-default-btn edu-greenback-btn mr20 height40 yslwushiwidthbuton">更换管理员</a>
className="edu-default-btn edu-greenback-btn mr20 height40 yslwushiwidthbuton">
<p style={{
textAlign: "center",
width:'100%',
lineHeight: "29px",
}}>更换管理员</p>
</a>
</div>
@ -680,17 +686,17 @@ class Collaborators extends Component {
<div
className={collaboratorList.length>10&&collaboratorListsumtype===true?"":"none"}
style={{textAlign:'center',borderTop:'1px solid #eee'}}>
{/*<a className="loadMore" onClick={this.loadMore}>加载更多</a>*/}
{
mylistansum>5?
<div className="edu-txt-center mt40 mb40">
<Pagination showQuickJumper current={page}
onChange={this.paginationonChanges} pageSize={limit}
total={mylistansum}
></Pagination>
</div>
:""
}
<a className="loadMore" onClick={this.loadMore}>加载更多</a>
{/*{*/}
{/* mylistansum>5?*/}
{/* <div className="edu-txt-center mt40 mb40">*/}
{/* <Pagination showQuickJumper current={page}*/}
{/* onChange={this.paginationonChanges} pageSize={limit}*/}
{/* total={mylistansum}*/}
{/* ></Pagination>*/}
{/* </div>*/}
{/* :""*/}
{/*}*/}
</div>

@ -67,6 +67,7 @@ class TPMRepositoryCommits extends Component {
user={user}
shixun={shixun}
{...this.props}
is_jupyter={this.props.is_jupyter}
></TPMNav>
{ loadingContent ?
<CircularProgress size={40} thickness={3}

@ -27,18 +27,19 @@
line-height: 25px;
}
.challenbaocun{
width:80px;
width:103px;
height:30px;
background:#29BD8B;
border-radius:3px;
}
.challenbaocuntest{
width:80px;
width:103px;
height:30px;
font-size:16px;
color:#FFFFFF;
text-align: center;
line-height:30px;
cursor:default
}
.renwuxiangqdiv{
width:72px;
@ -51,8 +52,6 @@
width:32px;
height:30px;
font-size:16px;
font-family:MicrosoftYaHei;
color:#4CACFF;
line-height:30px;
}

@ -132,10 +132,14 @@ class ShixunCard extends Component {
{/*<img style={{display:'block',height: '28px'}} src={require(`./shixunCss/tag2.png`)}/>*/}
</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"}>
<img src={getImageUrl("images/educoder/icon/lockclose.svg")}
className="mt80 mb25"/>

@ -95,7 +95,7 @@
}
.wenjiantit{
width: 300px;
width: 220px;
}
.zuihoushijian{
width: 125px;

@ -179,7 +179,19 @@ class SearchPage extends Component{
<div className="mainx">
<style>
{
`
.maxnamewidth92{
max-width: 92%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
cursor: default;
}
`
}
</style>
<div className="educontent project-packages-list mb30">
{data === undefined ? "" : data.map((item, key) => {
return (
@ -195,14 +207,19 @@ class SearchPage extends Component{
<div className={"font-16 color-dark fl "} style={{width:"100%"}} >
{/*标题*/}
<div className="sortinxdirection jupytertextheig" style={{width:"100%"}}>
<span className={"markdown-body fonttext maxnamewidth92"}
dangerouslySetInnerHTML={{__html:item.title}}/>
{
type==="shixun"?
<div className="jupytertext intermediatecenter "><p className="jupytertextp">Jupyter</p></div>
(
item.is_jupyter===true?
<div className="jupytertext intermediatecenter ml15"><p className="jupytertextp">Jupyter</p></div>
:""
)
:""
}
<span className={"markdown-body fonttext ml9"}
dangerouslySetInnerHTML={{__html:item.title}}/>
</div>
{/*描述*/}
@ -368,7 +385,7 @@ class SearchPage extends Component{
{
count && count && count> perpages ?
<div className="edu-txt-center" style={{paddingBottom:"30px"}}>
<div className="edu-txt-center" style={{marginBottom:"30px",paddingBottom:"30px"}}>
<Pagination showQuickJumper current={page}
onChange={this.paginationonChanges} pageSize={perpages}
total={count}></Pagination>

Loading…
Cancel
Save