Merge branch 'dev_aliyun' of https://bdgit.educoder.net/Hjqreturn/educoder into dev_aliyun

dev_cs
caicai8 6 years ago
commit d651edf6a4

@ -61,29 +61,7 @@ class GamesController < ApplicationController
tomcat_url: edu_setting('cloud_tomcat_php'), is_teacher: is_teacher, tomcat_url: edu_setting('cloud_tomcat_php'), is_teacher: is_teacher,
myshixun_manager: myshixun_manager, git_url: (@shixun.vnc ? repo_url(@myshixun.repo_path) : "")} myshixun_manager: myshixun_manager, git_url: (@shixun.vnc ? repo_url(@myshixun.repo_path) : "")}
if @shixun.vnc if @shixun.vnc
begin get_vnc_link(@game)
shixun_tomcat = edu_setting('cloud_bridge')
service_host = edu_setting('vnc_url')
uri = "#{shixun_tomcat}/bridge/vnc/getvnc"
params = {tpiID: @myshixun.id, :containers => "#{Base64.urlsafe_encode64(shixun_container_limit(@shixun))}"}
res = uri_post uri, params
logger.info("###############---- ")
if res && res['code'].to_i != 0
raise("实训云平台繁忙繁忙等级99")
end
if request.subdomain == "pre-newweb"
# 无域名版本
@vnc_url = "http://#{service_host}:#{res['port']}/vnc_lite.html?password=headless"
else
# 有域名版本
@vnc_url = "https://#{res['port']}.#{service_host}/vnc_lite.html?password=headless"
end
@vnc_evaluate = @shixun.vnc_evaluate
rescue Exception => e
Rails.logger.error(e.message)
end
end end
# 区分选择题和编程题st0编程题 # 区分选择题和编程题st0编程题
@ -110,6 +88,21 @@ class GamesController < ApplicationController
end end
end end
def reset_vnc_link
begin
# 删除vnc的pod
delete_vnc(@game)
# 重新连接
get_vnc_link(@game)
render :json => {status: 1, message: "重置VNC成功", data: {vnc_url: @vnc_url, vnc_evaluate: @vnc_evaluate}}
rescue Exception => e
logger.error("############'#{e.message}'")
tip_exception("实训云平台繁忙")
end
end
# 查看效果 # 查看效果
# todo : 这块代码有很大的改进空间 # todo : 这块代码有很大的改进空间
# todo : 中文排序问题 # todo : 中文排序问题
@ -880,7 +873,7 @@ class GamesController < ApplicationController
choose.challenge_questions.each do |question| choose.challenge_questions.each do |question|
position = question.position position = question.position
option_name = question.option_name option_name = question.option_name
challenge_question << {:positon => position, :option_name => option_name} challenge_question <<{:positon => position, :option_name => option_name}
end end
# actual_output为空表示暂时没有评测答题不允许查看 # actual_output为空表示暂时没有评测答题不允许查看
actual_output = output.try(:actual_output).try(:strip) actual_output = output.try(:actual_output).try(:strip)
@ -941,4 +934,47 @@ class GamesController < ApplicationController
game.myshixun.update_column(:updated_at, Time.now) game.myshixun.update_column(:updated_at, Time.now)
end end
# vnc连接
def get_vnc_link game
begin
shixun = game.myshixun.shixun
shixun_tomcat = edu_setting('cloud_bridge')
service_host = edu_setting('vnc_url')
uri = "#{shixun_tomcat}/bridge/vnc/getvnc"
params = {tpiID: game.myshixun.id, :containers => "#{Base64.urlsafe_encode64(shixun_container_limit(shixun))}"}
res = uri_post uri, params
if res && res['code'].to_i != 0
raise("实训云平台繁忙繁忙等级99")
end
@vnc_url =
if request.subdomain == "pre-newweb" || request.subdomain == "test-newweb"
# 无域名版本
"http://#{service_host}:#{res['port']}/vnc_lite.html?password=headless"
else
# 有域名版本
"https://#{res['port']}.#{service_host}/vnc_lite.html?password=headless"
end
@vnc_evaluate = shixun.vnc_evaluate
rescue Exception => e
Rails.logger.error(e.message)
end
end
# 删除pod
def delete_vnc game
myshixun_id = game.myshixun_id
digest = game.identifier + edu_setting('bridge_secret_key')
digest_key = Digest::SHA1.hexdigest("#{digest}")
begin
shixun_tomcat = edu_setting('cloud_bridge')
uri = "#{shixun_tomcat}/bridge/vnc/delete"
Rails.logger.info("#{current_user} => cloese_vnc digest is #{digest}")
params = {:tpiID => myshixun_id, :digestKey => digest_key, :identifier => game.identifier}
res = uri_post uri, params
if res && res['code'].to_i != 0
raise("实训云平台繁忙繁忙等级110")
end
end
end
end end

@ -113,7 +113,7 @@ class HomeworkCommonsController < ApplicationController
if @user_course_identity == Course::STUDENT if @user_course_identity == Course::STUDENT
@work = @homework.user_work(current_user.id) @work = @homework.user_work(current_user.id)
# 学生访问列表时计算个人成绩 # 学生访问列表时计算个人成绩
if @homework.homework_type == "practice" if @homework.homework_type == "practice" && !@homework.end_or_late
myshixun = Myshixun.find_by(shixun_id: @shixun.id, user_id: current_user.id) myshixun = Myshixun.find_by(shixun_id: @shixun.id, user_id: current_user.id)
if @work && myshixun if @work && myshixun
challenge_settings = @homework.homework_challenge_settings challenge_settings = @homework.homework_challenge_settings

@ -25,7 +25,8 @@ class MyshixunsController < ApplicationController
begin begin
@shixun = Shixun.select(:id, :identifier, :challenges_count).find(@myshixun.shixun_id) @shixun = Shixun.select(:id, :identifier, :challenges_count).find(@myshixun.shixun_id)
@myshixun.destroy! @myshixun.destroy!
StudentWork.where(:myshixun_id => @myshixun.id).update_all(:myshixun_id => 0, :work_status => 0) StudentWork.where(:myshixun_id => @myshixun.id).update_all(myshixun_id: 0, work_status: 0, work_score: nil,
final_score: nil, efficiency: 0, eff_score: 0, calculation_time: nil, cost_time: 0, compelete_status: 0)
rescue Exception => e rescue Exception => e
logger.error("######reset_my_game_failed:#{e.message}") logger.error("######reset_my_game_failed:#{e.message}")
raise("ActiveRecord::RecordInvalid") raise("ActiveRecord::RecordInvalid")

@ -128,10 +128,10 @@ class ShixunsController < ApplicationController
page = params[:page] || 1 page = params[:page] || 1
limit = params[:limit] || 10 limit = params[:limit] || 10
offset = (page.to_i - 1) * limit offset = (page.to_i - 1) * (limit.to_i)
order = params[:order] || "desc" order = params[:order] || "desc"
## 搜索关键字创建者、实训名称、院校名称 ## 搜索关键字创建者、实训名称、院校名称
keyword = params[:search] keyword = params[:search].blank? ? "*" : params[:search]
@shixuns = Shixun.search keyword, where: {id: @shixuns.pluck(:id)}, order: {"myshixuns_count" => order}, limit: limit, offset: offset @shixuns = Shixun.search keyword, where: {id: @shixuns.pluck(:id)}, order: {"myshixuns_count" => order}, limit: limit, offset: offset
@total_count = @shixuns.total_count @total_count = @shixuns.total_count
@ -978,7 +978,7 @@ private
raise("实训名称不能为空") if params[:shixun][:name].blank? raise("实训名称不能为空") if params[:shixun][:name].blank?
params.require(:shixun).permit(:name, :trainee, :webssh, :can_copy, :use_scope, :vnc, :test_set_permission, params.require(:shixun).permit(:name, :trainee, :webssh, :can_copy, :use_scope, :vnc, :test_set_permission,
:task_pass, :multi_webssh, :opening_time, :mirror_script_id, :code_hidden, :task_pass, :multi_webssh, :opening_time, :mirror_script_id, :code_hidden,
:hide_code, :forbid_copy, :vnc_evaluate) :hide_code, :forbid_copy, :vnc_evaluate, :code_edit_permission)
end end
def shixun_info_params def shixun_info_params

@ -56,11 +56,6 @@ module ApplicationHelper
end end
# 相关推荐
def relation_path(shixun)
shixun.subjects.where(hidden: 0).limit(2)
end
# shixun开启挑战对应的行为名及url # shixun开启挑战对应的行为名及url
def task_operation_url current_myshixun, shixun def task_operation_url current_myshixun, shixun
if current_myshixun.blank? if current_myshixun.blank?

@ -237,4 +237,9 @@ module HomeworkCommonsHelper
def anon_comments user, work_id def anon_comments user, work_id
StudentWorksScore.where(student_work_id: work_id, reviewer_role: 3, user_id: user.id) StudentWorksScore.where(student_work_id: work_id, reviewer_role: 3, user_id: user.id)
end end
def student_redo_work work, homework
publish_time = homework.homework_group_setting(work.user_id)&.publish_time
work.myshixun && publish_time && work.myshixun.games.where("status = 2 and answer_open = 1 and end_time <= '#{publish_time}'").count > 0 ? true : false
end
end end

@ -280,6 +280,11 @@ class Shixun < ApplicationRecord
self.mirror_name.include?('JavaWeb') || self.mirror_name.include?('PHP') && self.mirror_name.include?('Mysql') || self.mirror_name.include?('Web') self.mirror_name.include?('JavaWeb') || self.mirror_name.include?('PHP') && self.mirror_name.include?('Mysql') || self.mirror_name.include?('Web')
end end
# 所属实践课程
def relation_path
subjects.where(hidden: 0).uniq
end
private private
def send_tiding def send_tiding

@ -5,6 +5,7 @@ end
# json.description @subject&.description # json.description @subject&.description
json.start_learning @start_learning json.start_learning @start_learning
json.subject_id @subject.id
json.learned @start_learning ? @course.my_subject_progress : 0 json.learned @start_learning ? @course.my_subject_progress : 0

@ -40,6 +40,8 @@ elsif @user_course_identity == Course::STUDENT
json.efficiency work_score_format(@work.efficiency, true, @score_open) json.efficiency work_score_format(@work.efficiency, true, @score_open)
json.eff_score work_score_format(@work.eff_score, true, @score_open) json.eff_score work_score_format(@work.eff_score, true, @score_open)
json.complete_count @work.myshixun.try(:passed_count) json.complete_count @work.myshixun.try(:passed_count)
json.redo_work (@homework.end_or_late ? false : student_redo_work(@work, @homework))
json.myshixun_identifier @work.myshixun&.identifier
else else
json.(@work, :id, :work_status, :update_time, :ultimate_score) json.(@work, :id, :work_status, :update_time, :ultimate_score)

@ -3,14 +3,14 @@ json.creator do
json.partial! 'users/user', locals: { user: shixun.owner } json.partial! 'users/user', locals: { user: shixun.owner }
end end
# 推荐实训
json.recommands do
json.partial! 'shap_shixun', locals: { shixuns: recommend_shixun(shixun) }
end
# 相关路径 # 相关路径
json.paths do json.paths do
json.partial! 'subjects/subject', locals: {subjects: relation_path(shixun)} json.partial! 'subjects/subject', locals: {subjects: shixun.relation_path}
end
# 推荐实训
json.recommands do
json.partial! 'shap_shixun', locals: { shixuns: shixun.relation_path.size > 0 ? recommend_shixun(shixun) : [] }
end end
# 技能标签 # 技能标签

@ -30,6 +30,7 @@ json.shixun do
json.scope_partment @shixun.schools.map(&:name) # 公开范围 json.scope_partment @shixun.schools.map(&:name) # 公开范围
json.opening_time @shixun.opening_time json.opening_time @shixun.opening_time
json.forbid_copy @shixun.forbid_copy json.forbid_copy @shixun.forbid_copy
json.code_edit_permission @shixun.code_edit_permission # tpi学员是否有编辑所有代码的权限
# 私密仓库 # 私密仓库
json.is_secret_repository @shixun.shixun_secret_repository.present? json.is_secret_repository @shixun.shixun_secret_repository.present?

@ -161,6 +161,7 @@ Rails.application.routes.draw do
get :picture_display get :picture_display
get :sync_codes get :sync_codes
get :close_webssh get :close_webssh
get :reset_vnc_link
get :get_answer_info get :get_answer_info
get :unlock_answer get :unlock_answer
get :check_test_sets get :check_test_sets

@ -0,0 +1,6 @@
class AddCodeEditPermissionForShixun < ActiveRecord::Migration[5.2]
def change
add_column :shixuns, :code_edit_permission, :boolean, default: false
end
end

@ -32,7 +32,7 @@ import _ from 'lodash'
import TPIContext from './TPIContext' import TPIContext from './TPIContext'
import { EDU_ADMIN, EDU_SHIXUN_MANAGER, EDU_SHIXUN_MEMBER, EDU_CERTIFICATION_TEACHER import { EDU_ADMIN, EDU_SHIXUN_MANAGER, EDU_SHIXUN_MEMBER, EDU_CERTIFICATION_TEACHER
, EDU_GAME_MANAGER, EDU_TEACHER, EDU_NORMAL, EDU_BUSINESS} from 'educoder' , EDU_GAME_MANAGER, EDU_TEACHER, EDU_NORMAL, EDU_BUSINESS, CNotificationHOC } from 'educoder'
import { MuiThemeProvider, createMuiTheme, withStyles } from 'material-ui/styles'; import { MuiThemeProvider, createMuiTheme, withStyles } from 'material-ui/styles';
import MUIDialogStyleUtil from '../modules/page/component/MUIDialogStyleUtil' import MUIDialogStyleUtil from '../modules/page/component/MUIDialogStyleUtil'
@ -877,6 +877,7 @@ pop_box_new(htmlvalue, 480, 182);
return ( return (
<TPIContext.Provider <TPIContext.Provider
value={{ value={{
...this.props,
...this.state, ...this.state,
resetTestSetsExpandedArray: this.resetTestSetsExpandedArray, resetTestSetsExpandedArray: this.resetTestSetsExpandedArray,
onRunCodeTestFinish: this.onRunCodeTestFinish, onRunCodeTestFinish: this.onRunCodeTestFinish,
@ -961,7 +962,7 @@ pop_box_new(htmlvalue, 480, 182);
} }
} }
export default withStyles(styles) (TPIContextProvider); export default CNotificationHOC() (withStyles(styles) (TPIContextProvider));

@ -109,7 +109,7 @@ export function CNotificationHOC(options = {}) {
const { title, content,subContent, onOk, onCancel, okText } = object; const { title, content,subContent, onOk, onCancel, okText } = object;
this.onCancel = onCancel this.onCancel = onCancel
this.onOk = onOk this.onOk = onOk
this.okText = okText || '' this.okText = okText || '确定'
this.setState({ title, content , subContent , dialogOpen: true }) this.setState({ title, content , subContent , dialogOpen: true })
} }
onDialogOkBtnClick = () => { onDialogOkBtnClick = () => {
@ -146,7 +146,15 @@ export function CNotificationHOC(options = {}) {
return ( return (
<React.Fragment> <React.Fragment>
<style>
{`
.confirmModal .task-popup-content {
padding: 0px;
}
`}
</style>
<Modals <Modals
className="confirmModal"
modalsType={dialogOpen} modalsType={dialogOpen}
modalsTopval={ modalsTopval={
content content

@ -13,6 +13,7 @@ render() {
const antIcons = <Icon type="loading" style={{ fontSize: 24 }} spin /> const antIcons = <Icon type="loading" style={{ fontSize: 24 }} spin />
return( return(
<Modal <Modal
className={this.props.className}
keyboard={false} keyboard={false}
title="提示" title="提示"
visible={this.props.modalsType===undefined?false:this.props.modalsType} visible={this.props.modalsType===undefined?false:this.props.modalsType}

@ -210,6 +210,8 @@ class Index extends Component {
{/* 区分下repo和evaluate模块的以及子模块的 */} {/* 区分下repo和evaluate模块的以及子模块的 */}
<MainContentContainer <MainContentContainer
confirm={context.confirm}
onDrawerButtonClick={this.onDrawerButtonClick} onDrawerButtonClick={this.onDrawerButtonClick}
grade={context.grade} grade={context.grade}
allowed_unlock={context.allowed_unlock} allowed_unlock={context.allowed_unlock}

@ -21,7 +21,8 @@ class VNCContainer extends Component {
this.state = { this.state = {
fileTreeSelectedKeys: [], fileTreeSelectedKeys: [],
repositoryCode: '' repositoryCode: '',
displayKey: 1
} }
} }
componentDidMount() { componentDidMount() {
@ -114,6 +115,43 @@ class VNCContainer extends Component {
showCodeEvaluate = () => { showCodeEvaluate = () => {
this.setState({ bottomDrawer: true }) this.setState({ bottomDrawer: true })
} }
onResetVNC = () => {
// 桌面系统将恢复到初始状态,您在系统中创建的数据可能会丢失
// 请确保您的数据已保存(如:版本库代码已推送到服务器)
// 是否确认重置?
this.props.confirm({
content: <div style={{textAlign: 'center'}}>
<div>桌面系统将恢复到初始状态您在系统中创建的数据可能会丢失</div>
<div>请确保您的数据已保存版本库代码已推送到服务器</div>
<div>是否确认重置</div>
</div>,
onOk: () => {
const url = `/tasks/${this.props.game.identifier}/reset_vnc_link.json`
axios.get(url, {
}).then((response) => {
if (response.data.data && response.data.data.vnc_url) {
// reset
this.setState({
displayKey: this.state.displayKey + 1,
vnc_url: response.data.data.vnc_url
})
} else {
}
// this.setState({ isEditablePath, currentPath: path });
}).catch(error =>{
console.log(error)
this.setState({ readingCodeLoading: false });
this.props.showSnackbar(`服务端异常,请联系管理员!`);
})
console.log('doooo')
},
onCancel() {
console.log('Cancel');
},
})
}
/* /*
selectedKeys={fileTreeSelectedKeys} selectedKeys={fileTreeSelectedKeys}
@ -191,6 +229,30 @@ class VNCContainer extends Component {
.codeInDrawer .ant-spin-nested-loading > div > .ant-spin .ant-spin-text { .codeInDrawer .ant-spin-nested-loading > div > .ant-spin .ant-spin-text {
text-shadow: none; text-shadow: none;
} }
.resetVNC {
top: 30px;
writing-mode: initial;
left: calc(100% - 120px);
background-image: none;
width: auto;
background: #081516;
height: 30px;
padding: 0 6px;
border-radius: 4px;
}
.resetVNC .text {
top: 0px;
writing-mode: initial;
left: unset;
}
.resetVNC .text span {
vertical-align: middle;
margin-left: 2px;
}
.float_button:hover .text {
color: #4CACFF;
}
`}</style> `}</style>
<div style={{ 'padding': '16px', 'border-bottom': '1px solid #3A383A' }}> <div style={{ 'padding': '16px', 'border-bottom': '1px solid #3A383A' }}>
<div style={{ color: '#888888' }}>网址克隆</div> <div style={{ color: '#888888' }}>网址克隆</div>
@ -207,9 +269,14 @@ class VNCContainer extends Component {
></RepoTree> ></RepoTree>
</SecondDrawer> </SecondDrawer>
{/* <FloatButton></FloatButton> */} <FloatButton className="resetVNC" onClick={this.onResetVNC}>
<i className="iconfont icon-zhongzhi2 font-16 "></i>
<span>重置桌面系统</span>
</FloatButton>
<VNCDisplay <VNCDisplay
{...this.props} {...this.props}
key={this.state.displayKey}
vnc_url={this.state.vnc_url || this.props.vnc_url}
> >
<Drawer <Drawer

@ -6,6 +6,7 @@ const $ = window.$;
// const showIframeContent = window.location.search.indexOf('vnc=1') != -1; // const showIframeContent = window.location.search.indexOf('vnc=1') != -1;
class VNCDisplay extends Component { class VNCDisplay extends Component {
componentDidMount() { componentDidMount() {
console.log('vnc init')
console.log(RFB) console.log(RFB)
let rfb; let rfb;

@ -2355,10 +2355,10 @@ export default class TPMsettings extends Component {
return( return(
<div key={key}> <div key={key}>
<div id="5"> <div id="5">
<p className="color-grey-6 font-16 mt30 mb20" id="shixun_scenario_type_name"> <div className="color-grey-6 font-16 mt30 mb20" id="shixun_scenario_type_name">
<span className={"fl"}>{item.name}</span> <span className={"fl"}>{item.name}</span>
{/*<span className={"fr mr40"} onClick={()=>this.Deselectlittle(item.mirror_repository_id)}><i className="fa fa-times-circle color-grey-c font-16 fl"></i></span>*/} {/*<span className={"fr mr40"} onClick={()=>this.Deselectlittle(item.mirror_repository_id)}><i className="fa fa-times-circle color-grey-c font-16 fl"></i></span>*/}
</p> </div>
<div className="clearfix mb5"> <div className="clearfix mb5">
<label className="panel-form-label fl">CPU()</label> <label className="panel-form-label fl">CPU()</label>
<div className="pr fl with80 status_con"> <div className="pr fl with80 status_con">

Loading…
Cancel
Save