From 572de33a14dbd7ee30f18fc59e4d3a58866d9f37 Mon Sep 17 00:00:00 2001 From: VIHANGAGIT Date: Wed, 4 Dec 2024 03:10:14 +0530 Subject: [PATCH 1/4] session check time --- src/components/Exam/TimeContainer.tsx | 22 ++++++++- src/pages/candidate/CandidateDashboard.tsx | 52 +++++++++++++++++++--- src/pages/candidate/ExamSummary.tsx | 4 +- 3 files changed, 69 insertions(+), 9 deletions(-) diff --git a/src/components/Exam/TimeContainer.tsx b/src/components/Exam/TimeContainer.tsx index a016913..8fa6b41 100644 --- a/src/components/Exam/TimeContainer.tsx +++ b/src/components/Exam/TimeContainer.tsx @@ -22,6 +22,16 @@ export const TimeContainer = ({ }: TimeContainerProps) => { const navigate = useNavigate(); + // Calculate remaining time + const endTime = sessionStorage.getItem('endTime'); + console.log('endTime:', endTime); + const remainingTimeInMilliseconds = + endTime ? new Date(endTime).getTime() - new Date().getTime() : 0; + + const remainingTimeInSeconds = Math.max(0, Math.floor(remainingTimeInMilliseconds / 1000)); + const remainingMinutes = Math.floor(remainingTimeInSeconds / 60); + const remainingSeconds = remainingTimeInSeconds % 60; + const handleSubmitExam = async () => { const token = sessionStorage.getItem('accessToken'); const sessionId = sessionStorage.getItem('sessionId'); @@ -30,7 +40,6 @@ export const TimeContainer = ({ message.error('Failed to submit exam. Please try again.'); return; } - try { const response = await axios.put( @@ -61,7 +70,15 @@ export const TimeContainer = ({
Time Remaining - + {remainingTimeInSeconds > 0 ? ( + + ) : ( + Time's up! + )}
Submit Exam diff --git a/src/pages/candidate/CandidateDashboard.tsx b/src/pages/candidate/CandidateDashboard.tsx index b1d8f23..c930c86 100644 --- a/src/pages/candidate/CandidateDashboard.tsx +++ b/src/pages/candidate/CandidateDashboard.tsx @@ -16,6 +16,7 @@ import { } from 'antd'; import { getLoggedInUser } from '../../utils/authUtils'; import { useNavigate } from 'react-router-dom'; +import axios from 'axios'; export const CandidateDashboard = () => { const [isModalVisible, setIsModalVisible] = useState(false); @@ -25,18 +26,57 @@ export const CandidateDashboard = () => { // Example API call simulation useEffect(() => { - // Simulate checking for an ongoing exam const checkOngoingExam = async () => { - const ongoingExam = true; // Replace this with actual API call - if (ongoingExam) { - setIsModalVisible(false); + const userString = sessionStorage.getItem('user'); // Retrieve the user string + const token = sessionStorage.getItem('accessToken'); // Retrieve the access token + + if (!userString || !token) { + console.error("User information or token not found in session storage."); + return; + } + + const user = JSON.parse(userString); // Parse the user object + const candidateId = user?.id; // Extract the candidateId + + if (!candidateId) { + console.error("Candidate ID not found in user object."); + return; + } + + try { + const response = await axios.post( + 'http://localhost:8080/api/v1/candidate/check-active-session', + { candidateId }, + { + headers: { + Authorization: `Bearer ${token}`, + }, + } + ); + + const data = response.data; + if (data.examId && data.examName && data.examType) { + // If an active exam session exists, show the modal + setIsModalVisible(true); + // Store the exam data for navigation + sessionStorage.setItem('ongoingExam', JSON.stringify(data)); + sessionStorage.setItem('sessionId', data.sessionId); + sessionStorage.setItem('endTime', data.endTime); + } + } catch (error) { + console.error('Error checking active session:', error); } }; + checkOngoingExam(); }, []); - + const handleOk = () => { - navigate('/exam/view'); // Navigate to the exam view page + const ongoingExam = sessionStorage.getItem('ongoingExam'); + if (ongoingExam) { + const { examId, examName, examType } = JSON.parse(ongoingExam); + navigate('/candidate/exam/view', { state: { id: examId, name: examName, type: examType } }); + } }; const handleCancel = () => { diff --git a/src/pages/candidate/ExamSummary.tsx b/src/pages/candidate/ExamSummary.tsx index 544e246..818c5c4 100644 --- a/src/pages/candidate/ExamSummary.tsx +++ b/src/pages/candidate/ExamSummary.tsx @@ -101,8 +101,10 @@ export const ExamSummaryPage = () => { setSessionExists(true); // Store the sessionId in sessionStorage - const sessionId = response.data.sessionId; // Assuming the response structure contains the sessionId + const sessionId = response.data.sessionId; + console.log('sessionId:', response.data.endTime); sessionStorage.setItem('sessionId', sessionId); + sessionStorage.setItem('endTime', response.data.endTime); sessionStorage.setItem('examType', examData?.examType || ''); sessionStorage.setItem('examId', id); From 36f3195a46257bf899bc54c818304d97e5415b3b Mon Sep 17 00:00:00 2001 From: VIHANGAGIT Date: Wed, 4 Dec 2024 04:35:22 +0530 Subject: [PATCH 2/4] MCQ grading submit and name fetch --- src/pages/candidate/ExamMcqResults.tsx | 198 +++++++++++++++++-------- src/pages/candidate/ExamSummary.tsx | 1 + 2 files changed, 138 insertions(+), 61 deletions(-) diff --git a/src/pages/candidate/ExamMcqResults.tsx b/src/pages/candidate/ExamMcqResults.tsx index a22438e..e447f1d 100644 --- a/src/pages/candidate/ExamMcqResults.tsx +++ b/src/pages/candidate/ExamMcqResults.tsx @@ -27,13 +27,7 @@ export const ExamMcqResults = () => { correct: boolean; marks: number; } - - interface Question { - questionId: number; - questionText: string; - options: Option[]; - } - + interface UserAnswer { questionId: number; optionId: number; @@ -46,10 +40,27 @@ export const ExamMcqResults = () => { const [incorrectAnswers, setIncorrectAnswers] = useState(0); const [skippedQuestions, setSkippedQuestions] = useState(0); const [loading, setLoading] = useState(true); + const [gradingScheme, setGradingScheme] = useState([]); + const [candidateGrade, setCandidateGrade] = useState(''); + const [status, setStatus] = useState('PASS'); const token = sessionStorage.getItem('accessToken'); const sessionId = sessionStorage.getItem('sessionId'); const examId = sessionStorage.getItem('examId'); + const userString = sessionStorage.getItem('user'); + const name = sessionStorage.getItem('examName'); + if (!userString || !token) { + console.error("User information or token not found in session storage."); + return; + } + + const user = JSON.parse(userString); // Parse the user object + const candidateId = user?.id; // Extract the candidateId + + if (!candidateId) { + console.error("Candidate ID not found in user object."); + return; + } const handleDashboard = () => { navigate('/candidate/'); @@ -88,6 +99,19 @@ export const ExamMcqResults = () => { ); const examData: { questions: Question[] } = await examResponse.json(); + // Fetch Grading Scheme (Only Once) + const gradingResponse = await fetch( + `http://localhost:8080/api/v1/grade/1/grading-scheme`, + { + method: 'GET', + headers: { + Authorization: `Bearer ${token}`, + }, + } + ); + const gradingData = await gradingResponse.json(); + setGradingScheme(gradingData); + if (!Array.isArray(mcqData) || !Array.isArray(examData.questions)) { console.error("Invalid data structure"); return; @@ -100,47 +124,59 @@ export const ExamMcqResults = () => { // Calculate Statistics let correctAnswersCount = 0; let incorrectAnswersCount = 0; - let correctAnswersMarks = 0; - let skippedQuestions = 0; - let totalMarks = 0; + let totalMarks = 0; + let correctAnswersMarks = 0; + let skippedQuestions = 0; + + // Assuming each correct answer is worth 10 marks + const marksPerAnswer = 10; examData.questions.forEach((question) => { - // Calculate total marks for all correct options - totalMarks += question.options - .filter((opt) => opt.correct) - .reduce((sum, opt) => sum + opt.marks, 0); + // Calculate total marks for the exam + totalMarks += question.options.filter((opt) => opt.correct).length * marksPerAnswer; - // Find user's answer for the current question const userAnswer = mcqData.find( (answer) => Number(answer.questionId) === question.questionId ); if (userAnswer) { - // Find the selected option and the correct option const selectedOption = question.options.find( (opt) => opt.optionId === Number(userAnswer.optionId) ); const correctOption = question.options.find((opt) => opt.correct); if (selectedOption && correctOption) { - if (selectedOption.optionId == correctOption.optionId) { + if (selectedOption.optionId === correctOption.optionId) { correctAnswersCount += 1; - console.log(selectedOption.optionId); - correctAnswersMarks += selectedOption.marks || 0; + correctAnswersMarks += marksPerAnswer; } else { incorrectAnswersCount += 1; - console.log(selectedOption.optionId); } } } else { skippedQuestions += 1; } }); - + setTotalMarks(totalMarks); setCorrectAnswers(correctAnswersCount); setIncorrectAnswers(incorrectAnswersCount); setSkippedQuestions(skippedQuestions); + + // Determine Grade + const scorePercentage = (correctAnswersMarks / totalMarks) * 100; + let grade = 'F'; + + gradingScheme.forEach((gradeScheme) => { + if (scorePercentage >= gradeScheme.minMarks && scorePercentage <= gradeScheme.maxMarks) { + grade = gradeScheme.grade; + } + }); + + setCandidateGrade(grade); + setStatus(grade === 'F' ? 'FAIL' : 'PASS'); + + } catch (error) { console.error("Error fetching data", error); } finally { @@ -149,12 +185,40 @@ export const ExamMcqResults = () => { }; fetchExamResults(); - }, [token, sessionId, examId]); - - + }, [examId, sessionId, token]); + + const percentage = totalMarks > 0 ? Math.round((correctAnswers * 10 / totalMarks) * 100) : 0; + const examName = name; - const percentage = totalMarks > 0 ? Math.round((correctAnswers*10 / totalMarks) * 100) : 0; - const examName = 'Machine Learning - Quiz 2'; // Replace with actual exam name if needed + const handleGradeSubmission = async () => { + try { + const response = await fetch( + 'http://localhost:8080/api/v1/grade/setExamCandidateGrade', + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${token}`, + }, + body: JSON.stringify({ + examID: examId, + candidateID: candidateId, + grade: candidateGrade, + score: correctAnswers * 10, + status: status, + }), + } + ); + const result = await response.json(); + if (result.success) { + console.log('Grade submitted successfully'); + } else { + console.error('Error submitting grade'); + } + } catch (error) { + console.error('Error submitting grade:', error); + } + }; return (
@@ -224,7 +288,7 @@ export const ExamMcqResults = () => { )} /> - Scored {correctAnswers*10} out of {totalMarks} + Scored {correctAnswers * 10} out of {totalMarks} @@ -243,23 +307,41 @@ export const ExamMcqResults = () => { Skipped Questions: {skippedQuestions}
-
- - {!loading && - examDetails.map((question: Question, index: number) => { + + + +
+ + + +
+ + +
+ + {!loading && examDetails.map((question, index) => { const userAnswer = examResults.find( - (answer: UserAnswer) => Number(answer.questionId) === question.questionId + (answer) => Number(answer.questionId) === question.questionId ); const userSelectedOptionId = userAnswer ? userAnswer.optionId : null; @@ -279,36 +361,32 @@ export const ExamMcqResults = () => { Marks:{" "} {userSelectedOptionId - ? question.options.find((opt: Option) => opt.optionId === userSelectedOptionId)?.correct + ? question.options.find((opt) => opt.optionId === userSelectedOptionId)?.correct ? "10" : "0" : "0"} {" "} /{" "} {question.options.reduce( - (acc: number, opt: Option) => acc + (opt.correct ? 10 : 0), + (acc, opt) => acc + (opt.correct ? 10 : 0), 0 )}