Merge branch 'dev_aliyun' into develop

dev_tj
cxt 5 years ago
commit 3225f0a45d

@ -12,13 +12,7 @@ class ItemBasketsController < ApplicationController
end
def basket_list
@single_questions_count = current_user.item_baskets.where(item_type: "SINGLE").count
@multiple_questions_count = current_user.item_baskets.where(item_type: "MULTIPLE").count
@judgement_questions_count = current_user.item_baskets.where(item_type: "JUDGMENT").count
@completion_questions_count = current_user.item_baskets.where(item_type: "COMPLETION").count
@subjective_questions_count = current_user.item_baskets.where(item_type: "SUBJECTIVE").count
@practical_questions_count = current_user.item_baskets.where(item_type: "PRACTICAL").count
@program_questions_count = current_user.item_baskets.where(item_type: "PROGRAM").count
@basket_count = current_user.item_baskets.group(:item_type).count
end
def create

@ -1187,7 +1187,10 @@ private
end
def validate_wachat_support
tip_exception(-2, "..") if (params[:wechat].present? && !@shixun.is_wechat_support?)
if (params[:wechat].present? && !@shixun.is_wechat_support?)
tip_exception(-5, "..")
end
end
end

@ -19,38 +19,26 @@ class ItemBank < ApplicationRecord
end
def apply?
!public && ApplyAction.where(container_type: "ItemBank", container_id: id, status: 0).exists?
!public && ApplyAction.exists?(container_type: "ItemBank", container_id: id, status: 0)
end
def type_string
result = case item_type
when "SINGLE"
"单选题"
when "MULTIPLE"
"多选题"
when "JUDGMENT"
"判断题"
when "COMPLETION"
"填空题"
when "SUBJECTIVE"
"简答题"
when "PRACTICAL"
"实训题"
when "PROGRAM"
"编程题"
end
result
case item_type
when "SINGLE" then "单选题"
when "MULTIPLE" then "多选题"
when "JUDGMENT" then "判断题"
when "COMPLETION" then "填空题"
when "SUBJECTIVE" then "简答题"
when "PRACTICAL" then "实训题"
when "PROGRAM" then "编程题"
end
end
def difficulty_string
result = case difficulty
when 1
"简单"
when 2
"适中"
when 3
"困难"
end
result
case difficulty
when 1 then "简单"
when 2 then "适中"
when 3 then "困难"
end
end
end

@ -223,7 +223,7 @@ class StudentWork < ApplicationRecord
game_score = adjust_score.score
elsif game.present?
setting = homework_common.homework_group_setting game.user_id
if game.status == 2 && ((game.end_time && game.end_time < setting.end_time) || (homework_common.allow_late && game.end_time && game.end_time < homework_common.late_time))
if game.status == 2 && ((game.end_time && setting.end_time && game.end_time < setting.end_time) || (homework_common.allow_late && homework_common.late_time && game.end_time && game.end_time < homework_common.late_time))
answer_open_evaluation = homework_common.homework_detail_manual.answer_open_evaluation
game_score = answer_open_evaluation ? score : (game.final_score > 0 ? game.real_score(score) : 0)
end

@ -51,6 +51,7 @@ class Admins::ShixunSettingsQuery < ApplicationQuery
all_shixuns = all_shixuns.where(task_pass: params[:task_pass]) if params[:task_pass]
all_shixuns = all_shixuns.where(code_hidden: params[:code_hidden]) if params[:code_hidden]
all_shixuns = all_shixuns.where(vip: params[:vip]) if params[:vip]
all_shixuns = all_shixuns.where(is_wechat_support: params[:is_wechat_support]) if params[:is_wechat_support]
custom_sort(all_shixuns, params[:sort_by], params[:sort_direction])
end

@ -71,6 +71,13 @@
<span class="only_view">只看vip</span>
</label>
</div>
<div class="mr-5">
<label for="is_wechat_support">
<%= check_box_tag :is_wechat_support, !@sort_json[:is_wechat_support],@sort_json[:is_wechat_support], class:"shixun-settings-select" %>
<span class="only_view">只看小程序可用</span>
</label>
</div>
</div>
</div>
<% end %>

@ -1,7 +1,7 @@
json.single_questions_count @single_questions_count
json.multiple_questions_count @multiple_questions_count
json.judgement_questions_count @judgement_questions_count
json.completion_questions_count @completion_questions_count
json.subjective_questions_count @subjective_questions_count
json.practical_questions_count @practical_questions_count
json.program_questions_count @program_questions_count
json.single_questions_count @basket_count&.fetch("SINGLE", 0)
json.multiple_questions_count @basket_count&.fetch("MULTIPLE", 0)
json.judgement_questions_count @basket_count&.fetch("JUDGMENT", 0)
json.completion_questions_count @basket_count&.fetch("COMPLETION", 0)
json.subjective_questions_count @basket_count&.fetch("SUBJECTIVE", 0)
json.practical_questions_count @basket_count&.fetch("PRACTICAL", 0)
json.program_questions_count @basket_count&.fetch("PROGRAM", 0)

@ -1,51 +1,78 @@
# 执行示例 bundle exec rake zip_pack:shixun_pack args=123,2323
# 执行示例 bundle exec rake zip_pack:shixun_pack class=Course ids=123,2323
# 执行示例 bundle exec rake zip_pack:shixun_pack class=HomeworkCommon ids=123,2323
namespace :zip_pack do
desc "手工打包作品"
OUTPUT_FOLDER = "#{Rails.root}/files/archiveZip"
task :shixun_pack => :environment do
if ENV['args']
homework_ids = ENV['args'].split(",").map(&:to_i)
homeworks = HomeworkCommon.where(id: homework_ids)
homeworks.includes(:score_student_works).each do |homework|
out_file_name = "#{Time.now.strftime("%Y%m%d%H%M%S").to_s}-#{homework.course_id}-#{homework.name}.zip"
out_file_name.gsub!(" ", "-")
out_file_name.gsub!("/", "_")
if ENV['class'] && ENV['ids']
env_ids = ENV['ids'].split(",").map(&:to_i)
folders = []
if ENV['class'] == "Course"
courses = Course.where(id: env_ids)
courses.each do |course|
homeworks = course.practice_homeworks.homework_published
new_dir_name = "#{course.name.to_s.strip}_#{Time.now.strftime("%Y%m%d%H%M%S").to_s}"
new_dir_name.gsub!(" ", "-")
new_dir_name.gsub!("/", "_")
new_folder = "#{OUTPUT_FOLDER}/#{new_dir_name}"
zip_homework_pdf homeworks, new_folder
folders << new_folder
end
else
homeworks = HomeworkCommon.where(id: env_ids)
new_dir_name = "#{homeworks.first&.course&.name.to_s.strip}_#{Time.now.strftime("%Y%m%d%H%M%S").to_s}"
new_dir_name.gsub!(" ", "-")
new_dir_name.gsub!("/", "_")
new_folder = "#{OUTPUT_FOLDER}/#{new_dir_name}"
zip_homework_pdf homeworks, new_folder
folders << new_folder
end
puts "下载路径: #{folders.join(",")}"
end
end
zipfile_name = "#{OUTPUT_FOLDER}/#{out_file_name}"
Dir.mkdir(File.dirname(zipfile_name)) unless File.exist?(File.dirname(zipfile_name))
def zip_homework_pdf homeworks, folder
Dir.mkdir(folder) unless File.directory?(folder)
student_works = homework.score_student_works
homeworks.includes(:score_student_works).each do |homework|
out_file_name = "#{Time.now.strftime("%Y%m%d%H%M%S").to_s}-#{homework.course_id}-#{homework.name}.zip"
out_file_name.gsub!(" ", "-")
out_file_name.gsub!("/", "_")
if student_works.size > 0
pdfs = []
Zip::File.open(zipfile_name, Zip::File::CREATE) do |zip|
student_works.find_each.map do |student_work|
export = ExportShixunReportService.new(homework, student_work)
pdf = export.to_pdf
pdfs << pdf
begin
zip.add(export.filename, pdf.path)
puts "out: #{export.filename}_#{pdf.path}"
rescue => ex
Rails.logger.error(ex.message)
zipfile_name = "#{folder}/#{out_file_name}"
# Dir.mkdir(File.dirname(zipfile_name)) unless File.exist?(File.dirname(zipfile_name))
zip.get_output_stream('FILE_NOTICE.txt'){|os| os.write("文件重复:#{export.filename}") }
next
end
student_works = homework.score_student_works
if student_works.size > 0
pdfs = []
Zip::File.open(zipfile_name, Zip::File::CREATE) do |zip|
student_works.find_each.map do |student_work|
export = ExportShixunReportService.new(homework, student_work)
pdf = export.to_pdf
pdfs << pdf
begin
zip.add(export.filename, pdf.path)
puts "out: #{export.filename}_#{pdf.path}"
rescue => ex
Rails.logger.error(ex.message)
zip.get_output_stream('FILE_NOTICE.txt'){|os| os.write("文件重复:#{export.filename}") }
next
end
end
zipfile = zipfile_name
else
zipfile = {:message => "no file"}
end
puts "out: #{zipfile}"
zipfile = zipfile_name
else
zipfile = {:message => "no file"}
end
end
end
# 执行示例 bundle exec rake zip_pack:homework_attach_pack args=123
task :homework_attach_pack => :environment do
include ExportHelper
if ENV['args']
@ -61,7 +88,4 @@ namespace :zip_pack do
end
end
def filename_for_content_disposition(name)
request.env['HTTP_USER_AGENT'] =~ %r{MSIE|Trident|Edge} ? ERB::Util.url_encode(name) : name
end
end

@ -75,6 +75,7 @@
"react-codemirror": "^1.0.0",
"react-codemirror2": "^6.0.0",
"react-content-loader": "^3.1.1",
"react-cookies": "^0.1.1",
"react-dev-utils": "^5.0.0",
"react-dom": "^16.9.0",
"react-hot-loader": "^4.0.0",

@ -366,6 +366,11 @@ const JupyterTPI = Loadable({
loader: () => import('./modules/tpm/jupyter'),
loading: Loading
});
// 微信代码编辑器
const WXCode = Loadable({
loader: () => import('./modules/wxcode'),
loading: Loading
});
// //个人竞赛报名
// const PersonalCompetit = Loadable({
// loader: () => import('./modules/competition/personal/PersonalCompetit.js'),
@ -823,6 +828,11 @@ class App extends Component {
render={
(props) => (<Headplugselection {...this.props} {...props} {...this.state} />)
}/>
<Route path="/wxcode/:identifier?" component={WXCode}
render={
(props)=>(<WXCode {...this.props} {...props} {...this.state}></WXCode>)
}
/>
<Route exact path="/"
// component={ShixunsHome}
render={

@ -0,0 +1,285 @@
/*
* @Description: 微信端代码编辑器
* @Author: tangjiang
* @Github:
* @Date: 2020-01-15 09:56:34
* @LastEditors : tangjiang
* @LastEditTime : 2020-01-17 11:48:21
*/
import './index.scss';
import React, {useState, useEffect, useRef} from 'react';
import MonacoEditor from '@monaco-editor/react';
import { Input, Icon } from 'antd';
import { connect } from 'react-redux';
import actions from '../../redux/actions';
import cookie from 'react-cookies'
const { TextArea } = Input;
const App = (props) => {
const {
isShow,
wxCode,
path,
showLoading,
// userCode,
testCase = [],
getWXCode,
last_compile_output,
test_sets_count,
sets_error_count,
getWXCodeTestCase,
restoreWXCode,
updateWXCodeForEditor,
updateWXCodeForInterval,
evaluateWxCode,
showWXCodeTextCase,
changeWXCodeEvaluateLoading
} = props;
const {identifier} = props.match.params;
// 获取路径参数
const _params = window.location.search;
if (_params) {
let _search = _params.split('?')[1];
_search.split('&').forEach(item => {
console.log(item);
const _arr = item.split('=');
cookie.save(_arr[0], _arr[1], {
path: '/',
domain: '.educoder.net'
});
});
}
const [isActive, setIsActive] = useState(-1);
// const [isVisible, setIsVisible] = useState(false);
const editorRef = useRef(null);
let timer = null;
useEffect(() => {
// 加载代码块内容
getWXCode(identifier);
// 加载测试集
const params = {
path,
status: 0,
retry: 1
};
getWXCodeTestCase(identifier, params);
}, []);
// 关闭
const handleCloseTestCase = () => {
// setIsVisible(false);
showWXCodeTextCase(false)
}
// 测试集
const handleClickTestCase = () => {
// setIsVisible(true);
showWXCodeTextCase(true)
}
// 编辑器代码
const handleEditorChange = (origin, monaco) => {
editorRef.current = monaco; // 获取当前monaco实例
// setEditCode(origin); // 保存编辑器初始值
editorRef.current.onDidChangeModelContent(e => { // 监听编辑器内容的变化
// TODO 需要优化 节流
const val = editorRef.current.getValue();
// console.log('编辑器代码====>>>>', val);
// updateWXCodeForEditor(val);
codeChange(val);
});
};
const codeChange = (code) => {
// console.log(code);
updateWXCodeForEditor(code);
if (!timer) {
timer = setInterval(function () {
clearInterval(timer);
timer = null;
// 调用更新代码
updateWXCodeForInterval(identifier, path);
}, 10000);
}
}
// 关闭单个测试集
const handleCloseItem = (i, flag) => {
if (!flag) return;
setIsActive(isActive === i ? -1 : i);
}
// 初始化
const handleResetCode = () => {
const result = window.confirm('你在本文件中修改的内容将丢失, 是否确定重新加载初始代码?');
if (result) {
identifier && restoreWXCode(identifier, { path });
}
}
// 评测
const handleEvalateCode = () => {
changeWXCodeEvaluateLoading(true);
evaluateWxCode(identifier, path);
}
const tcclasses = isShow ? `wx-code-test-case active` : 'wx-code-test-case';
const loading = showLoading ? 'code-evaluate-loading active' : 'code-evaluate-loading';
const _val = sets_error_count === 0;
let resultTxt = (_val) ? '全部通过' : `${sets_error_count}组测试结果不匹配`;
const iclasses = _val ? 'iconfont icon-tishi1 icon success' : 'iconfont icon-tishi1 icon fail';
const tclasses = _val ? 'result-txt success' : 'result-txt fail';
const ulClasses = last_compile_output ? 'case-list hasResult' : 'case-list';
return (
<div className="wx-code-area">
<div className="wx-code-flex">
<div className="wx-code-item">
<MonacoEditor
height="100%"
width="100%"
language="python"
value={wxCode}
options={{
selectOnLineNumbers: true,
automaticLayout: true,
fontSize: `32px`
}}
theme='dark'
editorDidMount={handleEditorChange}
/>
</div>
<div className="wx-code-test">
<div className="flex-btn">
<span className="icon-btn" onClick={handleResetCode}>
<i className="iconfont icon-reset icon"></i>
<span className="icon-txt">初始化</span>
</span>
<span className="icon-btn" onClick={handleClickTestCase}>
<i className="iconfont icon-base icon"></i>
<span className="icon-txt">测试集</span>
</span>
</div>
{/* <Button type="primary" shape="circle">评测</Button> */}
<button className="wx-pt-btn" onClick={handleEvalateCode}>评测</button>
</div>
</div>
{/* 测试集 */}
<div className={tcclasses}>
<div className="text-case-list">
<div className="list-header">
<span className="header-title">{testCase.length}个测试用例</span>
<span className="header-close" onClick={handleCloseTestCase}>关闭</span>
</div>
<div className="wxcode-test-result" style={{ display: last_compile_output ? 'block' : 'none'}}>
<i className={iclasses}></i>
<span className={tclasses}>{test_sets_count - sets_error_count}/{test_sets_count}</span>
<span className={`${tclasses} result-txt-desc`}>{resultTxt}</span>
</div>
<ul className={ulClasses}>
{
testCase.map((item, i) => {
const {input, output, actual_output, is_public, result} = item;
const _classes = isActive === i ? 'case-item-desc active' : 'case-item-desc';
const iconclasses = isActive === i ? 'iconfont icon-sanjiaoxing-down icon active' : 'iconfont icon-triangle icon';
const headerClasses = is_public ? 'item-header-desc active' : 'item-header-desc';
// console.log(_classes);
return (
<li className="case-item" key={`item_${i}`}>
<div className="case-item-header" onClick={() => handleCloseItem(i, is_public)}>
<h2 className={headerClasses}>
<i className={iconclasses}></i>
测试集{i + 1}
</h2>
{
is_public
? (result
? <span className="iconfont icon-wancheng case_item_success"></span>
: <span className="iconfont icon-jinggao1 case_item_fail"></span>)
: (<span className="case-item-tips">
隐藏测试集暂不支持解锁和查看
{/* {result
? <span className="iconfont icon-wancheng case_item_success"></span>
: <span className="iconfont icon-jinggao1 case_item_fail"></span>
} */}
</span>)
}
</div>
<div className={_classes}>
<span className="desc-title">测试输入</span>
<span className="test-input">{input || '-'}</span>
<span className="desc-title">预期输出</span>
{/* <textarea rows="5">预期输出</textarea> */}
<TextArea
readOnly={true}
className="text-area-style"
value={output}
onChange={this.onChange}
placeholder="Controlled autosize"
autoSize={{ minRows: 3, maxRows: 6 }}
/>
{/* <TextArea rows={5} className="hope-result">预期输出</TextArea> */}
<span className="desc-title">实际输出</span>
<TextArea
readOnly={true}
className="text-area-style"
value={actual_output}
autoSize={{ minRows: 1, maxRows: 3 }}
/>
</div>
</li>
)
})
}
</ul>
</div>
</div>
{/* 测评中 */}
<div className={loading}>
<span className="loading-flex">
<Icon className="loading-icon" type="loading" />
<span className="loading-txt">测评中...</span>
</span>
</div>
</div>
);
}
const mapStateToProps = (state) => {
const {
path,
isShow,
wxCode,
userCode,
testCase,
showLoading,
last_compile_output,
test_sets_count,
sets_error_count
} = state.wxcodeReducer;
console.log(state);
return {
path,
isShow,
wxCode,
userCode,
testCase,
showLoading,
last_compile_output,
test_sets_count,
sets_error_count
};
}
const mapDispatchToProps = (dispatch) => ({
getWXCode: (identifier) => dispatch(actions.getWXCode(identifier)),
getWXCodeTestCase: (identifier, params) => dispatch(actions.getWXCodeTestCase(identifier, params)),
restoreWXCode: (identifier, params) => dispatch(actions.restoreWXCode(identifier, params)),
updateWXCodeForEditor: (code) => dispatch(actions.updateWXCodeForEditor(code)),
updateWXCodeForInterval: (identifier, path) => dispatch(actions.updateWXCodeForInterval(identifier, path)),
evaluateWxCode: (identifier, path) => dispatch(actions.evaluateWxCode(identifier, path)),
showWXCodeTextCase: (flag) => dispatch(actions.showWXCodeTextCase(flag)),
changeWXCodeEvaluateLoading: (flag) => dispatch(actions.changeWXCodeEvaluateLoading(flag))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(App);

@ -0,0 +1,275 @@
.wx-code-area{
height: 100vh;
overflow: hidden;
.wx-code-flex{
display: flex;
position: relative;
flex-direction: column;
height: 100%;
}
.wx-code-item{
flex: 1;
}
.wx-code-test{
display: flex;
justify-content: space-between;
align-items: center;
height: 120px;
background-color: #052645;
padding: 0 50px;
}
.flex-btn{
display: flex;
.icon-btn{
display: flex;
flex-direction: column;
color: #2EA4FF;
align-items: center;
.icon{
// font-size: 24px !important;
transform: scale(2);
line-height: 2;
}
.icon-reset{
transform: scale(2.4);
}
// .icon:first-child{
// transform: scale((3));
// }
&:last-child{
margin-left: 50px;
}
.icon-txt{
font-size: 24px;
}
}
}
.wx-pt-btn{
border: none;
outline: none;
border-radius: 9999px;
padding: 0 40px;
line-height: 72px;
font-size: 32px;
color: #fff;
background:#2EA4FF;
}
.wx-code-test-case{
position: fixed;
bottom: 0;
left: 0;
right: 0;
top: 0;
transform: translateY(100%);
transition: transform, opacity .3s;
opacity: 0;
// border-top-left-radius: 16px;
// border-top-right-radius: 16px;
&::before{
position: absolute;
width: 100%;
height: 100%;
content: '';
background: rgba(0,0,0,.6);
}
.text-case-list{
position: absolute;
width: 100%;
bottom: 0;
top: 150px;
background:rgba(1,14,31,1);
border-top-left-radius: 16px;
border-top-right-radius: 16px;
border:2px solid rgba(33,56,87,1);
color: #fff;
}
.list-header{
display: flex;
justify-content: space-between;
align-items: center;
height: 88px;
padding: 30px;
box-sizing: border-box;
border-bottom: 2px solid #213857;
.header-title{
color:#637DA6;
font-size: 24px;
}
.header-close{
font-size: 28px;
color: #2EA4FF;
}
}
.wxcode-test-result{
display: flex;
height: 72px;
// background: gold;
align-items: center;
padding: 0 30px;
.success{
color: rgba(68,209,95,1);
}
.fail{
color: rgba(196, 79, 78, 1);
}
.icon{
font-size: 36px !important;
position: relative;
top: -8px;
margin-right: 10px;
}
.result-txt{
font-size: 34px;
}
.result-txt-desc{
max-width: 500px;
overflow: hidden;
text-overflow:ellipsis;
white-space: nowrap;
margin: 20px;
}
}
.case-list{
position: absolute;
left: 0;
right: 0;
bottom: 0;
top: 0px;
overflow: auto;
margin-top: 88px;
padding: 0 30px 30px;
// margin: 88px 30px 0;
.case-item{
position: relative;
margin-top: 30px;
border-radius: 8px;
background:rgba(23,39,64,1);
padding: 30px;
}
&.hasResult{
margin-top: 170px;
.case-item:first-child{
margin-top: 0;
}
}
.case-item-header{
display: flex;
justify-content: space-between;
align-items: center;
.case_item_success,
.case_item_fail{
font-size: 24px !important;
transform: scale(2);
}
.case_item_success{
color: rgba(68,209,95,1);
}
.case_item_fail{
color: rgba(196, 79, 78, 1);
}
.case-item-tips{
color: #C67676;
font-size: 22px;
}
}
.item-header-desc{
font-size: 32px;
color: #405D8C;
line-height: 1.5;
// background: gold;
.icon{
position: relative;
top: -4px;
font-size: 32px !important;
margin-right: 10px;
}
&.active{
color: #fff;
}
}
.case-item-tips{
font-size: 28px;
}
.case-item-desc{
display: none;
flex-direction: column;
font-size: 28px;
line-height: 1.5;
&.active{
display: flex;
}
}
.desc-title{
color: #637DA6;
line-height: 2;
}
.text-area-style{
background:#010E1F !important;
color: #fff;
font-size: 28px;
line-height: 1.5;
border: none;
}
}
&.active{
transform: translateY(0);
opacity: 1;
// .item-header-desc{
// color: #fff;
// }
}
}
.code-evaluate-loading{
display: none;
position: fixed;
left: 0;
right: 0;
bottom: 0;
top: 0;
justify-content: center;
align-items: center;
&::before{
position: absolute;
width: 100%;
height: 100%;
content: '';
background: rgba(0,0,0,.5);
}
.loading-flex{
display: flex;
position: absolute;
flex-direction: column;
justify-content: center;
width: 100%;
height: 100%;
text-align: center;
color: #fff;
.loading-icon{
font-size: 100px !important;
}
.loading-txt{
font-size: 32px;
}
}
&.active{
display: flex;
}
}
}

@ -92,7 +92,14 @@ const types = {
/** 统计 */
GET_STATIC_INFO: 'GET_STATIC_INFO',
CHANGE_STATIC_PARAMS: 'CHANGE_STATIC_PARAMS',
CHANGE_STATIC_TOTAL: 'CHANGE_STATIC_TOTAL'
CHANGE_STATIC_TOTAL: 'CHANGE_STATIC_TOTAL',
/** 微信 */
GET_WXCODE_BY_IDENTIFIER: 'GET_WXCODE_BY_IDENTIFIER',
GET_WXCODE_TEST_CASE: 'GET_WXCODE_TEST_CASE',
UPDATE_WXCODE_BY_IDENTIFIER: 'UPDATE_WXCODE_BY_IDENTIFIER',
UPDATE_WXCODE_FOR_EDITOR: 'UPDATE_WXCODE_FOR_EDITOR',
IS_SHOW_WXCODE_TEST_CASES: 'IS_SHOW_WXCODE_TEST_CASES',
SHOW_WX_CODE_LOADING: 'SHOW_WX_CODE_LOADING'
}
export default types;

@ -12,6 +12,7 @@ import {
changePaginationInfo,
deleteItem
} from './ojList';
import {
validateOjForm,
saveOjFormCode,
@ -109,6 +110,16 @@ import {
initTotal
} from './static';
import {
getWXCode,
getWXCodeTestCase,
restoreWXCode,
updateWXCodeForEditor,
updateWXCodeForInterval,
evaluateWxCode,
showWXCodeTextCase,
changeWXCodeEvaluateLoading
} from './wxCode';
export default {
toggleTodo,
getOJList,
@ -191,5 +202,14 @@ export default {
// 统计
staticList,
changeParams,
initTotal
initTotal,
// 微信
getWXCode,
getWXCodeTestCase,
restoreWXCode,
updateWXCodeForEditor,
updateWXCodeForInterval,
evaluateWxCode,
showWXCodeTextCase,
changeWXCodeEvaluateLoading
}

@ -0,0 +1,196 @@
/*
* @Description:
* @Author: tangjiang
* @Github:
* @Date: 2020-01-15 15:41:10
* @LastEditors : tangjiang
* @LastEditTime : 2020-01-16 15:25:25
*/
import types from './actionTypes.js';
import {
fetchWxCode,
fetchWxCodeTextCase,
fetchRestoreWxCode,
fetchUpdateWxCode,
fetchWxCodeGameBuild,
fetchWxCodeGameStatus
} from '../../services/wxcodeService.js';
// 加载代码块
export const getWXCode = (identifier) => {
return (dispatch) => {
fetchWxCode(identifier).then(res => {
if (res.status === 200) {
dispatch({
type: types.GET_WXCODE_BY_IDENTIFIER,
payload: res.data.content
});
}
});
}
};
// 加载测试集
export const getWXCodeTestCase = (identifier, params) => {
return (dispatch) => {
fetchWxCodeTextCase(identifier, params).then(res => {
// console.log('加载测试集: ====>>>>>>', res);
try{
const {data = {}} = res;
console.log(data.test_sets);
dispatch({
type: types.GET_WXCODE_TEST_CASE,
payload: {
test_sets: data.test_sets || [],
game_id: data.game && data.game.id,
myIdentifier: data.myshixun.identifier,
exec_time: data.challenge.exec_time,
path: data.challenge.path,
last_compile_output: data.last_compile_output,
test_sets_count: data.test_sets_count,
sets_error_count: data.sets_error_count
}
});
} catch(err) {
console.log(err);
};
});
}
}
// 初始化
export const restoreWXCode = (identifier, params) => {
return (dispatch) => {
fetchRestoreWxCode(identifier, params).then(res => {
console.log('点击了初始化代码: ', res);
const {data} = res;
dispatch({
type: types.GET_WXCODE_BY_IDENTIFIER,
payload: data.content || ''
});
});
}
}
// 更新编辑器代码
export const updateWXCodeForEditor = (code) => {
return {
type: types.UPDATE_WXCODE_FOR_EDITOR,
payload: code
}
}
export const updateWxCode = (path, identifier, userCode, game_id, evaluate = 0,) => {
return fetchUpdateWxCode(identifier, {
path,
evaluate,
content: userCode,
game_id
});
}
// 定时更新代码内容
export const updateWXCodeForInterval = (identifier, path) => {
return (dispatch, getState) => {
const {wxCode, userCode, game_id, myIdentifier} = getState().wxcodeReducer;
if (wxCode !== userCode) {
updateWxCode(path, myIdentifier, userCode, game_id, 0);
}
}
}
// 评测
export const evaluateWxCode = (identifier, path) => {
return (dispatch, getState) => {
const {
userCode,
game_id,
myIdentifier,
exec_time,
path,
last_compile_output,
test_sets_count,
sets_error_count
} = getState().wxcodeReducer;
updateWxCode(path, myIdentifier, userCode, game_id, 1).then(res => {
// build
// const {} = res;
console.log(res);
const params = {
content_modified: 1,
sec_key: res.data.sec_key
}
console.log(params);
fetchWxCodeGameBuild(identifier, params).then(res => {
if (res.data.status === 1) {
// 定时调用 game_status fetchWxCodeGameStatus
let count = 1;
const intervalTime = 500;
function wxCodeGameStatus (intervalTime, finalTime, count, timer) {
const excuteTime = (count++) * intervalTime; // 当前执行时间
console.log(finalTime, count, excuteTime);
fetchWxCodeGameStatus(identifier).then(r => {
const { status, test_sets = [] } = r.data;
if (+status > -1 || ((excuteTime / 1000) > (finalTime + 1))) {
clearInterval(timer);
timer = null;
dispatch({
type: types.SHOW_WX_CODE_LOADING,
payload: false
});
setTimeout(() => {
// 显示测试集弹框
dispatch({
type: types.IS_SHOW_WXCODE_TEST_CASES,
payload: true
});
dispatch({
type: types.GET_WXCODE_TEST_CASE,
payload: {
test_sets,
game_id,
myIdentifier,
exec_time,
path,
last_compile_output,
test_sets_count,
sets_error_count
}
});
}, 50);
}
}).catch(err => {
dispatch({
type: types.SHOW_WX_CODE_LOADING,
payload: false
});
});
}
let timer = setInterval(() => {
wxCodeGameStatus(intervalTime, exec_time, count++, timer);
}, intervalTime);
}
})
}).catch(err => {
dispatch({
type: types.SHOW_WX_CODE_LOADING,
payload: false
});
});
}
}
// 显示测试集
export const showWXCodeTextCase = (flag) => {
return {
type: types.IS_SHOW_WXCODE_TEST_CASES,
payload: flag
}
}
// 显示测评中的状态
export const changeWXCodeEvaluateLoading = (flag) => {
return {
type: types.SHOW_WX_CODE_LOADING,
payload: flag
}
}

@ -17,6 +17,7 @@ import jupyterReducer from './jupyterReducer';
import commentReducer from './commentReducer';
import tpiReducer from './tpiReducer';
import staticReducer from './staticReducer';
import wxcodeReducer from './wxcodeReducer';
export default combineReducers({
testReducer,
@ -28,5 +29,6 @@ export default combineReducers({
jupyterReducer,
commentReducer,
tpiReducer,
staticReducer
staticReducer,
wxcodeReducer
});

@ -0,0 +1,68 @@
/*
* @Description:
* @Author: tangjiang
* @Github:
* @Date: 2020-01-15 15:37:44
* @LastEditors : tangjiang
* @LastEditTime : 2020-01-16 15:22:37
*/
import types from "../actions/actionTypes";
const initialState = {
wxCode: '',
userCode: '',
testCase: [],
game_id: '',
myIdentifier: '',
exec_time: 0,
last_compile_output: '',
test_sets_count: 0,
sets_error_count: 0,
path: '',
isShow: false,
showLoading: false
};
const wxcodeReducer = (state = initialState, action) => {
const { payload, type } = action;
switch (type) {
case types.GET_WXCODE_BY_IDENTIFIER:
return {
...state,
wxCode: payload,
userCode: payload
}
case types.GET_WXCODE_TEST_CASE:
return {
...state,
testCase: payload.test_sets,
game_id: payload.game_id,
myIdentifier: payload.myIdentifier,
exec_time: payload.exec_time,
path: payload.path,
last_compile_output: payload.last_compile_output,
test_sets_count: payload.test_sets_count,
sets_error_count: payload.sets_error_count
}
case types.UPDATE_WXCODE_FOR_EDITOR:
return {
...state,
userCode: payload
}
case types.IS_SHOW_WXCODE_TEST_CASES:
return {
...state,
isShow: payload
}
case types.SHOW_WX_CODE_LOADING:
return {
...state,
showLoading: payload
}
default:
return {
...state
}
}
}
export default wxcodeReducer;

@ -0,0 +1,50 @@
/*
* @Description:
* @Author: tangjiang
* @Github:
* @Date: 2020-01-15 15:44:36
* @LastEditors : tangjiang
* @LastEditTime : 2020-01-17 10:06:45
*/
import axios from 'axios';
// 获取代码块
export async function fetchWxCode (identifier, params) {
const url = `/tasks/${identifier}/rep_content.json`;
params = Object.assign({}, params, {withCredentials: true});
return axios.get(url, {params});
}
// 获取测试值
export async function fetchWxCodeTextCase (identifier) {
const url = `/tasks/${identifier}.json`;
const params = Object.assign({}, {withCredentials: true});
return axios.get(url, {params});
}
// 更新代码块内容
export async function fetchUpdateWxCode (identifier, params) {
// /myshixuns/8etu3pilsa/update_file.json
const url = `/myshixuns/${identifier}/update_file.json`;
params = Object.assign({}, params, {withCredentials: true});
return axios.post(url, params);
}
// 恢复初始化
export async function fetchRestoreWxCode (identifier, params) {
const url = `/tasks/${identifier}/reset_original_code.json`;
params = Object.assign({}, params, {withCredentials: true});
return axios.get(url, {params});
}
// 评测
export async function fetchWxCodeGameBuild (identifier, params) {
const url = `/tasks/${identifier}/game_build.json`;
params = Object.assign({}, params, {withCredentials: true});
return axios.get(url, {params});
}
export async function fetchWxCodeGameStatus (identifier) {
const url = `/tasks/${identifier}/game_status.json`;
const params = Object.assign({}, {withCredentials: true});
return axios.get(url, {params});
}
Loading…
Cancel
Save