Grade home works
This commit is contained in:
@@ -1,32 +1,35 @@
|
||||
<script>
|
||||
import { onMount } from 'svelte';
|
||||
import Modal from '../../../../Modal/index.svelte';
|
||||
import Select from '../../../../Form/Select.svelte';
|
||||
import PrimaryButton from '../../../../PrimaryButton/index.svelte';
|
||||
import { VARIANTS } from '../../../../PrimaryButton/constants';
|
||||
import Preview from './Preview.svelte';
|
||||
import { SELECTABLE_STATUS } from './constants';
|
||||
import { snackbarStore } from '../../../../Snackbar/store';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
export let open;
|
||||
export let onClose;
|
||||
export let open = false;
|
||||
export let onClose = () => {};
|
||||
export let handleSave = () => {};
|
||||
|
||||
// export let submissionId;
|
||||
export let data = {};
|
||||
export let status = SELECTABLE_STATUS[0];
|
||||
export let updateStatus = () => {};
|
||||
|
||||
let status = SELECTABLE_STATUS[0];
|
||||
|
||||
let grades = {};
|
||||
let total = 0;
|
||||
let gradeTotal = 0;
|
||||
let maxPoints = 0;
|
||||
|
||||
function setGrades(data) {
|
||||
grades = (data.questions || []).reduce(
|
||||
(acc, question) => ({ ...acc, [question.name]: question.points }),
|
||||
{}
|
||||
function getMaxPoints(questions) {
|
||||
return (questions || []).reduce(
|
||||
(acc, question) => acc + question.points,
|
||||
0
|
||||
);
|
||||
|
||||
gradeTotal = calculateTotal(grades);
|
||||
}
|
||||
|
||||
function calculateTotal(grades) {
|
||||
if (!grades) return 0;
|
||||
return Object.values(grades).reduce(
|
||||
(acc, grade) => acc + parseInt(grade),
|
||||
0
|
||||
@@ -34,21 +37,48 @@
|
||||
}
|
||||
|
||||
function handleStatusChange() {
|
||||
console.log(`status`, status);
|
||||
updateStatus({
|
||||
submissionId: data.id,
|
||||
prevStatusId: data.status_id,
|
||||
nextStatusId: status.value,
|
||||
total,
|
||||
});
|
||||
|
||||
$snackbarStore.open = true;
|
||||
$snackbarStore.message = `Submission updated to '${status.label}'`;
|
||||
}
|
||||
|
||||
function setStatus(data) {
|
||||
status = SELECTABLE_STATUS.find(
|
||||
(status) => status.value === data.status_id
|
||||
);
|
||||
}
|
||||
|
||||
function getStatusColor(status) {
|
||||
// if (!status)
|
||||
return '';
|
||||
|
||||
// switch (status.value) {
|
||||
// case STATUS.SUBMITTED:
|
||||
// return '';
|
||||
// case STATUS.IN_PROGRESS:
|
||||
// return 'text-white bg-blue-700';
|
||||
// case STATUS.GRADED:
|
||||
// return 'text-white bg-green-700';
|
||||
// }
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
console.log('mounting');
|
||||
});
|
||||
|
||||
$: setGrades(data);
|
||||
$: total = calculateTotal(grades);
|
||||
$: total = calculateTotal(data.questionAnswerByPoint);
|
||||
$: maxPoints = getMaxPoints(data.questions);
|
||||
$: setStatus(data);
|
||||
</script>
|
||||
|
||||
<Modal
|
||||
modalHeading="Lesson 1: How to use props in React.js"
|
||||
modalHeading={data.title}
|
||||
bind:open
|
||||
{onClose}
|
||||
width="w-4/5"
|
||||
@@ -61,16 +91,27 @@
|
||||
answers: data.answers || {},
|
||||
isFinished: true,
|
||||
}}
|
||||
bind:grades
|
||||
bind:grades={data.questionAnswerByPoint}
|
||||
/>
|
||||
</div>
|
||||
<div class="ml-10 w-1/3">
|
||||
<Select
|
||||
bind:value={status}
|
||||
options={SELECTABLE_STATUS}
|
||||
color="primary"
|
||||
onChange={handleStatusChange}
|
||||
/>
|
||||
<div class="ml-4 w-2/5 sticky top-0">
|
||||
<div class="flex items-center justify-between">
|
||||
<Select
|
||||
bind:value={status}
|
||||
options={SELECTABLE_STATUS}
|
||||
selectClassName={getStatusColor(status)}
|
||||
onChange={handleStatusChange}
|
||||
/>
|
||||
|
||||
<PrimaryButton
|
||||
onClick={() => {
|
||||
handleSave(data);
|
||||
}}
|
||||
label="Save"
|
||||
variant={VARIANTS.CONTAINED_SUCCESS}
|
||||
className="py-2 px-8"
|
||||
/>
|
||||
</div>
|
||||
<div class="border border-gray-300 rounded-md mt-2">
|
||||
<div
|
||||
class="hover:bg-gray-100 border-b border-t-0 border-l-0 border-r-0 border-gray-300 p-3"
|
||||
@@ -79,24 +120,31 @@
|
||||
</div>
|
||||
|
||||
<div class="flex items-center text-sm p-3">
|
||||
<p class="font-bold w-1/2">Total grade</p>
|
||||
<p>{total} / {gradeTotal}</p>
|
||||
<p class="font-bold w-2/5">Total grade</p>
|
||||
<p>{total} / {maxPoints}</p>
|
||||
</div>
|
||||
<div class="flex items-center text-sm p-3">
|
||||
<!-- <div class="flex items-center text-sm p-3">
|
||||
<p class="font-bold w-1/2">Status</p>
|
||||
<div class="flex items-center">
|
||||
<p class="rounded-full w-5 h-5 bg-yellow-300 mr-2" />
|
||||
<p>Grading</p>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
<div class="flex items-center text-sm p-3">
|
||||
<p class="font-bold w-1/2">Student</p>
|
||||
<p>Jeremy Matt</p>
|
||||
<p class="font-bold w-2/5">Student</p>
|
||||
{#if data.student}
|
||||
<img
|
||||
alt="Student avatar"
|
||||
class="block rounded-full h-6 w-6"
|
||||
src={data.student.avatar_url}
|
||||
/>
|
||||
<p class="ml-2 text-sm">{data.student.fullname}</p>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="flex items-center text-sm p-3">
|
||||
<!-- <div class="flex items-center text-sm p-3">
|
||||
<p class="font-bold w-1/2">Teacher</p>
|
||||
<p>rotimi-best</p>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
export let onSubmit = () => {};
|
||||
export let onPrevious = () => {};
|
||||
export let handleGrade = () => {};
|
||||
export let disableGrading = false;
|
||||
</script>
|
||||
|
||||
{#each questions as currentQuestion, currentQuestionIndex}
|
||||
@@ -25,9 +26,10 @@
|
||||
onPrevious,
|
||||
true
|
||||
)}
|
||||
bind:grade={grades[currentQuestion.name]}
|
||||
bind:grade={grades[currentQuestion.id]}
|
||||
gradeMax={currentQuestion.points}
|
||||
handleGrade={handleGrade(currentQuestion.name)}
|
||||
handleGrade={handleGrade(currentQuestion.id)}
|
||||
{disableGrading}
|
||||
/>
|
||||
{:else if QUESTION_TYPE.CHECKBOX === currentQuestion.question_type.id}
|
||||
<CheckboxQuestion
|
||||
@@ -40,9 +42,10 @@
|
||||
onPrevious,
|
||||
true
|
||||
)}
|
||||
bind:grade={grades[currentQuestion.name]}
|
||||
bind:grade={grades[currentQuestion.id]}
|
||||
gradeMax={currentQuestion.points}
|
||||
handleGrade={handleGrade(currentQuestion.name)}
|
||||
handleGrade={handleGrade(currentQuestion.id)}
|
||||
{disableGrading}
|
||||
/>
|
||||
{:else if QUESTION_TYPE.TEXTAREA === currentQuestion.question_type.id}
|
||||
<TextareaQuestion
|
||||
@@ -55,9 +58,10 @@
|
||||
onPrevious,
|
||||
true
|
||||
)}
|
||||
bind:grade={grades[currentQuestion.name]}
|
||||
bind:grade={grades[currentQuestion.id]}
|
||||
gradeMax={currentQuestion.points}
|
||||
handleGrade={handleGrade(currentQuestion.name)}
|
||||
handleGrade={handleGrade(currentQuestion.id)}
|
||||
{disableGrading}
|
||||
/>
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
@@ -13,7 +13,9 @@
|
||||
import { QUESTION_TYPE } from '../../../../Question/constants';
|
||||
import { STATUS } from './constants';
|
||||
import { getPropsForQuestion, filterOutDeleted } from './functions';
|
||||
import { formatAnswers } from '../../../function';
|
||||
import { submitExercise } from '../../../../../utils/services/courses';
|
||||
import { fetchSubmission } from '../../../../../utils/services/submissions';
|
||||
import { profile } from '../../../../../utils/store/user';
|
||||
export let preview;
|
||||
|
||||
@@ -21,6 +23,15 @@
|
||||
|
||||
let currentQuestion = {};
|
||||
let renderProps = {};
|
||||
let submission;
|
||||
|
||||
function getGroupMemberId(people, profileId) {
|
||||
const groupMember = people.find(
|
||||
(person) => person.profile_id === profileId
|
||||
);
|
||||
|
||||
return groupMember ? groupMember.id : null;
|
||||
}
|
||||
|
||||
function handleStart() {
|
||||
$questionnaireMetaData.currentQuestionIndex += 1;
|
||||
@@ -55,13 +66,13 @@
|
||||
|
||||
// If last question send to server
|
||||
if (isFinished) {
|
||||
$questionnaireMetaData.status = 1;
|
||||
submitExercise(
|
||||
$questionnaireMetaData.answers,
|
||||
questions,
|
||||
exerciseId,
|
||||
$course.id,
|
||||
$group,
|
||||
$profile
|
||||
getGroupMemberId($group.people, $profile.id)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -70,15 +81,6 @@
|
||||
$questionnaireMetaData.currentQuestionIndex -= 1;
|
||||
}
|
||||
|
||||
function getTotalScores() {
|
||||
let total = 0;
|
||||
for (const score in $questionnaireMetaData.scores) {
|
||||
total += parseInt($questionnaireMetaData.scores[score]);
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
function getProgressValue(currentQuestionIndex) {
|
||||
if ($questionnaireMetaData.isFinished) {
|
||||
return 100;
|
||||
@@ -91,6 +93,55 @@
|
||||
);
|
||||
}
|
||||
|
||||
function getTotalPossibleGrade() {
|
||||
return $questionnaire.questions.reduce((acc, question) => {
|
||||
acc += question.points;
|
||||
return acc;
|
||||
}, 0);
|
||||
}
|
||||
|
||||
async function checkForSubmission(people, profileId, courseId) {
|
||||
console.log('checkForSubmission');
|
||||
|
||||
if (!Array.isArray(people) || !profileId || !courseId || !!submission) {
|
||||
return;
|
||||
}
|
||||
|
||||
const args = {
|
||||
exerciseId,
|
||||
courseId,
|
||||
submittedBy: getGroupMemberId(people, profileId),
|
||||
};
|
||||
const { data } = await fetchSubmission(args);
|
||||
|
||||
if (Array.isArray(data) && data.length) {
|
||||
console.log(`submissions`, data);
|
||||
submission = data[0];
|
||||
|
||||
$questionnaireMetaData.answers = formatAnswers({
|
||||
questions: $questionnaire.questions,
|
||||
answers: submission.answers,
|
||||
});
|
||||
|
||||
$questionnaireMetaData.totalPossibleGrade = getTotalPossibleGrade();
|
||||
|
||||
$questionnaireMetaData.currentQuestionIndex =
|
||||
$questionnaire.questions.length;
|
||||
$questionnaireMetaData.isFinished = true;
|
||||
$questionnaireMetaData.status = submission.status_id;
|
||||
$questionnaireMetaData.finalTotalGrade = 0;
|
||||
$questionnaireMetaData.grades = submission.answers.reduce(
|
||||
(acc, answer) => {
|
||||
acc[answer.question_id] = answer.point;
|
||||
$questionnaireMetaData.finalTotalGrade += answer.point;
|
||||
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$: {
|
||||
currentQuestion =
|
||||
$questionnaire.questions[$questionnaireMetaData.currentQuestionIndex - 1];
|
||||
@@ -114,6 +165,8 @@
|
||||
$: $questionnaireMetaData.progressValue = getProgressValue(
|
||||
$questionnaireMetaData.currentQuestionIndex
|
||||
);
|
||||
|
||||
$: checkForSubmission($group.people, $profile.id, $course.id);
|
||||
</script>
|
||||
|
||||
{#if !preview && $questionnaire.questions.length && !$questionnaireMetaData.isFinished}
|
||||
@@ -137,7 +190,20 @@
|
||||
{:else if $questionnaireMetaData.currentQuestionIndex === 0}
|
||||
<div>
|
||||
<h2 class="my-1">{$questionnaire.title}</h2>
|
||||
<p>{$questionnaire.description}</p>
|
||||
|
||||
<div class="flex items-center">
|
||||
<p class="mx-2">
|
||||
<strong>{$questionnaire.questions.length}</strong> questions
|
||||
</p>
|
||||
|
|
||||
<p class="mx-2">
|
||||
<strong>{getTotalPossibleGrade()}</strong> points.
|
||||
</p>
|
||||
|
|
||||
<p class="mx-2">All required</p>
|
||||
</div>
|
||||
|
||||
<p class="text-lg mt-3">{$questionnaire.description}</p>
|
||||
|
||||
<PrimaryButton
|
||||
onClick={handleStart}
|
||||
@@ -150,20 +216,20 @@
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex flex-col justify-between">
|
||||
<h2 class="text-xl mb-2 mt-0">{$questionnaire.title}</h2>
|
||||
{#if STATUS.PENDING === $questionnaireMetaData.status}
|
||||
<span
|
||||
class="bg-yellow-600 text-white rounded-full py-2 px-6 text-center"
|
||||
title="Status: Pending Review"
|
||||
>
|
||||
Pending
|
||||
</span>
|
||||
{:else if STATUS.GRADED === $questionnaireMetaData.status && getTotalScores() > 0}
|
||||
{#if STATUS.GRADED === $questionnaireMetaData.status}
|
||||
<span
|
||||
class="bg-green-700 text-white rounded-full py-2 px-6 text-center"
|
||||
title="Status: Pending Review"
|
||||
>
|
||||
Graded
|
||||
</span>
|
||||
{:else}
|
||||
<span
|
||||
class="bg-yellow-600 text-white rounded-full py-2 px-6 text-center"
|
||||
title="Status: Pending Review"
|
||||
>
|
||||
Pending
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -171,12 +237,14 @@
|
||||
class="border-2 border-gray-700 rounded-full h-24 w-24 flex items-center justify-center text-2xl"
|
||||
title="Status: Pending Review"
|
||||
>
|
||||
{getTotalScores()}/100
|
||||
{$questionnaireMetaData.finalTotalGrade}/{$questionnaireMetaData.totalPossibleGrade}
|
||||
</span>
|
||||
</div>
|
||||
<Preview
|
||||
questions={$questionnaire.questions}
|
||||
questionnaireMetaData={$questionnaireMetaData}
|
||||
grades={$questionnaireMetaData.grades}
|
||||
disableGrading={true}
|
||||
/>
|
||||
{:else if currentQuestion}
|
||||
{#key currentQuestion.id}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
export const STATUS = {
|
||||
PENDING: 0,
|
||||
IN_PROGRESS: 1,
|
||||
GRADED: 2,
|
||||
SUBMITTED: 1,
|
||||
IN_PROGRESS: 2,
|
||||
GRADED: 3,
|
||||
};
|
||||
|
||||
export const SELECTABLE_STATUS = [
|
||||
{
|
||||
label: 'Submitted',
|
||||
value: STATUS.PENDING,
|
||||
value: STATUS.SUBMITTED,
|
||||
},
|
||||
{
|
||||
label: 'In progress',
|
||||
|
||||
@@ -13,6 +13,9 @@ import { STATUS } from '../Exercise/constants';
|
||||
const initAnswerState = {
|
||||
answers: {},
|
||||
scores: {},
|
||||
grades: {},
|
||||
totalPossibleGrade: 0,
|
||||
finalTotalGrade: 0,
|
||||
currentQuestionIndex: 0,
|
||||
isFinished: false,
|
||||
progressValue: 100,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ROUTES } from "./constants";
|
||||
import { ROUTES } from './constants';
|
||||
|
||||
export function getNavItemRoute(courseId, routeId) {
|
||||
const path = `/${ROUTES.COURSES}/${courseId}`;
|
||||
@@ -27,3 +27,23 @@ export function getLectureNo(index) {
|
||||
|
||||
return `0${index}`;
|
||||
}
|
||||
|
||||
export function formatAnswers(data) {
|
||||
const answers = {};
|
||||
const questionByIdAndName = {};
|
||||
|
||||
for (const question of data.questions) {
|
||||
questionByIdAndName[question.id] = question.name;
|
||||
}
|
||||
|
||||
for (const answer of data.answers) {
|
||||
const questionName = questionByIdAndName[answer.question_id];
|
||||
|
||||
answers[questionName] =
|
||||
Array.isArray(answer.answers) && answer.answers.length
|
||||
? answer.answers
|
||||
: answer.open_answer;
|
||||
}
|
||||
|
||||
return answers;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user