7.1
This commit is contained in:
parent
634321d69d
commit
1beb4fdb86
|
@ -1,4 +1,2 @@
|
|||
.idea
|
||||
node_modules
|
||||
__pycache__
|
||||
public
|
||||
node_modules
|
|
@ -38,6 +38,7 @@
|
|||
"react-scripts": "^5.0.1",
|
||||
"recharts": "^2.12.7",
|
||||
"redux": "^5.0.1",
|
||||
"socket.io-client": "^4.7.5",
|
||||
"stream": "^0.0.2",
|
||||
"swr": "^2.2.5",
|
||||
"tailwindcss": "^3.4.3",
|
||||
|
@ -4009,6 +4010,11 @@
|
|||
"@sinonjs/commons": "^1.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@socket.io/component-emitter": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
|
||||
"integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA=="
|
||||
},
|
||||
"node_modules/@surma/rollup-plugin-off-main-thread": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmmirror.com/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz",
|
||||
|
@ -5441,7 +5447,7 @@
|
|||
},
|
||||
"node_modules/@xterm/xterm": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmmirror.com/@xterm/xterm/-/xterm-5.5.0.tgz",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.5.0.tgz",
|
||||
"integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A=="
|
||||
},
|
||||
"node_modules/@xtuc/ieee754": {
|
||||
|
@ -7865,6 +7871,46 @@
|
|||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io-client": {
|
||||
"version": "6.5.4",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.4.tgz",
|
||||
"integrity": "sha512-GeZeeRjpD2qf49cZQ0Wvh/8NJNfeXkXXcoGh+F77oEAgo9gUHwT1fCRxSNU+YEEaysOJTnsFHmM5oAcPy4ntvQ==",
|
||||
"dependencies": {
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
"debug": "~4.3.1",
|
||||
"engine.io-parser": "~5.2.1",
|
||||
"ws": "~8.17.1",
|
||||
"xmlhttprequest-ssl": "~2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io-client/node_modules/ws": {
|
||||
"version": "8.17.1",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
|
||||
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": ">=5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
"optional": true
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io-parser": {
|
||||
"version": "5.2.2",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.2.tgz",
|
||||
"integrity": "sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw==",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/enhanced-resolve": {
|
||||
"version": "5.16.0",
|
||||
"resolved": "https://registry.npmmirror.com/enhanced-resolve/-/enhanced-resolve-5.16.0.tgz",
|
||||
|
@ -16477,6 +16523,32 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io-client": {
|
||||
"version": "4.7.5",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.5.tgz",
|
||||
"integrity": "sha512-sJ/tqHOCe7Z50JCBCXrsY3I2k03iOiUe+tj1OmKeD2lXPiGH/RUCdTZFoqVyN7l1MnpIzPrGtLcijffmeouNlQ==",
|
||||
"dependencies": {
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
"debug": "~4.3.2",
|
||||
"engine.io-client": "~6.5.2",
|
||||
"socket.io-parser": "~4.2.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io-parser": {
|
||||
"version": "4.2.4",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
|
||||
"integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==",
|
||||
"dependencies": {
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
"debug": "~4.3.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/sockjs": {
|
||||
"version": "0.3.24",
|
||||
"resolved": "https://registry.npmmirror.com/sockjs/-/sockjs-0.3.24.tgz",
|
||||
|
@ -18912,6 +18984,14 @@
|
|||
"resolved": "https://registry.npmmirror.com/xmlchars/-/xmlchars-2.2.0.tgz",
|
||||
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="
|
||||
},
|
||||
"node_modules/xmlhttprequest-ssl": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz",
|
||||
"integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/y18n": {
|
||||
"version": "5.0.8",
|
||||
"resolved": "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz",
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
"react-scripts": "^5.0.1",
|
||||
"recharts": "^2.12.7",
|
||||
"redux": "^5.0.1",
|
||||
"socket.io-client": "^4.7.5",
|
||||
"stream": "^0.0.2",
|
||||
"swr": "^2.2.5",
|
||||
"tailwindcss": "^3.4.3",
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
import { Terminal } from "@xterm/xterm";
|
||||
import "@xterm/xterm/css/xterm.css";
|
||||
import React, { useEffect, useState, useRef } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import io, { Socket } from "socket.io-client";
|
||||
|
||||
function base64Decode(encoded: string): string {
|
||||
return atob(encoded);
|
||||
}
|
||||
|
||||
export default function WebSSH() {
|
||||
const location = useLocation();
|
||||
const terminalObj = useRef<HTMLDivElement | null>(null);
|
||||
const [terminal, setTerminal] = useState<Terminal | null>(null);
|
||||
const [socket, setSocket] = useState<Socket | null>(null);
|
||||
const [ip, setIp] = useState("");
|
||||
const [port, setPort] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
const params = new URLSearchParams(location.search);
|
||||
const encodedIp = params.get("ip");
|
||||
const encodedPort = params.get("port");
|
||||
const encodedPassword = params.get("password");
|
||||
|
||||
if (encodedIp && encodedPort && encodedPassword) {
|
||||
setIp(base64Decode(encodedIp));
|
||||
setPort(base64Decode(encodedPort));
|
||||
setPassword(base64Decode(encodedPassword));
|
||||
}
|
||||
}, [location]);
|
||||
|
||||
useEffect(() => {
|
||||
if (terminal === null) {
|
||||
setTerminal(new Terminal());
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (terminal !== null) {
|
||||
terminal.open(terminalObj.current as HTMLDivElement);
|
||||
|
||||
const newSocket = io("ws://36.138.114.105:32087");
|
||||
setSocket(newSocket);
|
||||
|
||||
terminal.onData((data) => {
|
||||
newSocket.emit("input", data);
|
||||
});
|
||||
|
||||
terminal.onSelectionChange(() => {
|
||||
if (terminal.hasSelection()) {
|
||||
navigator.clipboard.writeText(terminal.getSelection());
|
||||
}
|
||||
});
|
||||
|
||||
setInterval(() => {
|
||||
newSocket.emit("heartbeat");
|
||||
}, 1000);
|
||||
|
||||
newSocket.on("output", (data: string) => {
|
||||
terminal.write(data);
|
||||
});
|
||||
|
||||
return () => {
|
||||
newSocket.disconnect();
|
||||
};
|
||||
}
|
||||
}, [terminal]);
|
||||
|
||||
useEffect(() => {
|
||||
if (socket && ip && port && password) {
|
||||
socket.emit("connect_ssh", { ip, port, password });
|
||||
}
|
||||
}, [socket, ip, port, password]);
|
||||
|
||||
function pasteContent(event: React.MouseEvent) {
|
||||
navigator.clipboard.readText().then((content) => {
|
||||
socket?.emit("input", content);
|
||||
});
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div ref={terminalObj} onContextMenu={pasteContent} style={{ height: "100vh", width: "100%" }}>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -202,9 +202,8 @@
|
|||
|
||||
.calendar-box span{
|
||||
position: absolute;
|
||||
/* top: 20px; */
|
||||
top: 380px;
|
||||
right: 160px;
|
||||
top: 400px;
|
||||
right: 185px;
|
||||
}
|
||||
.calendar1{
|
||||
width: 100px;
|
||||
|
|
|
@ -3,7 +3,8 @@ import { Link } from 'react-router-dom';
|
|||
import './SendTrain.css'
|
||||
import axios from 'axios';
|
||||
import DatePicker from 'react-datepicker';
|
||||
import 'react-datepicker/dist/react-datepicker.css';
|
||||
import "react-datepicker/dist/react-datepicker.css";
|
||||
|
||||
|
||||
function SendTrain(){
|
||||
const teacher_ID=localStorage.getItem('islogin')
|
||||
|
@ -138,29 +139,17 @@ function SendTrain(){
|
|||
}
|
||||
|
||||
// 日历
|
||||
// 用于存储开始日期和结束日期的状态
|
||||
const [startDate, setStartDate] = useState(new Date());
|
||||
const [endDate, setEndDate] = useState(new Date());
|
||||
// 格式化日期显示
|
||||
const formatDate = (date) => {
|
||||
return date.toLocaleDateString();
|
||||
};
|
||||
// 选择日期时更新状态
|
||||
// 处理开始时间选择
|
||||
const handleStartDateChange = (date) => {
|
||||
setStartDate(date);
|
||||
};
|
||||
|
||||
// 处理结束时间选择
|
||||
const handleEndDateChange = (date) => {
|
||||
setEndDate(date);
|
||||
};
|
||||
// 计算并显示总天数
|
||||
const calculateTotalDays = () => {
|
||||
const differenceInTime = endDate - startDate;
|
||||
const differenceInDays = Math.floor(differenceInTime / (1000 * 60 * 60 * 24));
|
||||
return differenceInDays;
|
||||
};
|
||||
useEffect(() => {
|
||||
console.log(calculateTotalDays());
|
||||
}, [startDate, endDate]);
|
||||
|
||||
return(
|
||||
<div className='SendTrain_body'>
|
||||
|
@ -346,30 +335,35 @@ function SendTrain(){
|
|||
{/* 日历 */}
|
||||
<p>请填写考试开始、截止日期</p>
|
||||
<div className='calendar-box'>
|
||||
<DatePicker
|
||||
selected={startDate}
|
||||
onChange={handleStartDateChange}
|
||||
dateFormat="yyyy/MM/dd"
|
||||
placeholderText="选择开始日期"
|
||||
popperPlacement="left-start"
|
||||
className='calendar1'
|
||||
/>
|
||||
<span>-</span>
|
||||
<DatePicker
|
||||
selected={endDate}
|
||||
onChange={handleEndDateChange}
|
||||
dateFormat="yyyy/MM/dd"
|
||||
placeholderText="选择结束日期"
|
||||
minDate={startDate} // 设置最小日期为开始日期
|
||||
withFullScreenPortal={false} // 避免全屏模式
|
||||
popperPlacement="left-start"
|
||||
className='calendar2'
|
||||
/>
|
||||
{/* 开始时间输入框 */}
|
||||
<div className='start-date-input'>
|
||||
{/* 日历组件 */}
|
||||
<div className='calendar-picker'>
|
||||
<DatePicker
|
||||
selected={startDate}
|
||||
onChange={handleStartDateChange}
|
||||
dateFormat="yyyy-MM-dd HH:mm"
|
||||
showTimeSelect
|
||||
timeIntervals={10} // 时间选择间隔,单位为分钟
|
||||
timeCaption="time"
|
||||
minDate={new Date()} // 不能选择过去的日期
|
||||
/>
|
||||
<DatePicker
|
||||
selected={endDate}
|
||||
onChange={handleEndDateChange}
|
||||
dateFormat="yyyy-MM-dd HH:mm"
|
||||
showTimeSelect
|
||||
timeIntervals={10}
|
||||
timeCaption="time"
|
||||
minDate={startDate || new Date()} // 结束日期不能早于开始日期
|
||||
/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<p className='data-num'>总天数:{calculateTotalDays()}天</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -11,11 +11,10 @@ import { max, min } from 'lodash';
|
|||
|
||||
// 首页
|
||||
function Home(){
|
||||
const [isChecked,setisChecked]=useState(false)
|
||||
const student_ID=localStorage.getItem('islogin')
|
||||
if(student_ID==null){
|
||||
window.location.href='http://localhost:3000/signin'
|
||||
alert('未登录,请先前往登录')
|
||||
window.location.href='http://36.138.114.105:30294/'
|
||||
}
|
||||
//课表数据
|
||||
const [lesson, setLesson] = useState([]);
|
||||
|
|
|
@ -119,4 +119,13 @@
|
|||
margin-left: 5px;
|
||||
margin-top: 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.body-right1-exam{
|
||||
width: 1414px;
|
||||
height: 1540px;
|
||||
position: absolute;
|
||||
top: 64px;
|
||||
left: 230px;
|
||||
border-radius: 10px;
|
||||
background-color: #f7f8fa;
|
||||
}
|
|
@ -10,10 +10,66 @@ import top from '../img/top.jpg'
|
|||
import axios from 'axios';
|
||||
|
||||
function Exam() {
|
||||
|
||||
|
||||
//倒计时
|
||||
const [countdown, setCountdown] = useState(null);
|
||||
const {examId}=useParams()
|
||||
const student_ID=localStorage.getItem('islogin')
|
||||
const [score,setscore]=useState(0)
|
||||
const [isEntry,setisEntry]=useState(false)
|
||||
|
||||
//获取题目
|
||||
const [TestData,setTestData]=useState()
|
||||
const TestFunc=async ()=>{
|
||||
try{
|
||||
const TestSrc=await axios.post('/api/student/TestData',{
|
||||
examId
|
||||
})
|
||||
const data=TestSrc.data
|
||||
setTestData(data)
|
||||
}catch{
|
||||
alert('TestSrc出错')
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(()=>{
|
||||
TestFunc()
|
||||
},[])
|
||||
|
||||
|
||||
//添加成绩
|
||||
const scoreEntry=async()=>{
|
||||
try{
|
||||
const scoreEntrySrc=await axios.post('/api/student/score_entry',{
|
||||
student_ID,
|
||||
examId,
|
||||
score
|
||||
})
|
||||
}catch{
|
||||
alert('scoreEntry出错')
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(()=>{
|
||||
if(isEntry){
|
||||
scoreEntry()
|
||||
}
|
||||
},[score])
|
||||
|
||||
function daojishi(){
|
||||
if(localStorage.getItem('time')){
|
||||
setCountdown(localStorage.getItem('time'))
|
||||
}else{
|
||||
if(TestData){
|
||||
setCountdown(60 * parseInt(TestData['examID'][3]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(()=>{
|
||||
daojishi()
|
||||
console.log('a');
|
||||
},[TestData])
|
||||
|
||||
|
||||
// 倒计时更新
|
||||
useEffect(() => {
|
||||
|
@ -24,19 +80,22 @@ function Exam() {
|
|||
} else {
|
||||
clearInterval(interval);
|
||||
alert('考试时间到!');
|
||||
correct()
|
||||
window.location.href='http://localhost:3000/subject'
|
||||
}
|
||||
}, 1000);
|
||||
return () => clearInterval(interval);
|
||||
}, [countdown]);
|
||||
|
||||
// useEffect(()=>{
|
||||
// if(countdown<0){
|
||||
// console.log('c');
|
||||
// setCountdown(60 * parseInt(TrainData['operateID'][4]))
|
||||
// localStorage.removeItem('time')
|
||||
// }
|
||||
// },[TrainData])
|
||||
useEffect(()=>{
|
||||
if(countdown<0){
|
||||
localStorage.removeItem('time')
|
||||
setCountdown(60 * parseInt(TestData['examID'][3]))
|
||||
|
||||
}
|
||||
},[TestData])
|
||||
|
||||
|
||||
// 转换秒数为时分秒格式
|
||||
const formatTime = (seconds) => {
|
||||
const hours = Math.floor(seconds / 3600);
|
||||
|
@ -48,6 +107,109 @@ function Exam() {
|
|||
// 下拉菜单
|
||||
const [isSubjectDropdownOpen, setIsSubjectDropdownOpen] = useState(false);
|
||||
|
||||
//创建答案字典,将用户填入的答案添加到答案字典
|
||||
const [choice_answer,setchoice_answer]=useState({})
|
||||
const [completion_answer,setcompletion_answer]=useState({})
|
||||
const [judge_answer,setjudge_answer]=useState({})
|
||||
//选中反馈
|
||||
const [answeredChoice, setAnsweredChoice] = useState([]);
|
||||
const [answeredComple, setAnsweredComple] = useState([]);
|
||||
const [answeredJudge, setAnsweredJudge] = useState([]);
|
||||
|
||||
//给选择题添加事件监听器
|
||||
const handleOptionChange = (event) => {
|
||||
const selectedValue = event.target.value;
|
||||
const key = parseInt(event.target.name.replace('group', ''));
|
||||
const updatedChoiceAnswer = { ...choice_answer, [key]: selectedValue };
|
||||
setchoice_answer(updatedChoiceAnswer);
|
||||
// 更新已做题目的状态
|
||||
if (!answeredChoice.includes(key)) {
|
||||
setAnsweredChoice([...answeredChoice, key]);
|
||||
}
|
||||
};
|
||||
//填空题
|
||||
const handleInputChange = (event) => {
|
||||
const { name, value } = event.target;
|
||||
setcompletion_answer({
|
||||
...completion_answer,
|
||||
[name]: value
|
||||
});
|
||||
if (!answeredComple.includes(parseInt(name))) {
|
||||
setAnsweredComple([...answeredComple, parseInt(name)]);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
//判断题
|
||||
const handleJudgeOption=(event)=>{
|
||||
const judgeoption=event.target.value;
|
||||
const key=parseInt(event.target.name.replace('judge',''))
|
||||
const updatedJudgeAnswer={ ...judge_answer,[key]:judgeoption}
|
||||
setjudge_answer(updatedJudgeAnswer)
|
||||
if (!answeredJudge.includes(key)) {
|
||||
setAnsweredJudge([...answeredJudge, key]);
|
||||
}
|
||||
}
|
||||
|
||||
//批改
|
||||
const correct=()=>{
|
||||
let CHO_sum=0;
|
||||
let COM_sum=0;
|
||||
let JUD_sum=0;
|
||||
for(let item of TestData['examID'][0]){
|
||||
if(item[0][6]===choice_answer[CHO_sum]){
|
||||
setscore(prevscore=>prevscore+5)
|
||||
}
|
||||
CHO_sum++
|
||||
}
|
||||
for(let item of TestData['examID'][1]){
|
||||
if (item[0][2]===completion_answer[COM_sum]) {
|
||||
setscore(prevscore=>prevscore+5)
|
||||
}
|
||||
COM_sum++
|
||||
}
|
||||
for(let item of TestData['examID'][2]){
|
||||
if (item[0][2]===judge_answer[JUD_sum]) {
|
||||
setscore(prevscore=>prevscore+5)
|
||||
}
|
||||
JUD_sum++
|
||||
}
|
||||
setisEntry(true)
|
||||
}
|
||||
|
||||
|
||||
const submit=()=>{
|
||||
correct()
|
||||
setCountdown(0)
|
||||
localStorage.removeItem('time')
|
||||
alert('提交成功')
|
||||
window.location.href='http://localhost:3000/subject'
|
||||
|
||||
}
|
||||
|
||||
|
||||
// 题目预览框
|
||||
const [isFixed,setIsFixed]=useState(false)
|
||||
// 创建一个函数来处理滚动逻辑
|
||||
const handleScroll = () => {
|
||||
const scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
|
||||
const fixedThreshold = 100; // 滚动到100px后固定
|
||||
|
||||
if (scrollTop > fixedThreshold) {
|
||||
setIsFixed(true);
|
||||
} else {
|
||||
setIsFixed(false);
|
||||
}
|
||||
};
|
||||
// 在组件挂载后添加滚动事件监听器,并在卸载时移除
|
||||
useEffect(() => {
|
||||
window.addEventListener('scroll', handleScroll);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('scroll', handleScroll);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className='nav-exam'>
|
||||
|
@ -78,32 +240,99 @@ function Exam() {
|
|||
</ul>
|
||||
{/* 题目预览框 */}
|
||||
{/* 根据isFixed状态来设置countBox的类 */}
|
||||
{/* <div className={`countBox ${isFixed ? 'fixed-count-box' : ''}`}>
|
||||
{TrainData&&<table>
|
||||
<div className={`countBox ${isFixed ? 'fixed-count-box' : ''}`}> {/* 根据isFixed状态来设置countBox的类 */}
|
||||
{TestData&&<table>
|
||||
<tr>选择题:</tr>
|
||||
<div>
|
||||
{Object.keys(TrainData['operateID'][0]).map((key,index)=>(
|
||||
{Object.keys(TestData['examID'][0]).map((key,index)=>(
|
||||
<td key={key} className={answeredChoice.includes(index) ? 'answered' : ''}>{index+1}</td>
|
||||
))}
|
||||
</div>
|
||||
<tr>填空题:</tr>
|
||||
<div>
|
||||
{Object.keys(TrainData['operateID'][1]).map((key,index)=>(
|
||||
{Object.keys(TestData['examID'][1]).map((key,index)=>(
|
||||
<td key={key} className={answeredComple.includes(index) ? 'answered' : ''}>{index+1}</td>
|
||||
))}
|
||||
</div>
|
||||
<tr>判断题:</tr>
|
||||
<div>
|
||||
{Object.keys(TrainData['operateID'][2]).map((key,index)=>(
|
||||
{Object.keys(TestData['examID'][2]).map((key,index)=>(
|
||||
<td key={key} className={answeredJudge.includes(index) ? 'answered' : ''}>{index+1}</td>
|
||||
))}
|
||||
</div>
|
||||
</table>}
|
||||
</div> */}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div className='body-right1'>
|
||||
{TestData&&<table>
|
||||
<p>选择题</p>
|
||||
<tbody>
|
||||
{Object.keys(TestData['examID'][0]).map((key,index) => (
|
||||
<tr key={key}>
|
||||
<tr className='test-title'>{index+1}.{TestData['examID'][0][key][0][1]}</tr>
|
||||
<div className='optionBox'>
|
||||
<label>
|
||||
<input type="radio" value={TestData['examID'][0][key][0][2]} checked={choice_answer[key] === TestData['examID'][0][key][0][2]} onChange={handleOptionChange} name={`group${key}`}/>
|
||||
<span>A.{TestData['examID'][0][key][0][2]}</span>
|
||||
</label>
|
||||
<br />
|
||||
<label>
|
||||
<input type="radio" value={TestData['examID'][0][key][0][3]} checked={choice_answer[key] === TestData['examID'][0][key][0][3]} onChange={handleOptionChange} name={`group${key}`}/>
|
||||
<span>B.{TestData['examID'][0][key][0][3]}</span>
|
||||
</label>
|
||||
<br />
|
||||
<label>
|
||||
<input type="radio" value={TestData['examID'][0][key][0][4]} checked={choice_answer[key] === TestData['examID'][0][key][0][4]} onChange={handleOptionChange} name={`group${key}`}/>
|
||||
<span>C.{TestData['examID'][0][key][0][4]}</span>
|
||||
</label>
|
||||
<br />
|
||||
<label>
|
||||
<input type="radio" value={TestData['examID'][0][key][0][5]} checked={choice_answer[key] === TestData['examID'][0][key][0][5]} onChange={handleOptionChange} name={`group${key}`}/>
|
||||
<span>D.{TestData['examID'][0][key][0][5]}</span>
|
||||
</label>
|
||||
</div>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>}
|
||||
|
||||
{TestData&&(
|
||||
<table className='body-right1-comple'>
|
||||
<tbody>
|
||||
<p>填空题</p>
|
||||
{Object.keys(TestData['examID'][1]).map((key,index)=>(
|
||||
<tr key={key}>
|
||||
<tr className='test-title1'>{index+1}.{TestData['examID'][1][key][0][1]}</tr>
|
||||
{/* <br /> */}
|
||||
<div className='input-box'>
|
||||
<span>请填入你的答案:</span>
|
||||
<input type="text" onChange={handleInputChange} name={key} value={completion_answer[key]||''}/>
|
||||
</div>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
)}
|
||||
|
||||
{TestData&&(
|
||||
<table>
|
||||
<tbody>
|
||||
<p>判断题</p>
|
||||
{Object.keys(TestData['examID'][2]).map((key,index)=>(
|
||||
<tr key={key} className='panduan-box'>
|
||||
<tr>{index+1}.{TestData['examID'][2][key][0][1]}</tr>
|
||||
<tr>
|
||||
<label><input type="radio" name={`judge${key}`} checked={judge_answer[key] === 'true'} onChange={handleJudgeOption} value={true} />T</label>
|
||||
<label><input type="radio" name={`judge${key}`} checked={judge_answer[key] === 'false'} onChange={handleJudgeOption} value={false} />F</label>
|
||||
</tr>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
)}
|
||||
<button onClick={submit}>提交</button>
|
||||
</div>
|
||||
{/* <div className='body-right1-exam'>
|
||||
|
||||
</div> */}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -57,19 +57,18 @@ function SubjectPage(){
|
|||
))}
|
||||
</div>}
|
||||
<h3>已结束或完成的试卷与练习</h3>
|
||||
{ testdata&& result&&<div className='test-subject'>
|
||||
{Object.keys(testdata).map((key)=>{
|
||||
return(
|
||||
<div className='test-box' key={key}>
|
||||
<p>试卷ID:{testdata[key][0][3]}</p>
|
||||
<span>发布者:{'小陈老师'}</span>
|
||||
<div>发布时间:{testdata[key][1][0][5]}</div>
|
||||
<div>截至时间:{testdata[key][1][0][6]}</div>
|
||||
<div id='score'>{testdata[key][0][5]}</div>
|
||||
<div className='test-subject'>
|
||||
{ Object.keys(testdata).slice(-3).reverse().map((key) => (
|
||||
<div className='test-box' key={key}>
|
||||
<p>试卷ID:{testdata[key][0][3]}</p>
|
||||
<span>发布者:{'小陈老师'}</span>
|
||||
<div>发布时间:{testdata[key][1][0][5]}</div>
|
||||
<div>截至时间:{testdata[key][1][0][6]}</div>
|
||||
<div id='score'>{testdata[key][0][5]}</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>}
|
||||
))}
|
||||
</div>
|
||||
<button>更多历史</button>
|
||||
{ NotFalse&& result&&<div className='test-subject'>
|
||||
{Object.keys(NotFalse).map((key)=>{
|
||||
return(
|
||||
|
|
|
@ -16,19 +16,9 @@ function Operation1() {
|
|||
console.log(60 * parseInt(TrainData['operateID'][4]));
|
||||
}
|
||||
const clear=()=>{
|
||||
localStorage.removeItem('choice_answers');
|
||||
setchoice_answer({})
|
||||
localStorage.removeItem('completion_answer')
|
||||
setcompletion_answer({})
|
||||
localStorage.removeItem('judge_answer')
|
||||
setjudge_answer({})
|
||||
localStorage.removeItem('answeredChoice')
|
||||
setAnsweredChoice([])
|
||||
localStorage.removeItem('answeredComple')
|
||||
setAnsweredComple([])
|
||||
localStorage.removeItem('answeredJudge')
|
||||
setAnsweredJudge([])
|
||||
localStorage.removeItem('RemainingTime')
|
||||
['time', 'choice_answers', 'completion_answer', 'judge_answer',
|
||||
'answeredChoice', 'answeredComple', 'answeredJudge', 'RemainingTime', 'TrainData']
|
||||
.forEach(item => localStorage.removeItem(item));
|
||||
}
|
||||
|
||||
const { operateID } = useParams();
|
||||
|
@ -85,7 +75,6 @@ function Operation1() {
|
|||
|
||||
useEffect(()=>{
|
||||
if(countdown<0){
|
||||
console.log('c');
|
||||
setCountdown(60 * parseInt(TrainData['operateID'][4]))
|
||||
localStorage.removeItem('time')
|
||||
}
|
||||
|
|
|
@ -146,8 +146,16 @@ function Operation2() {
|
|||
const tijiao = ()=>{
|
||||
setCountdown(0)
|
||||
localStorage.removeItem('time')
|
||||
localStorage.removeItem('choice_answers');
|
||||
localStorage.removeItem('completion_answer')
|
||||
localStorage.removeItem('judge_answer')
|
||||
localStorage.removeItem('answeredChoice')
|
||||
localStorage.removeItem('answeredComple')
|
||||
localStorage.removeItem('answeredJudge')
|
||||
localStorage.removeItem('RemainingTime')
|
||||
alert('提交成功')
|
||||
window.location.href='http://localhost:3000/train'
|
||||
localStorage.removeItem('TrainData')
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -184,7 +192,7 @@ function Operation2() {
|
|||
|
||||
<button onClick={click}>测试按钮</button>
|
||||
{/* 前端连接数据库⬇ */}
|
||||
{ !isTrue&&<div>
|
||||
{ !isTrue&&TrainData&&<div>
|
||||
<Link to={Src}>{TrainData['operateID'][3][0]}</Link>
|
||||
<br />
|
||||
点击以上链接前往实训
|
||||
|
@ -192,7 +200,7 @@ function Operation2() {
|
|||
{/* 前端连接数据库 ⬆*/}
|
||||
|
||||
{/* 达梦数据库连接 ⬇*/}
|
||||
{ isTrue&&
|
||||
{ isTrue&&TrainData&&
|
||||
<div>
|
||||
<p>{TrainData['operateID'][3][0]}</p>
|
||||
<div style={{width: "1430px",position:'absolute',right:'0'}} ref={terminalObj} onContextMenu={pasteContent}></div>
|
||||
|
|
|
@ -23,11 +23,12 @@ import ClassID from '../TeacherPages/classlistpages/classId';
|
|||
import ManageTest from '../TeacherPages/TestManage/ManageTest';
|
||||
import SendTest from '../TeacherPages/TestManage/SendTest';
|
||||
import Marking from '../TeacherPages/MarkingPages/Marking';
|
||||
import SendTrain from '../TeacherPages/TrainManage/SendTrain'
|
||||
import TrainManage from '../TeacherPages/TrainManage/Trainmanage'
|
||||
import StudentLink from '../TeacherPages/MarkingPages/StudentLink';
|
||||
|
||||
|
||||
import SendTrain from '../TeacherPages/TrainManage/SendTrain';
|
||||
import TrainManage from '../TeacherPages/TrainManage/Trainmanage';
|
||||
import Test from "../TeacherPages/teacherManagetest/Test.tsx"
|
||||
// 定义一个组件来包裹除了特定页面(exam)外的所有页面使其有导航
|
||||
const MainLayout = ({ headerNav:HeaderNav }) => {
|
||||
return (
|
||||
|
@ -69,6 +70,7 @@ function App() {
|
|||
<Route path="/classID/:key" element={<MainLayout headerNav={HeaderNavTeacher} />}>
|
||||
<Route index element={<ClassID/>} />
|
||||
</Route>
|
||||
<Route path="test" element={<Test />}/>
|
||||
|
||||
{/* 页面使用单独的布局,不包含HeaderNav和HeaderNavTeacher */}
|
||||
<Route path="exam/:examId" element={<Exam />} />{/* 练习-考试页面 */}
|
||||
|
|
14
build.sh
14
build.sh
|
@ -20,13 +20,14 @@ build_image() {
|
|||
|
||||
# 推送镜像
|
||||
push_image() {
|
||||
local service_name=$1
|
||||
read -p "是否要推送到《达梦启元云原生大数据平台》? [Y/N] " confirm
|
||||
if [[ $confirm =~ ^[yY]([eE][sS])?$ ]]; then
|
||||
echo "tag"
|
||||
docker tag "${current_dir}-flask-app" 36.138.114.105:31000/cnsof50011836/flask-app
|
||||
echo "pushing"
|
||||
docker push 36.138.114.105:31000/cnsof50011836/flask-app
|
||||
echo "pushed"
|
||||
echo "${service_name} tag"
|
||||
docker tag "${service_name}" 36.138.114.105:31000/cnsof50011836/$service_name
|
||||
echo "${service_name} pushing"
|
||||
docker push 36.138.114.105:31000/cnsof50011836/$service_name
|
||||
echo "${service_name} pushed"
|
||||
else
|
||||
echo "用户选择不推送镜像"
|
||||
fi
|
||||
|
@ -43,7 +44,8 @@ main() {
|
|||
build_image build-frontend
|
||||
build_image flask-app
|
||||
|
||||
push_image
|
||||
push_image flask-app
|
||||
push_image base-dm
|
||||
|
||||
echo "脚本结束"
|
||||
}
|
||||
|
|
|
@ -23,14 +23,11 @@ services:
|
|||
flask-app:
|
||||
build: ./python
|
||||
restart: always
|
||||
image: flask-app
|
||||
depends_on:
|
||||
- base-dm
|
||||
ports:
|
||||
- '8000:8000'
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "curl --silent --fail localhost:8000/flask-health-check || exit 1"]
|
||||
interval: 10s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
- '8765:8765'
|
||||
command: gunicorn -w 3 -t 60 -b 0.0.0.0:8000 app:app
|
||||
|
||||
|
|
|
@ -6,8 +6,8 @@ RUN useradd -m -s /bin/bash dmdba \
|
|||
WORKDIR /home/dmdba
|
||||
|
||||
# 传入安装包并解压
|
||||
RUN wget https://download.dameng.com/eco/adapter/DM8/202405/dm8_20240408_x86_rh7_64_ent_8.1.3.140.zip
|
||||
RUN unzip dm8_20240408_x86_rh7_64_ent_8.1.3.140.zip \
|
||||
RUN wget https://download.dameng.com/eco/adapter/DM8/202405/dm8_20240408_x86_rh7_64_ent_8.1.3.140.zip \
|
||||
&& unzip dm8_20240408_x86_rh7_64_ent_8.1.3.140.zip \
|
||||
&& chown -R dmdba:dmdba /home/dmdba \
|
||||
&& rm -f dm8_20240408_x86_rh7_64_ent_8.1.3.140.zip \
|
||||
&& 7z x dm8_20240408_x86_rh7_64.iso \
|
||||
|
|
|
@ -9,6 +9,8 @@ RUN sed -i 's@//.*archive.ubuntu.com@//mirrors.ustc.edu.cn@g' /etc/apt/sources.l
|
|||
&& apt-get install -y python3.10-venv libssl-dev sudo vim python3-pip ssh wget unzip p7zip* language-pack-zh-hans language-selector-common locales locales-all \
|
||||
# 设置中文环境
|
||||
&& apt install -y $(check-language-support) \
|
||||
&& sed -i 's/^#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config \
|
||||
&& sudo sed -i 's/^UsePAM yes/UsePAM no/' /etc/ssh/sshd_config \
|
||||
&& echo "zh_CN.UTF-8 UTF-8" >> /etc/locale.gen \
|
||||
&& sudo /usr/sbin/update-locale LANG=zh_CN.UTF-8 LC_ALL=zh_CN.UTF-8 \
|
||||
&& locale-gen \
|
||||
|
|
|
@ -1,26 +1 @@
|
|||
FROM ubuntu
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
ENV LANG=zh_CN.UTF-8
|
||||
ENV LC_ALL=zh_CN.UTF-8
|
||||
# 基础软件安装
|
||||
RUN sed -i 's@//.*archive.ubuntu.com@//mirrors.ustc.edu.cn@g' /etc/apt/sources.list \
|
||||
&& apt-get -y update && apt-get -y upgrade \
|
||||
&& apt-get install -y sudo vim python3-pip ssh wget unzip p7zip* language-pack-zh-hans language-selector-common locales locales-all \
|
||||
# 设置中文环境
|
||||
&& apt install -y $(check-language-support) \
|
||||
&& echo "zh_CN.UTF-8 UTF-8" >> /etc/locale.gen \
|
||||
&& sudo /usr/sbin/update-locale LANG=zh_CN.UTF-8 LC_ALL=zh_CN.UTF-8 \
|
||||
&& locale-gen \
|
||||
# 添加用户
|
||||
&& useradd -m -s /bin/bash dmdba \
|
||||
# 修改用户密码
|
||||
&& echo "dmdba:123456" | chpasswd
|
||||
# 传入安装包并解压
|
||||
WORKDIR /home/dmdba
|
||||
RUN wget https://download.dameng.com/eco/adapter/DM8/202405/dm8_20240408_x86_rh7_64_ent_8.1.3.140.zip
|
||||
RUN unzip dm8_20240408_x86_rh7_64_ent_8.1.3.140.zip \
|
||||
&& chown -R dmdba:dmdba /home/dmdba \
|
||||
&& rm -f dm8_20240408_x86_rh7_64_ent_8.1.3.140.zip \
|
||||
&& 7z x dm8_20240408_x86_rh7_64.iso \
|
||||
&& rm -f dm8_20240408_x86_rh7_64.iso \
|
||||
&& chmod +x DMInstall.bin
|
||||
FROM base-dm
|
|
@ -0,0 +1,27 @@
|
|||
SHELL=/bin/bash
|
||||
KUBERNETES_SERVICE_PORT_HTTPS=443
|
||||
KUBERNETES_SERVICE_PORT=443
|
||||
HOSTNAME=flask-app-deployment-76d66d98b8-s6hxs
|
||||
DMCCA_CANVAS_NAME=flask-app
|
||||
PWD=/home/flask
|
||||
LOGNAME=flask
|
||||
TZ=Asia/Shanghai
|
||||
HOME=/home/flask
|
||||
LANG=zh_CN.UTF-8
|
||||
KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
|
||||
VIRTUAL_ENV=/home/flask/venv
|
||||
TERM=xterm
|
||||
USER=flask
|
||||
DMCCA_CANVAS_NAMESPACE=cnsof50011836-system
|
||||
SHLVL=2
|
||||
KUBERNETES_PORT_443_TCP_PROTO=tcp
|
||||
KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
|
||||
KUBERNETES_SERVICE_HOST=10.96.0.1
|
||||
LC_ALL=zh_CN.UTF-8
|
||||
KUBERNETES_PORT=tcp://10.96.0.1:443
|
||||
KUBERNETES_PORT_443_TCP_PORT=443
|
||||
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
|
||||
MAIL=/var/mail/flask
|
||||
DEBIAN_FRONTEND=noninteractive
|
||||
OLDPWD=/root
|
||||
_=/usr/bin/printenv
|
|
@ -0,0 +1,3 @@
|
|||
.idea
|
||||
__pycache__
|
||||
public
|
|
@ -4,10 +4,12 @@ FROM base
|
|||
RUN pip install --upgrade pip
|
||||
|
||||
# 创建一个用户运行flask
|
||||
RUN adduser flask && \
|
||||
RUN useradd -m -s /bin/bash flask && \
|
||||
chown -R flask:flask /home/flask && \
|
||||
mkdir -p /var/log/flask-app && touch /var/log/flask-app/flask-app.err.log && touch /var/log/flask-app/flask-app.out.log && \
|
||||
chown -R flask:flask /var/log/flask-app
|
||||
chown -R flask:flask /var/log/flask-app && \
|
||||
service ssh start && \
|
||||
service ssh restart
|
||||
WORKDIR /home/flask
|
||||
USER flask
|
||||
|
||||
|
@ -24,10 +26,12 @@ RUN python3 -m venv "$VIRTUAL_ENV" && \
|
|||
export FLASK_APP=app.py && \
|
||||
pip3 install dmPython-2.5.5-cp310-cp310-linux_x86_64.whl && rm dmPython-2.5.5-cp310-cp310-linux_x86_64.whl && \
|
||||
MAKEFLAGS="-j$(nproc)" pip install -r requirements.txt
|
||||
|
||||
COPY --from=build-dmpython /home/dmdba/build_artifacts/libdmdpi.so /usr/lib/
|
||||
COPY --from=build-frontend /usr/src/app/build/ /home/flask/public/
|
||||
|
||||
# 暴露端口
|
||||
EXPOSE 5000
|
||||
EXPOSE 8000 22 8765
|
||||
USER root
|
||||
|
||||
CMD ["gunicorn", "-w", "3", "-t", "60", "-b", "0.0.0.0:8000", "app:app"]
|
|
@ -1,12 +1,28 @@
|
|||
import subprocess
|
||||
import threading
|
||||
import time
|
||||
|
||||
import paramiko
|
||||
|
||||
from teacher_func import *
|
||||
from student_func import *
|
||||
|
||||
from flask import Flask, render_template, request, jsonify, send_from_directory, session
|
||||
from flask_cors import CORS
|
||||
|
||||
from flask_socketio import SocketIO, emit, disconnect
|
||||
import logging
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
app = Flask(__name__, static_folder="public")
|
||||
CORS(app, resources={r"/*": {"origins": "*"}})
|
||||
CORS(app)
|
||||
cors = CORS(app, resource={
|
||||
r"/*":{
|
||||
"origins":"*"
|
||||
}
|
||||
})
|
||||
app.config['SECRET_KEY'] = '350625'
|
||||
socketio = SocketIO(app, cors_allowed_origins="*")
|
||||
|
||||
clients = {}
|
||||
|
||||
@app.route('/api/student', methods=['POST']) # 检测历史是否登录过,登录则直接进入index,反之则进去login界面
|
||||
def student(): # 判断是否已经登录
|
||||
|
@ -103,10 +119,11 @@ def get_test(): # 获取试卷以及历史试卷
|
|||
result2=falseTest_func(ID)
|
||||
return jsonify({"True":result1,'FalseTest':result2})
|
||||
|
||||
@app.route('/api/student/get_testID',methods=['POST'])
|
||||
def get_testID(): # 获取试卷ID
|
||||
ID=request.json['student_ID']
|
||||
return jsonify({'data': 'a'})
|
||||
@app.route('/api/student/TestData',methods=['POST'])
|
||||
def TestData(): # 获取试卷ID
|
||||
examID=request.json['examId']
|
||||
result=TestDataFunc(examID)
|
||||
return result
|
||||
|
||||
|
||||
@app.route('/api/student/fetch_result', methods=['POST']) # 查找成绩
|
||||
|
@ -115,11 +132,7 @@ def fetch_result():
|
|||
ID = data['student_ID']
|
||||
return jsonify({'result': fetch_result_func(ID)})
|
||||
|
||||
@app.route('/api/student/get_end_student', methods=['POST'])
|
||||
def get_end_student():
|
||||
data = request.json
|
||||
ID = data['student_ID']
|
||||
return jsonify({'result': find_end_test(ID)})
|
||||
|
||||
|
||||
|
||||
@app.route('/api/student/score_entry', methods=['POST'])
|
||||
|
@ -183,6 +196,12 @@ def select_class(): # 查找每个老师管理的班级 以及在考试管理
|
|||
# print(testID)
|
||||
return jsonify({'Class': class_dic,'default': find_default_class_func(default_class, teacher_id)})
|
||||
|
||||
@app.route('/api/teacher/marktrain',methods=['POST'])
|
||||
def mark(): # 批阅试卷
|
||||
ClassID=request.json['ClassID']
|
||||
teacher_ID=request.json['teacher_ID']
|
||||
return jsonify({'MarkClass': MarkTrainFunc(ClassID,teacher_ID)})
|
||||
|
||||
@app.route('/api/teacher/change_class', methods=['POST'])
|
||||
def change_class(): # 查找每个老师管理的班级 以及在考试管理查找默认班级
|
||||
teacher_id = request.json['teacher_ID']
|
||||
|
@ -242,6 +261,7 @@ def SendTrainTest():
|
|||
Train=data['Train']
|
||||
teacher_id=data['teacher_ID']
|
||||
SendTrainTestFunc(TrainChoice, TrainCompletion, TrainJudge, Hour, Min, StopTime,Class,Train,teacher_id)
|
||||
TeacherMark(Class,teacher_id)
|
||||
return '发布成功'
|
||||
|
||||
|
||||
|
@ -250,7 +270,6 @@ def Find_details():
|
|||
data=request.json
|
||||
ID=data['ID']
|
||||
result=Find_details_Func(ID)
|
||||
print(result)
|
||||
return jsonify({'TestScore':result})
|
||||
|
||||
|
||||
|
@ -260,7 +279,56 @@ def catch_all(path = "index.html"):
|
|||
return send_from_directory("public", path)
|
||||
|
||||
|
||||
@socketio.on('connect_ssh')
|
||||
def handle_connect_ssh(data):
|
||||
ip = data['ip']
|
||||
port = int(data['port'])
|
||||
password = data['password']
|
||||
client_id = request.sid
|
||||
|
||||
ssh_client = paramiko.SSHClient()
|
||||
ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
|
||||
try:
|
||||
ssh_client.connect(ip, port=port, username='your-username', password=password)
|
||||
transport = ssh_client.get_transport()
|
||||
channel = transport.open_session()
|
||||
channel.get_pty()
|
||||
channel.invoke_shell()
|
||||
|
||||
clients[client_id] = {
|
||||
'ssh_client': ssh_client,
|
||||
'channel': channel
|
||||
}
|
||||
|
||||
socketio.start_background_task(target=read_from_channel, channel=channel, client_id=client_id)
|
||||
except Exception as e:
|
||||
emit('output', f'Connection failed: {str(e)}')
|
||||
|
||||
|
||||
def read_from_channel(channel, client_id):
|
||||
while True:
|
||||
if channel.recv_ready():
|
||||
data = channel.recv(1024).decode('utf-8')
|
||||
socketio.emit('output', data, room=client_id)
|
||||
|
||||
|
||||
@socketio.on('input')
|
||||
def handle_input(data):
|
||||
client_id = request.sid
|
||||
if client_id in clients:
|
||||
clients[client_id]['channel'].send(data)
|
||||
|
||||
|
||||
@socketio.on('disconnect')
|
||||
def handle_disconnect():
|
||||
client_id = request.sid
|
||||
if client_id in clients:
|
||||
clients[client_id]['channel'].close()
|
||||
clients[client_id]['ssh_client'].close()
|
||||
del clients[client_id]
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(host='0.0.0.0', port=5000, debug=True)
|
||||
socketio.run(app, host='0.0.0.0', port=5000 ,allow_unsafe_werkzeug=True)
|
||||
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: NAME
|
||||
namespace: ""
|
||||
labels:
|
||||
app: NAME
|
||||
spec:
|
||||
containers:
|
||||
- name: base-dm
|
||||
image: harbor.dameng.io/cnsof50011836/base-dm
|
||||
imagePullPolicy: IfNotPresent
|
||||
resources:
|
||||
limits:
|
||||
cpu: '1'
|
||||
memory: 1Gi
|
||||
requests:
|
||||
cpu: '0.25'
|
||||
memory: 0.5G
|
|
@ -1,4 +1,8 @@
|
|||
Flask==2.3.0
|
||||
Flask_Cors==4.0.0
|
||||
pycryptodome==3.19.0
|
||||
gunicorn
|
||||
gunicorn
|
||||
kubernetes
|
||||
websockets
|
||||
paramiko
|
||||
flask-socketio
|
|
@ -117,10 +117,9 @@ def get_lesson_func(id):
|
|||
|
||||
def student_succeed_func(ID):
|
||||
cursor = db.cursor()
|
||||
cursor.execute(f'SELECT * FROM STUDENT WHERE ID={ID}')
|
||||
cursor.execute(f'SELECT * FROM STUDENT WHERE ID={ID}')# 报错
|
||||
student_succeed = cursor.fetchall()[0]
|
||||
student_succeeds = student_succeed[0], student_succeed[1], student_succeed[3], student_succeed[4]
|
||||
print(student_succeeds)
|
||||
cursor.close()
|
||||
return student_succeeds
|
||||
|
||||
|
@ -128,17 +127,27 @@ def student_succeed_func(ID):
|
|||
# fetch_testID('20240101')
|
||||
def fetch_test_func(ID):#获取试卷
|
||||
cursor = db.cursor()
|
||||
correct=[]
|
||||
TrueTestDic={}
|
||||
TruetestID=[]
|
||||
cursor.execute(f"SELECT * FROM STUDENT_TEST WHERE STUDENT_ID=? AND TF='true'",(ID))
|
||||
for i in cursor.fetchall():
|
||||
correct.append(i)
|
||||
|
||||
correct=correct[::-1][:3]
|
||||
|
||||
for i in correct:
|
||||
TruetestID.append(i[3])
|
||||
TrueTestDic[i[3]]=i
|
||||
|
||||
TruetestID=TruetestID[::-1][:3]
|
||||
|
||||
for index in TruetestID:
|
||||
cursor.execute(f"SELECT * FROM TEST_BANK WHERE ID=?",(index))
|
||||
TrueTestDic[index]=TrueTestDic[index],cursor.fetchall()
|
||||
|
||||
# print(TrueTestDic)
|
||||
|
||||
return TrueTestDic
|
||||
# fetch_test_func('20240101')
|
||||
|
||||
|
@ -166,6 +175,53 @@ def falseTest_func(ID):
|
|||
|
||||
# falseTest_func('20240101')
|
||||
|
||||
|
||||
def TestDataFunc(examID):
|
||||
cursor = db.cursor()
|
||||
cursor.execute(f"SELECT * FROM TEST_BANK WHERE ID=?",(examID,))
|
||||
TestID=cursor.fetchall()[0]
|
||||
ChoiceList=json.loads(TestID[0])
|
||||
CompleList=json.loads(TestID[1])
|
||||
JudgeList=json.loads(TestID[2])
|
||||
|
||||
Choice=[]
|
||||
Comple=[]
|
||||
Judge=[]
|
||||
|
||||
CHOICE=[str(x) for x in ChoiceList]
|
||||
COMPLE=[str(x) for x in CompleList]
|
||||
JUDGE=[str(x) for x in JudgeList]
|
||||
|
||||
for i in CHOICE:
|
||||
cursor.execute(f"SELECT * FROM CHOICE_QUESTION_BANK WHERE ID=?",(i))
|
||||
Choice.append(cursor.fetchall())
|
||||
|
||||
for i in COMPLE:
|
||||
cursor.execute(f"SELECT * FROM COMPLETION_QUESTION_BANK WHERE ID=?",(i))
|
||||
Comple.append(cursor.fetchall())
|
||||
|
||||
for i in JUDGE:
|
||||
cursor.execute(f"SELECT * FROM T_OR_F_QUESTION_BANK WHERE ID=?",(i))
|
||||
Judge.append(cursor.fetchall())
|
||||
|
||||
|
||||
HOUR=TestID[3]
|
||||
MIN=TestID[4]
|
||||
if int(HOUR)==0:
|
||||
time=int(MIN)
|
||||
elif int(MIN)==0:
|
||||
time=int(HOUR)*60
|
||||
else:
|
||||
time=int(HOUR)*60+int(MIN)
|
||||
|
||||
dic={'examID':[Choice,Comple,Judge,time]}
|
||||
|
||||
# print(dic)
|
||||
|
||||
return dic
|
||||
|
||||
# TestDataFunc('35')
|
||||
|
||||
# 查找学生已经完成的记录
|
||||
def find_end_test(ID):
|
||||
cursor = db.cursor()
|
||||
|
@ -196,12 +252,22 @@ def FetchTrainTitleFunc(ID):
|
|||
titles.append(i[2])
|
||||
|
||||
for i in titles:
|
||||
cursor.execute('SELECT * FROM TRAINTEST WHERE ID=? ',(i))
|
||||
title.append(cursor.fetchall())
|
||||
cursor.execute('SELECT * FROM TRAINTEST WHERE ID=? AND STOPTIME>? ',(i,Time))
|
||||
result=cursor.fetchall()
|
||||
title.append(result)
|
||||
if result:
|
||||
pass
|
||||
else:
|
||||
cursor.execute("UPDATE TRAINSCORE SET TF='true' , RESULT='0' , TRAINRESULT='0' WHERE TEST_ID=? AND STUDENT_ID=?",(i,ID))
|
||||
|
||||
|
||||
title = [item for item in title if item]
|
||||
print(title)
|
||||
db.commit()
|
||||
cursor.close()
|
||||
return title
|
||||
|
||||
# FetchTrainTitleFunc("20240101")
|
||||
|
||||
|
||||
def FetchTrainTestFunc(operateID):
|
||||
|
|
|
@ -7,6 +7,8 @@ import datetime
|
|||
|
||||
db = dmPython.connect(user='SYSDBA', password='dameng!!', host="36.138.114.105", port="32522")
|
||||
|
||||
|
||||
|
||||
#获取题目
|
||||
def choice_question_func(ID):
|
||||
question_cursor = db.cursor()
|
||||
|
@ -218,6 +220,9 @@ def train_question():
|
|||
# print(Train)
|
||||
return Train
|
||||
|
||||
|
||||
testID=None
|
||||
TrainName=""
|
||||
# train_question()
|
||||
def SendTrainTestFunc(TrainChoice,TrainCompletion,TrainJudge,Hour,Min,StopTime,Class,Train,teacher_id):
|
||||
TrainChoice = json.dumps(TrainChoice)
|
||||
|
@ -225,6 +230,8 @@ def SendTrainTestFunc(TrainChoice,TrainCompletion,TrainJudge,Hour,Min,StopTime,C
|
|||
TrainJudge = json.dumps(TrainJudge)
|
||||
Class=json.dumps(Class)
|
||||
Train=json.dumps(Train)
|
||||
global TrainName
|
||||
TrainName=Train
|
||||
# 获取当前时间
|
||||
now_time = datetime.datetime.now().replace(microsecond=0)
|
||||
end_time = now_time + datetime.timedelta(days=int(StopTime))
|
||||
|
@ -258,13 +265,27 @@ def SendTrainTestFunc(TrainChoice,TrainCompletion,TrainJudge,Hour,Min,StopTime,C
|
|||
(i, i[0:6], test_id, end,one))
|
||||
|
||||
# 发布实训的时候根据实训不同给予不同链接
|
||||
|
||||
cursor.execute(f"SELECT ID FROM TRAINTEST WHERE RELEASETIME=? ", (now_time))
|
||||
test_id = cursor.fetchall()[0][0]
|
||||
global testID
|
||||
testID=test_id
|
||||
print('分配成功')
|
||||
db.commit()
|
||||
cursor.close()
|
||||
return '发布成功'
|
||||
|
||||
|
||||
def TeacherMark(CLASS,ID):
|
||||
global testID
|
||||
global TrainName
|
||||
cursor = db.cursor()
|
||||
strClass=str(CLASS)
|
||||
if TrainName.find(r'["\u524d\u7aef\u4e0e\u6570\u636e\u5e93\u7ed3\u5408"]') != -1:
|
||||
for i in eval(strClass):
|
||||
cursor.execute(f'INSERT INTO TEACHERMARK VALUES(?,?,?,?) ', (ID,testID,'false', i))
|
||||
db.commit()
|
||||
cursor.close()
|
||||
|
||||
# def Find_dic(dic):
|
||||
# # 获取字典的键,并按照从大到小的顺序排序
|
||||
# keys_sorted = sorted(dic.keys(), reverse=True)
|
||||
|
@ -291,3 +312,17 @@ def Find_details_Func(ID):
|
|||
return dic
|
||||
|
||||
# print(Find_details('20240101'))
|
||||
|
||||
|
||||
#获取未批改的卷子
|
||||
def MarkTrainFunc(ClassID,teacher_ID):
|
||||
dic={}
|
||||
cursor = db.cursor()
|
||||
cursor.execute(f"SELECT * FROM TEACHERMARK WHERE CLASS=? AND TEACHERID=? AND TF='false'", (ClassID,teacher_ID))
|
||||
for i in cursor.fetchall():
|
||||
cursor.execute(f"SELECT * FROM TRAINTEST WHERE ID=? ", (i[1],))
|
||||
dic[i[1]]=cursor.fetchall()
|
||||
# print(dic)
|
||||
return dic
|
||||
|
||||
# MarkTrainFunc('202401','111111')
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
TrainName='["\u524d\u7aef\u4e0e\u6570\u636e\u5e93\u7ed3\u5408"]'
|
||||
print(TrainName)
|
||||
print(type(TrainName))
|
||||
if TrainName == '["\u524d\u7aef\u4e0e\u6570\u636e\u5e93\u7ed3\u5408"]':
|
||||
print('yes')
|
Loading…
Reference in New Issue