From 1115c1287a152fea59957e3b416acacbb62e4801 Mon Sep 17 00:00:00 2001 From: Kevin Charles Date: Mon, 6 Mar 2023 14:55:11 -0600 Subject: [PATCH] Grade override (#1955) --- src/Api/saveActivityOverrideGrades.php | 115 ++++++++++++++++++ src/Tools/_framework/Menus/CreditAchieved.jsx | 67 +++++++++- src/Tools/_framework/ToolPanels/Gradebook.jsx | 17 +++ .../ToolPanels/GradebookAssignment.jsx | 4 +- .../ToolPanels/GradebookStudentAssignment.jsx | 15 ++- 5 files changed, 206 insertions(+), 12 deletions(-) create mode 100644 src/Api/saveActivityOverrideGrades.php diff --git a/src/Api/saveActivityOverrideGrades.php b/src/Api/saveActivityOverrideGrades.php new file mode 100644 index 0000000000..d1eb2eb40a --- /dev/null +++ b/src/Api/saveActivityOverrideGrades.php @@ -0,0 +1,115 @@ +query( + "SELECT + totalPointsOrPercent, + courseId + FROM assignment + WHERE doenetId = '$doenetId'" + ); + if ($result->num_rows > 0) { + $row = $result->fetch_assoc(); + $totalPointsOrPercent = $row['totalPointsOrPercent']; + $courseId = $row['courseId']; + } else { + $success = false; + $message = "No assignment with doenetId: $doenetId"; + } +} + +//Check permissions +if ($success) { + $requestorPermissions = permissionsAndSettingsForOneCourseFunction( + $conn, + $requestorUserId, + $courseId + ); + + if ($requestorPermissions == false) { + $success = false; + $message = 'You are not authorized to view or modify grade data'; + } elseif ($requestorPermissions['canViewAndModifyGrades'] != '1') { + $success = false; + $message = 'You are only allowed to view your own data'; + } +} + +if ($success) { + $creditOverride = $score / $totalPointsOrPercent; + + // if we don't have a record for this user on the user_assignment table then we need to insert not update + $result = $conn->query( + "SELECT creditOverride + FROM user_assignment + WHERE doenetId = '$doenetId' + AND userId = '$userId'" + ); + + $need_insert = true; + if ($result->num_rows > 0) { + $need_insert = false; + } + + if ($need_insert) { + // insert creditOverride in user_assigment + $sql = "INSERT INTO user_assignment (doenetId,userId,credit,creditOverride) + VALUES + ('$doenetId','$userId','$creditOverride','$creditOverride') + "; + } else { + // update creditOverride in user_assigment + $sql = "UPDATE user_assignment + SET credit='$creditOverride', creditOverride='$creditOverride' + WHERE userId = '$userId' + AND doenetId = '$doenetId' + "; + } + $result = $conn->query($sql); +} + +$response_arr = [ + 'success' => $success, + 'message' => $message, +]; + +// set response code - 200 OK +http_response_code(200); + +echo json_encode($response_arr); + +$conn->close(); +?> diff --git a/src/Tools/_framework/Menus/CreditAchieved.jsx b/src/Tools/_framework/Menus/CreditAchieved.jsx index d92b71ec0c..1fe3af1ef5 100644 --- a/src/Tools/_framework/Menus/CreditAchieved.jsx +++ b/src/Tools/_framework/Menus/CreditAchieved.jsx @@ -1,5 +1,5 @@ import React, { useEffect, useState, useRef } from 'react'; -import { useRecoilValue, useRecoilCallback } from 'recoil'; +import { useRecoilValue, useRecoilCallback, useSetRecoilState, useRecoilState } from 'recoil'; import { searchParamAtomFamily } from '../NewToolRoot'; import axios from 'axios'; import { creditAchievedAtom, currentAttemptNumber } from '../ToolPanels/AssignmentViewer'; @@ -7,6 +7,12 @@ import styled from "styled-components"; import { itemByDoenetId } from '../../../_reactComponents/Course/CourseActions'; import { activityAttemptNumberSetUpAtom, currentPageAtom, itemWeightsAtom } from '../../../Viewer/ActivityViewer'; import { useLocation, useNavigate } from 'react-router'; +import { effectivePermissionsByCourseId } from '../../../_reactComponents/PanelHeaderComponents/RoleDropdown'; +import Button from '../../../_reactComponents/PanelHeaderComponents/Button'; +import ButtonGroup from '../../../_reactComponents/PanelHeaderComponents/ButtonGroup'; +import Textfield from '../../../_reactComponents/PanelHeaderComponents/Textfield'; +import { toastType, useToast } from '../Toast'; +import { overviewData } from '../ToolPanels/Gradebook'; const Line = styled.div` border-bottom: 2px solid var(--canvastext); @@ -23,9 +29,58 @@ const ScoreOnRight = styled.div` const ScoreContainer = styled.div` position: relative; background: ${props => props.highlight ? "var(--mainGray)" : "var(--canvas)"}; - cursor: ${props=> props.isLink ? "pointer" : "auto" }; + cursor: ${props => props.isLink ? "pointer" : "auto"}; ` +function FinalScore({ score }) { + const addToast = useToast(); + let courseId = useRecoilValue(searchParamAtomFamily('courseId')); + let doenetId = useRecoilValue(searchParamAtomFamily('doenetId')); + let userId = useRecoilValue(searchParamAtomFamily('userId')); + let { canViewAndModifyGrades } = useRecoilValue(effectivePermissionsByCourseId(courseId)); + const [creditAchieved, setCreditAchieved] = useRecoilState(creditAchievedAtom); + + + const [editMode, setEditMode] = useState(false); + const [scoreState, setScore] = useState(score); + const setOverview = useSetRecoilState(overviewData); + + if (editMode) { + return
+
Final Score:
+ {/* Original Final Score: {score} */} + { setScore(e.target.value) }} /> +
+ } + + if (canViewAndModifyGrades == 1) { + return Final Score