From 8abf333e1312114d0cc758a445b63d37c9510779 Mon Sep 17 00:00:00 2001 From: 30404 <3040471803@qq.com> Date: Sat, 29 Jun 2024 17:07:59 +0800 Subject: [PATCH 1/9] 1 --- app-dm/src/pages/ListPages/Subject/Exam.css | 9 + app-dm/src/pages/ListPages/Subject/Exam.jsx | 184 ++++++++++++++++-- .../pages/ListPages/Training/Operation1.jsx | 16 +- .../pages/ListPages/Training/Operation2.jsx | 12 +- app-dm/src/router/index.jsx | 4 +- python/app.py | 15 +- python/student_func.py | 48 ++++- 7 files changed, 243 insertions(+), 45 deletions(-) diff --git a/app-dm/src/pages/ListPages/Subject/Exam.css b/app-dm/src/pages/ListPages/Subject/Exam.css index 247fb2f..5751808 100644 --- a/app-dm/src/pages/ListPages/Subject/Exam.css +++ b/app-dm/src/pages/ListPages/Subject/Exam.css @@ -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; } \ No newline at end of file diff --git a/app-dm/src/pages/ListPages/Subject/Exam.jsx b/app-dm/src/pages/ListPages/Subject/Exam.jsx index 17f52a8..b968908 100644 --- a/app-dm/src/pages/ListPages/Subject/Exam.jsx +++ b/app-dm/src/pages/ListPages/Subject/Exam.jsx @@ -10,10 +10,27 @@ import top from '../img/top.jpg' import axios from 'axios'; function Exam() { - - //倒计时 const [countdown, setCountdown] = useState(null); + const {examId}=useParams() + + //获取题目 + 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() + },[]) // 倒计时更新 useEffect(() => { @@ -29,13 +46,9 @@ function Exam() { return () => clearInterval(interval); }, [countdown]); - // useEffect(()=>{ - // if(countdown<0){ - // console.log('c'); - // setCountdown(60 * parseInt(TrainData['operateID'][4])) - // localStorage.removeItem('time') - // } - // },[TrainData]) + + + // 转换秒数为时分秒格式 const formatTime = (seconds) => { @@ -48,6 +61,88 @@ 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 [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 (
@@ -78,32 +173,85 @@ function Exam() { {/* 题目预览框 */} {/* 根据isFixed状态来设置countBox的类 */} - {/*
- {TrainData&& +
{/* 根据isFixed状态来设置countBox的类 */} + {TestData&&
选择题:
- {Object.keys(TrainData['operateID'][0]).map((key,index)=>( + {Object.keys(TestData['examID'][0]).map((key,index)=>(
))} 填空题:
- {Object.keys(TrainData['operateID'][1]).map((key,index)=>( + {Object.keys(TestData['examID'][1]).map((key,index)=>(
))} 判断题:
- {Object.keys(TrainData['operateID'][2]).map((key,index)=>( + {Object.keys(TestData['examID'][2]).map((key,index)=>(
))}
{index+1}
{index+1}
{index+1}
} -
*/} +
+ +
+
+ {TestData&& +

选择题

+ + {Object.keys(TestData['examID'][0]).map((key,index) => ( + + {index+1}.{TestData['examID'][key][0][0][1]} +
+ +
+ +
+ +
+ +
+ + ))} + +
} + + {TestData&&( + + +

填空题

+ {Object.keys(TestData['examID'][1]).map((key,index)=>( + + {index+1}.{TestData['examID'][1][key][0][1]} + {/*
*/} +
+ 请填入你的答案: + +
+ + ))} + +
+ )} + +
- {/*
- -
*/} ) diff --git a/app-dm/src/pages/ListPages/Training/Operation1.jsx b/app-dm/src/pages/ListPages/Training/Operation1.jsx index c6b8092..7f1e258 100644 --- a/app-dm/src/pages/ListPages/Training/Operation1.jsx +++ b/app-dm/src/pages/ListPages/Training/Operation1.jsx @@ -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(); diff --git a/app-dm/src/pages/ListPages/Training/Operation2.jsx b/app-dm/src/pages/ListPages/Training/Operation2.jsx index 95e3699..bee25e5 100644 --- a/app-dm/src/pages/ListPages/Training/Operation2.jsx +++ b/app-dm/src/pages/ListPages/Training/Operation2.jsx @@ -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() { {/* 前端连接数据库⬇ */} - { !isTrue&&
+ { !isTrue&&TrainData&&
{TrainData['operateID'][3][0]}
点击以上链接前往实训 @@ -192,7 +200,7 @@ function Operation2() { {/* 前端连接数据库 ⬆*/} {/* 达梦数据库连接 ⬇*/} - { isTrue&& + { isTrue&&TrainData&&

{TrainData['operateID'][3][0]}

diff --git a/app-dm/src/router/index.jsx b/app-dm/src/router/index.jsx index a2c4596..b5e2aa3 100755 --- a/app-dm/src/router/index.jsx +++ b/app-dm/src/router/index.jsx @@ -23,8 +23,8 @@ 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 SendTrain from '../TeacherPages/Trainmanage/SendTrain' +import TrainManage from '../TeacherPages/Trainmanage/Trainmanage' import StudentLink from '../TeacherPages/MarkingPages/StudentLink'; diff --git a/python/app.py b/python/app.py index f8825ef..26960f9 100644 --- a/python/app.py +++ b/python/app.py @@ -103,10 +103,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 +116,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']) diff --git a/python/student_func.py b/python/student_func.py index e1f72d0..e8f6b74 100644 --- a/python/student_func.py +++ b/python/student_func.py @@ -120,7 +120,6 @@ def student_succeed_func(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 @@ -166,6 +165,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() From 5c5803e819435e9f904a3cef47583ac58cb44d61 Mon Sep 17 00:00:00 2001 From: 30404 <3040471803@qq.com> Date: Sat, 29 Jun 2024 22:05:53 +0800 Subject: [PATCH 2/9] 1 --- app-dm/src/pages/Home/Home.jsx | 3 +- app-dm/src/pages/ListPages/Subject/Exam.jsx | 117 +++++++++++++++--- .../pages/ListPages/Subject/SubjectPage.jsx | 23 ++-- .../pages/ListPages/Training/Operation1.jsx | 1 - python/student_func.py | 10 ++ 5 files changed, 119 insertions(+), 35 deletions(-) diff --git a/app-dm/src/pages/Home/Home.jsx b/app-dm/src/pages/Home/Home.jsx index bb83281..3368983 100644 --- a/app-dm/src/pages/Home/Home.jsx +++ b/app-dm/src/pages/Home/Home.jsx @@ -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([]); diff --git a/app-dm/src/pages/ListPages/Subject/Exam.jsx b/app-dm/src/pages/ListPages/Subject/Exam.jsx index b968908..59a35e1 100644 --- a/app-dm/src/pages/ListPages/Subject/Exam.jsx +++ b/app-dm/src/pages/ListPages/Subject/Exam.jsx @@ -13,6 +13,8 @@ function Exam() { //倒计时 const [countdown, setCountdown] = useState(null); const {examId}=useParams() + const student_ID=localStorage.getItem('islogin') + const [score,setscore]=useState(0) //获取题目 const [TestData,setTestData]=useState() @@ -32,6 +34,40 @@ function Exam() { TestFunc() },[]) + + //添加成绩 + const scoreEntry=async()=>{ + try{ + const scoreEntrySrc=await axios.post('/api/student/score_entry',{ + student_ID, + examId, + score + }) + }catch{ + alert('scoreEntry出错') + } + } + + useEffect(()=>{ + 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'); + },[]) + + // 倒计时更新 useEffect(() => { let interval = setInterval(() => { @@ -41,15 +77,22 @@ function Exam() { } else { clearInterval(interval); alert('考试时间到!'); + correct() + + window.location.href='http://localhost:3000/subject' } }, 1000); return () => clearInterval(interval); }, [countdown]); - + useEffect(()=>{ + if(countdown<0){ + setCountdown(60 * parseInt(TestData['examID'][3])) + localStorage.removeItem('time') + } + },[TestData]) - // 转换秒数为时分秒格式 const formatTime = (seconds) => { const hours = Math.floor(seconds / 3600); @@ -105,20 +148,40 @@ function Exam() { } } + //批改 + 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++ + } + } - - - - - - - - - - - - - + + const submit=()=>{ + correct() + setCountdown(0) + localStorage.removeItem('time') + alert('提交成功') + window.location.href='http://localhost:3000/subject' + + } // 题目预览框 @@ -197,13 +260,13 @@ function Exam() {
-
+
{TestData&&

选择题

{Object.keys(TestData['examID'][0]).map((key,index) => ( - {index+1}.{TestData['examID'][key][0][0][1]} + {index+1}.{TestData['examID'][0][key][0][1]}
)} - + {TestData&&( + + +

判断题

+ {Object.keys(TestData['examID'][2]).map((key,index)=>( + + {index+1}.{TestData['examID'][2][key][0][1]} + + + + + + ))} + +
+ )} +
diff --git a/app-dm/src/pages/ListPages/Subject/SubjectPage.jsx b/app-dm/src/pages/ListPages/Subject/SubjectPage.jsx index 89a99ef..6544e5f 100644 --- a/app-dm/src/pages/ListPages/Subject/SubjectPage.jsx +++ b/app-dm/src/pages/ListPages/Subject/SubjectPage.jsx @@ -57,19 +57,18 @@ function SubjectPage(){ ))} }

已结束或完成的试卷与练习

- { testdata&& result&&
- {Object.keys(testdata).map((key)=>{ - return( -
-

试卷ID:{testdata[key][0][3]}

- 发布者:{'小陈老师'} -
发布时间:{testdata[key][1][0][5]}
-
截至时间:{testdata[key][1][0][6]}
-
{testdata[key][0][5]}
+
+ { Object.keys(testdata).slice(-3).reverse().map((key) => ( +
+

试卷ID:{testdata[key][0][3]}

+ 发布者:{'小陈老师'} +
发布时间:{testdata[key][1][0][5]}
+
截至时间:{testdata[key][1][0][6]}
+
{testdata[key][0][5]}
- ) - })} -
} + ))} +
+ { NotFalse&& result&&
{Object.keys(NotFalse).map((key)=>{ return( diff --git a/app-dm/src/pages/ListPages/Training/Operation1.jsx b/app-dm/src/pages/ListPages/Training/Operation1.jsx index d9ad6ac..d88685b 100644 --- a/app-dm/src/pages/ListPages/Training/Operation1.jsx +++ b/app-dm/src/pages/ListPages/Training/Operation1.jsx @@ -75,7 +75,6 @@ function Operation1() { useEffect(()=>{ if(countdown<0){ - console.log('c'); setCountdown(60 * parseInt(TrainData['operateID'][4])) localStorage.removeItem('time') } diff --git a/python/student_func.py b/python/student_func.py index e8f6b74..beab01e 100644 --- a/python/student_func.py +++ b/python/student_func.py @@ -127,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') From dea760fcb9135607580c71d181ca7a7ea0a1450c Mon Sep 17 00:00:00 2001 From: 30404 <3040471803@qq.com> Date: Sat, 29 Jun 2024 22:19:01 +0800 Subject: [PATCH 3/9] ok --- app-dm/src/pages/ListPages/Subject/Exam.jsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/app-dm/src/pages/ListPages/Subject/Exam.jsx b/app-dm/src/pages/ListPages/Subject/Exam.jsx index 59a35e1..8058205 100644 --- a/app-dm/src/pages/ListPages/Subject/Exam.jsx +++ b/app-dm/src/pages/ListPages/Subject/Exam.jsx @@ -15,6 +15,7 @@ function Exam() { const {examId}=useParams() const student_ID=localStorage.getItem('islogin') const [score,setscore]=useState(0) + const [isEntry,setisEntry]=useState(false) //获取题目 const [TestData,setTestData]=useState() @@ -49,7 +50,9 @@ function Exam() { } useEffect(()=>{ - scoreEntry() + if(isEntry){ + scoreEntry() + } },[score]) function daojishi(){ @@ -65,7 +68,7 @@ function Exam() { useEffect(()=>{ daojishi() console.log('a'); - },[]) + },[TestData]) // 倒计时更新 @@ -78,7 +81,6 @@ function Exam() { clearInterval(interval); alert('考试时间到!'); correct() - window.location.href='http://localhost:3000/subject' } }, 1000); @@ -87,8 +89,9 @@ function Exam() { useEffect(()=>{ if(countdown<0){ - setCountdown(60 * parseInt(TestData['examID'][3])) localStorage.removeItem('time') + setCountdown(60 * parseInt(TestData['examID'][3])) + } },[TestData]) @@ -171,6 +174,7 @@ function Exam() { } JUD_sum++ } + setisEntry(true) } From fba2428e30b79712eee04e0cb5aefa2ce38fa405 Mon Sep 17 00:00:00 2001 From: wang <842355358@qq.com> Date: Mon, 1 Jul 2024 10:42:54 +0800 Subject: [PATCH 4/9] =?UTF-8?q?=E4=BF=AE=E6=94=B9webssh?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- python/assets/type0-service.yaml | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 python/assets/type0-service.yaml diff --git a/python/assets/type0-service.yaml b/python/assets/type0-service.yaml new file mode 100644 index 0000000..e69de29 From cb10f62c35d15593257bfdb326d8f1ce61f01455 Mon Sep 17 00:00:00 2001 From: wang <842355358@qq.com> Date: Mon, 1 Jul 2024 10:43:22 +0800 Subject: [PATCH 5/9] =?UTF-8?q?=E4=BF=AE=E6=94=B9webssh?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app-dm/src/pages/ListPages/Training/TrainingPage.jsx | 0 app-dm/src/router/index.jsx | 0 2 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 app-dm/src/pages/ListPages/Training/TrainingPage.jsx mode change 100755 => 100644 app-dm/src/router/index.jsx diff --git a/app-dm/src/pages/ListPages/Training/TrainingPage.jsx b/app-dm/src/pages/ListPages/Training/TrainingPage.jsx old mode 100755 new mode 100644 diff --git a/app-dm/src/router/index.jsx b/app-dm/src/router/index.jsx old mode 100755 new mode 100644 From fadb4a4ee3fd3a39a93192028c321f92d7c769e2 Mon Sep 17 00:00:00 2001 From: wang <842355358@qq.com> Date: Mon, 1 Jul 2024 10:43:22 +0800 Subject: [PATCH 6/9] =?UTF-8?q?=E4=BF=AE=E6=94=B9webssh?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 4 +- app-dm/package-lock.json | 82 ++++++++++++++++++- app-dm/package.json | 1 + app-dm/src/TeacherPages/TestManage/Test.tsx | 89 +++++++++++++++++++++ app-dm/src/router/index.jsx | 6 +- app-dm/src/setupProxy.js | 2 +- build.sh | 14 ++-- docker-compose.yml | 7 +- docker/base-dm/Dockerfile | 4 +- docker/base/Dockerfile | 2 + docker/ssh-server/Dockerfile | 27 +------ python/.devenv | 27 +++++++ python/.gitignore | 3 + python/Dockerfile | 10 ++- python/app.py | 83 ++++++++++++++++++- python/assets/type0-pod.yaml | 19 +++++ python/k8s_func.py | 85 ++++++++++++++++++++ python/requirements.txt | 6 +- python/student_func.py | 2 +- 19 files changed, 419 insertions(+), 54 deletions(-) create mode 100644 app-dm/src/TeacherPages/TestManage/Test.tsx create mode 100644 python/.devenv create mode 100644 python/.gitignore create mode 100644 python/assets/type0-pod.yaml create mode 100644 python/k8s_func.py diff --git a/.gitignore b/.gitignore index cdd94a1..7dc4f0e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,2 @@ .idea -node_modules -__pycache__ -public \ No newline at end of file +node_modules \ No newline at end of file diff --git a/app-dm/package-lock.json b/app-dm/package-lock.json index a462fec..0261786 100644 --- a/app-dm/package-lock.json +++ b/app-dm/package-lock.json @@ -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", diff --git a/app-dm/package.json b/app-dm/package.json index 89c2d69..4053782 100644 --- a/app-dm/package.json +++ b/app-dm/package.json @@ -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", diff --git a/app-dm/src/TeacherPages/TestManage/Test.tsx b/app-dm/src/TeacherPages/TestManage/Test.tsx new file mode 100644 index 0000000..cb17197 --- /dev/null +++ b/app-dm/src/TeacherPages/TestManage/Test.tsx @@ -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(null); + const [terminal, setTerminal] = useState(null); + const [socket, setSocket] = useState(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 ( +
+
+
+
+ ); +} diff --git a/app-dm/src/router/index.jsx b/app-dm/src/router/index.jsx index b5e2aa3..b94d2d8 100644 --- a/app-dm/src/router/index.jsx +++ b/app-dm/src/router/index.jsx @@ -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/SendTrain/SendTrain'; +import TrainManage from '../TeacherPages/SendTest/trainmanage'; +import Test from "../TeacherPages/teachermanagetest/Test.tsx" // 定义一个组件来包裹除了特定页面(exam)外的所有页面使其有导航 const MainLayout = ({ headerNav:HeaderNav }) => { return ( @@ -69,6 +70,7 @@ function App() { }> } /> + }/> {/* 页面使用单独的布局,不包含HeaderNav和HeaderNavTeacher */} } />{/* 练习-考试页面 */} diff --git a/app-dm/src/setupProxy.js b/app-dm/src/setupProxy.js index 4a40b4f..d6d9212 100644 --- a/app-dm/src/setupProxy.js +++ b/app-dm/src/setupProxy.js @@ -4,7 +4,7 @@ module.exports = function(app) { app.use( '/api', // 如果请求路径匹配'/api',则进行代理 createProxyMiddleware({ - target: 'http://127.0.0.1:5000', // 目标后端服务地址 + target: 'http://36.138.114.105:32087', // 目标后端服务地址 changeOrigin: true, }) ); diff --git a/build.sh b/build.sh index 029e22f..38c1c27 100644 --- a/build.sh +++ b/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 "脚本结束" } diff --git a/docker-compose.yml b/docker-compose.yml index 7b095cd..4d5bc3e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -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 diff --git a/docker/base-dm/Dockerfile b/docker/base-dm/Dockerfile index 1d30f8f..24689fc 100644 --- a/docker/base-dm/Dockerfile +++ b/docker/base-dm/Dockerfile @@ -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 \ diff --git a/docker/base/Dockerfile b/docker/base/Dockerfile index f73ec32..c03eea3 100644 --- a/docker/base/Dockerfile +++ b/docker/base/Dockerfile @@ -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 \ diff --git a/docker/ssh-server/Dockerfile b/docker/ssh-server/Dockerfile index 048c94f..a005bc5 100644 --- a/docker/ssh-server/Dockerfile +++ b/docker/ssh-server/Dockerfile @@ -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 \ No newline at end of file diff --git a/python/.devenv b/python/.devenv new file mode 100644 index 0000000..4139b95 --- /dev/null +++ b/python/.devenv @@ -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 \ No newline at end of file diff --git a/python/.gitignore b/python/.gitignore new file mode 100644 index 0000000..ebed232 --- /dev/null +++ b/python/.gitignore @@ -0,0 +1,3 @@ +.idea +__pycache__ +public \ No newline at end of file diff --git a/python/Dockerfile b/python/Dockerfile index 4d0eac3..db826f5 100644 --- a/python/Dockerfile +++ b/python/Dockerfile @@ -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"] \ No newline at end of file diff --git a/python/app.py b/python/app.py index 26960f9..bd443e9 100644 --- a/python/app.py +++ b/python/app.py @@ -1,12 +1,29 @@ +import subprocess +import threading +import time + +import paramiko + from teacher_func import * from student_func import * +from k8s_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(): # 判断是否已经登录 @@ -249,6 +266,17 @@ def Find_details(): result=Find_details_Func(ID) print(result) return jsonify({'TestScore':result}) +@app.route("/api/teacher/list_pods") +def teacher_list_pods(): + return list_pods() +@app.route("/api/teacher/list_services") +def teacher_list_services(): + return list_services() + +@app.route("/api/teacher/create_pod") +def teacher_create_pod(): + create_pod(0, "test") + @app.route('/') @@ -257,7 +285,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) + diff --git a/python/assets/type0-pod.yaml b/python/assets/type0-pod.yaml new file mode 100644 index 0000000..667ef74 --- /dev/null +++ b/python/assets/type0-pod.yaml @@ -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 \ No newline at end of file diff --git a/python/k8s_func.py b/python/k8s_func.py new file mode 100644 index 0000000..311c040 --- /dev/null +++ b/python/k8s_func.py @@ -0,0 +1,85 @@ +import json + +import yaml +from kubernetes import client, config +from flask import jsonify +# Load in-cluster config +config.load_incluster_config() +v1 = client.CoreV1Api() +namespace = open("/var/run/secrets/kubernetes.io/serviceaccount/namespace").read() +IP = "36.138.114.105" +def list_pods(): + ret = v1.list_namespaced_pod(namespace, watch=False) + return { + "list": [{ + "name": item.metadata.name.replace("database-dmserver-0", "student-2210502337-0-12"), + "ip": item.status.pod_ip + } for item in ret.items] + } + +def list_services(): + ret = v1.list_namespaced_service(namespace) + return { + "list": [ + # "name": item.metadata.name.replace("database-dmserver-0", "student-2210502337-0-12"), + # "ip": item.spec.cluster_ip, + # "ports": [[port.to_dict() for port in item.spec.ports]], + # + item.to_dict() + # "node_port": item.spec.ports[0].node_port, + for item in ret.items] + } + +def create_pod(type, name): + # 读取 pod yaml 文件 + with open(f"assets/type{type}-pod.yaml") as f: + pod_manifest = yaml.safe_load(f) + pod_manifest["metadata"]["name"] = name + pod_manifest["metadata"]["labels"]["app"] = name + # 创建 Pod + resp = v1.create_namespaced_pod( + body=pod_manifest, + namespace=namespace + ) + print(f"pod '{resp.metadata.name}' 创建成功") + # 创建 Service + service_manifest = { + "apiVersion": "v1", + "kind": "Service", + "metadata": { + "name": name+ "-service" + }, + "spec": { + "selector": { + "app": pod_manifest['metadata']['labels']['app'] + }, + "ports": [ + { + "protocol": "TCP", + "port": 22, + "targetPort": 22 + }, + { + "protocol": "TCP", + "port": 5236, + "targetPort": 5236 + } + ] + } + } + + resp = v1.create_namespaced_service( + body=service_manifest, + namespace=namespace + ) + print(f"服务 '{resp.metadata.name}' 创建成功") + # 创建成功,再次查询,拿到端口 + service = v1.read_namespaced_service(name=name+ "-service", namespace=namespace) + print(service) + print("访问地址:" + IP + ":" + service.spec.ports[0].node_port) + + +def delete_pod(name): + pass + + diff --git a/python/requirements.txt b/python/requirements.txt index 1eddad4..a785c55 100644 --- a/python/requirements.txt +++ b/python/requirements.txt @@ -1,4 +1,8 @@ Flask==2.3.0 Flask_Cors==4.0.0 pycryptodome==3.19.0 -gunicorn \ No newline at end of file +gunicorn +kubernetes +websockets +paramiko +flask-socketio \ No newline at end of file diff --git a/python/student_func.py b/python/student_func.py index beab01e..306b76d 100644 --- a/python/student_func.py +++ b/python/student_func.py @@ -117,7 +117,7 @@ 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] cursor.close() From 2420365c72baa20266fda063e2fb77eac349b82f Mon Sep 17 00:00:00 2001 From: 30404 <3040471803@qq.com> Date: Mon, 1 Jul 2024 20:08:58 +0800 Subject: [PATCH 7/9] 1 --- .../src/TeacherPages/MarkingPages/Marking.jsx | 93 ++++++++++++++----- .../src/TeacherPages/TestManage/SendTest.jsx | 9 +- .../TeacherPages/Trainmanage/SendTrain.jsx | 16 ++-- .../pages/ListPages/Training/TrainingPage.jsx | 5 +- python/app.py | 8 +- python/student_func.py | 14 ++- python/teacher_func.py | 37 +++++++- python/test.py | 5 + 8 files changed, 141 insertions(+), 46 deletions(-) create mode 100644 python/test.py diff --git a/app-dm/src/TeacherPages/MarkingPages/Marking.jsx b/app-dm/src/TeacherPages/MarkingPages/Marking.jsx index a62ca65..1c42827 100644 --- a/app-dm/src/TeacherPages/MarkingPages/Marking.jsx +++ b/app-dm/src/TeacherPages/MarkingPages/Marking.jsx @@ -3,6 +3,8 @@ import { Link } from 'react-router-dom'; // import axios from 'axios'; import './Marking.css'; import Test from '../../pages/Home/img/test.jpg' +import React, { useEffect, useState } from 'react'; +import axios from 'axios'; // 批改 function Marking(){ @@ -11,34 +13,81 @@ function Marking(){ alert('登录过期,请重新登录') window.location.href='http://36.138.114.105:30294/' } + + const [istrue,setistrue]=useState(false) + + //获取班级 + const [ClassID,setClassID]=useState() + const [selectClass,SetClassData]=useState() + const SeleactClass= async()=>{ + try { + const select_class_src = await axios.post('/api/teacher/select_class', { + teacher_ID + }); + SetClassData(select_class_src.data['Class']); + setClassID(Object.keys(select_class_src.data['Class'])[0]) + setistrue(true) + } catch (error) { + console.log(error); + } + + } + useEffect(()=>{ + SeleactClass() + },[]) + + //获取默认的未批改的实训 + const [MarkData,setMarkData]=useState() + const MarkTrainFunc=async()=>{ + try{ + const MarkTrainSrc=await axios.post('/api/teacher/marktrain',{ + ClassID, + teacher_ID + }) + setMarkData(MarkTrainSrc.data['MarkClass']) + }catch{ + alert('MarkTrain出错') + } + } + useEffect(()=>{ + if(istrue){ + MarkTrainFunc() + } + },[ClassID]) + + const ClassChange=(event)=>{ + setClassID(event.target.value) + } + const click=()=>{ + console.log(ClassID); + console.log(MarkData); + } + return(
- -
+ {selectClass&&} + {MarkData&&
{/* 内容 */}
    - {[{class:'课程1:达梦数据库的安装',time:"关闭时间:2024-4-16 11.46",ImgSrc:Test}, - {class:'课程2:web前端开发与数据库的结合',time:"关闭时间:2024-4-16 11.46",ImgSrc:Test}, - {class:'课程2:web前端开发与数据库的结合',time:"关闭时间:2024-4-16 11.46",ImgSrc:Test}, - ].map((item) => - -
  • - -

    -

    {item.class}

    - {item.time} -

    -
  • - - )} + {Object.keys(MarkData).map((key)=>( + +
  • + +

    +

    {eval(MarkData[key][0][11])}

    + {} +

    +
  • + + ))}
- -
+ +
}
) diff --git a/app-dm/src/TeacherPages/TestManage/SendTest.jsx b/app-dm/src/TeacherPages/TestManage/SendTest.jsx index 4ae2686..94df4f2 100644 --- a/app-dm/src/TeacherPages/TestManage/SendTest.jsx +++ b/app-dm/src/TeacherPages/TestManage/SendTest.jsx @@ -123,14 +123,14 @@ function SendTest(){
  • 第一章
  • -
  • 第二章
  • +
  • 第二章

题目选择

- { + {question&&
选择题 {Object.keys(question['choice']).map((key)=>( @@ -207,11 +207,6 @@ function SendTest(){
} - - - - -
{/* 右边的内容 */} diff --git a/app-dm/src/TeacherPages/Trainmanage/SendTrain.jsx b/app-dm/src/TeacherPages/Trainmanage/SendTrain.jsx index 4094c03..8883bc1 100644 --- a/app-dm/src/TeacherPages/Trainmanage/SendTrain.jsx +++ b/app-dm/src/TeacherPages/Trainmanage/SendTrain.jsx @@ -133,11 +133,7 @@ function SendTrain(){ } //获取截止时间 const [StopTime,SetStopTime]=useState('') - const getstoptime=(event)=>{ - SetStopTime(event.target.value) - } - // 日历 // 用于存储开始日期和结束日期的状态 const [startDate, setStartDate] = useState(new Date()); const [endDate, setEndDate] = useState(new Date()); @@ -155,10 +151,12 @@ function SendTrain(){ // 计算并显示总天数 const calculateTotalDays = () => { const differenceInTime = endDate - startDate; - const differenceInDays = Math.floor(differenceInTime / (1000 * 60 * 60 * 24)); + const differenceInDays = Math.floor(differenceInTime / (1000 * 60 * 60 * 24)+1); return differenceInDays; }; + // const [starTime,setstartTime]=useState() useEffect(() => { + SetStopTime(calculateTotalDays()) console.log(calculateTotalDays()); }, [startDate, endDate]); @@ -334,10 +332,10 @@ function SendTrain(){ ))} 小时 - {/* */} +