mirror of
https://github.com/rzmk/learnhouse.git
synced 2025-12-19 04:19:25 +00:00
feat: grading, mark as done and grade showing
This commit is contained in:
parent
9530ee6930
commit
40ef2d0cec
7 changed files with 572 additions and 94 deletions
|
|
@ -20,7 +20,10 @@ from src.services.courses.activities.assignments import (
|
||||||
delete_assignment_submission,
|
delete_assignment_submission,
|
||||||
delete_assignment_task,
|
delete_assignment_task,
|
||||||
delete_assignment_task_submission,
|
delete_assignment_task_submission,
|
||||||
|
get_grade_assignment_submission,
|
||||||
|
grade_assignment_submission,
|
||||||
handle_assignment_task_submission,
|
handle_assignment_task_submission,
|
||||||
|
mark_activity_as_done_for_user,
|
||||||
put_assignment_task_reference_file,
|
put_assignment_task_reference_file,
|
||||||
put_assignment_task_submission_file,
|
put_assignment_task_submission_file,
|
||||||
read_assignment,
|
read_assignment,
|
||||||
|
|
@ -263,7 +266,9 @@ async def api_handle_assignment_task_submissions(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/{assignment_uuid}/tasks/{assignment_task_uuid}/submissions/user/{user_id}")
|
@router.get(
|
||||||
|
"/{assignment_uuid}/tasks/{assignment_task_uuid}/submissions/user/{user_id}"
|
||||||
|
)
|
||||||
async def api_read_user_assignment_task_submissions(
|
async def api_read_user_assignment_task_submissions(
|
||||||
request: Request,
|
request: Request,
|
||||||
assignment_task_uuid: str,
|
assignment_task_uuid: str,
|
||||||
|
|
@ -410,7 +415,7 @@ async def api_update_user_assignment_submissions(
|
||||||
@router.delete("/{assignment_uuid}/submissions/{user_id}")
|
@router.delete("/{assignment_uuid}/submissions/{user_id}")
|
||||||
async def api_delete_user_assignment_submissions(
|
async def api_delete_user_assignment_submissions(
|
||||||
request: Request,
|
request: Request,
|
||||||
assignment_id: str,
|
assignment_uuid: str,
|
||||||
user_id: str,
|
user_id: str,
|
||||||
current_user: PublicUser = Depends(get_current_user),
|
current_user: PublicUser = Depends(get_current_user),
|
||||||
db_session=Depends(get_db_session),
|
db_session=Depends(get_db_session),
|
||||||
|
|
@ -419,5 +424,53 @@ async def api_delete_user_assignment_submissions(
|
||||||
Delete submissions for an assignment from a user
|
Delete submissions for an assignment from a user
|
||||||
"""
|
"""
|
||||||
return await delete_assignment_submission(
|
return await delete_assignment_submission(
|
||||||
request, assignment_id, user_id, current_user, db_session
|
request, user_id, assignment_uuid, current_user, db_session
|
||||||
|
)
|
||||||
|
@router.get("/{assignment_uuid}/submissions/{user_id}/grade")
|
||||||
|
async def api_get_submission_grade(
|
||||||
|
request: Request,
|
||||||
|
assignment_uuid: str,
|
||||||
|
user_id: str,
|
||||||
|
current_user: PublicUser = Depends(get_current_user),
|
||||||
|
db_session=Depends(get_db_session),
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Grade submissions for an assignment from a user
|
||||||
|
"""
|
||||||
|
|
||||||
|
return await get_grade_assignment_submission(
|
||||||
|
request, user_id, assignment_uuid, current_user, db_session
|
||||||
|
)
|
||||||
|
|
||||||
|
@router.post("/{assignment_uuid}/submissions/{user_id}/grade")
|
||||||
|
async def api_final_grade_submission(
|
||||||
|
request: Request,
|
||||||
|
assignment_uuid: str,
|
||||||
|
user_id: str,
|
||||||
|
current_user: PublicUser = Depends(get_current_user),
|
||||||
|
db_session=Depends(get_db_session),
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Grade submissions for an assignment from a user
|
||||||
|
"""
|
||||||
|
|
||||||
|
return await grade_assignment_submission(
|
||||||
|
request, user_id, assignment_uuid, current_user, db_session
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/{assignment_uuid}/submissions/{user_id}/done")
|
||||||
|
async def api_submission_mark_as_done(
|
||||||
|
request: Request,
|
||||||
|
assignment_uuid: str,
|
||||||
|
user_id: str,
|
||||||
|
current_user: PublicUser = Depends(get_current_user),
|
||||||
|
db_session=Depends(get_db_session),
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Grade submissions for an assignment from a user
|
||||||
|
"""
|
||||||
|
|
||||||
|
return await mark_activity_as_done_for_user(
|
||||||
|
request, user_id, assignment_uuid, current_user, db_session
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,9 @@ from src.db.courses.assignments import (
|
||||||
)
|
)
|
||||||
from src.db.courses.courses import Course
|
from src.db.courses.courses import Course
|
||||||
from src.db.organizations import Organization
|
from src.db.organizations import Organization
|
||||||
from src.db.users import AnonymousUser, PublicUser
|
from src.db.trail_runs import TrailRun
|
||||||
|
from src.db.trail_steps import TrailStep
|
||||||
|
from src.db.users import AnonymousUser, PublicUser, User
|
||||||
from src.security.rbac.rbac import (
|
from src.security.rbac.rbac import (
|
||||||
authorization_verify_based_on_roles_and_authorship_and_usergroups,
|
authorization_verify_based_on_roles_and_authorship_and_usergroups,
|
||||||
authorization_verify_if_element_is_public,
|
authorization_verify_if_element_is_public,
|
||||||
|
|
@ -39,6 +41,7 @@ from src.services.courses.activities.uploads.sub_file import upload_submission_f
|
||||||
from src.services.courses.activities.uploads.tasks_ref_files import (
|
from src.services.courses.activities.uploads.tasks_ref_files import (
|
||||||
upload_reference_file,
|
upload_reference_file,
|
||||||
)
|
)
|
||||||
|
from src.services.trail.trail import check_trail_presence
|
||||||
|
|
||||||
## > Assignments CRUD
|
## > Assignments CRUD
|
||||||
|
|
||||||
|
|
@ -1097,6 +1100,80 @@ async def create_assignment_submission(
|
||||||
db_session.add(assignment_user_submission)
|
db_session.add(assignment_user_submission)
|
||||||
db_session.commit()
|
db_session.commit()
|
||||||
|
|
||||||
|
# User
|
||||||
|
statement = select(User).where(User.id == current_user.id)
|
||||||
|
user = db_session.exec(statement).first()
|
||||||
|
|
||||||
|
if not user:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=404,
|
||||||
|
detail="User not found",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Activity
|
||||||
|
statement = select(Activity).where(Activity.id == assignment.activity_id)
|
||||||
|
activity = db_session.exec(statement).first()
|
||||||
|
|
||||||
|
if not activity:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=404,
|
||||||
|
detail="Activity not found",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Add TrailStep
|
||||||
|
trail = await check_trail_presence(
|
||||||
|
org_id=course.org_id,
|
||||||
|
user_id=user.id,
|
||||||
|
request=request,
|
||||||
|
user=user,
|
||||||
|
db_session=db_session,
|
||||||
|
)
|
||||||
|
|
||||||
|
statement = select(TrailRun).where(
|
||||||
|
TrailRun.trail_id == trail.id,
|
||||||
|
TrailRun.course_id == course.id,
|
||||||
|
TrailRun.user_id == user.id,
|
||||||
|
)
|
||||||
|
trailrun = db_session.exec(statement).first()
|
||||||
|
|
||||||
|
if not trailrun:
|
||||||
|
trailrun = TrailRun(
|
||||||
|
trail_id=trail.id if trail.id is not None else 0,
|
||||||
|
course_id=course.id if course.id is not None else 0,
|
||||||
|
org_id=course.org_id,
|
||||||
|
user_id=user.id,
|
||||||
|
creation_date=str(datetime.now()),
|
||||||
|
update_date=str(datetime.now()),
|
||||||
|
)
|
||||||
|
db_session.add(trailrun)
|
||||||
|
db_session.commit()
|
||||||
|
db_session.refresh(trailrun)
|
||||||
|
|
||||||
|
statement = select(TrailStep).where(
|
||||||
|
TrailStep.trailrun_id == trailrun.id,
|
||||||
|
TrailStep.activity_id == activity.id,
|
||||||
|
TrailStep.user_id == user.id,
|
||||||
|
)
|
||||||
|
trailstep = db_session.exec(statement).first()
|
||||||
|
|
||||||
|
if not trailstep:
|
||||||
|
trailstep = TrailStep(
|
||||||
|
trailrun_id=trailrun.id if trailrun.id is not None else 0,
|
||||||
|
activity_id=activity.id if activity.id is not None else 0,
|
||||||
|
course_id=course.id if course.id is not None else 0,
|
||||||
|
trail_id=trail.id if trail.id is not None else 0,
|
||||||
|
org_id=course.org_id,
|
||||||
|
complete=True,
|
||||||
|
teacher_verified=False,
|
||||||
|
grade="",
|
||||||
|
user_id=user.id,
|
||||||
|
creation_date=str(datetime.now()),
|
||||||
|
update_date=str(datetime.now()),
|
||||||
|
)
|
||||||
|
db_session.add(trailstep)
|
||||||
|
db_session.commit()
|
||||||
|
db_session.refresh(trailstep)
|
||||||
|
|
||||||
# return assignment user submission read
|
# return assignment user submission read
|
||||||
return AssignmentUserSubmissionRead.model_validate(assignment_user_submission)
|
return AssignmentUserSubmissionRead.model_validate(assignment_user_submission)
|
||||||
|
|
||||||
|
|
@ -1262,14 +1339,24 @@ async def update_assignment_submission(
|
||||||
async def delete_assignment_submission(
|
async def delete_assignment_submission(
|
||||||
request: Request,
|
request: Request,
|
||||||
user_id: str,
|
user_id: str,
|
||||||
assignment_id: str,
|
assignment_uuid: str,
|
||||||
current_user: PublicUser | AnonymousUser,
|
current_user: PublicUser | AnonymousUser,
|
||||||
db_session: Session,
|
db_session: Session,
|
||||||
):
|
):
|
||||||
|
# Check if assignment exists
|
||||||
|
statement = select(Assignment).where(Assignment.assignment_uuid == assignment_uuid)
|
||||||
|
assignment = db_session.exec(statement).first()
|
||||||
|
|
||||||
|
if not assignment:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=404,
|
||||||
|
detail="Assignment not found",
|
||||||
|
)
|
||||||
|
|
||||||
# Check if assignment user submission exists
|
# Check if assignment user submission exists
|
||||||
statement = select(AssignmentUserSubmission).where(
|
statement = select(AssignmentUserSubmission).where(
|
||||||
AssignmentUserSubmission.user_id == user_id,
|
AssignmentUserSubmission.user_id == user_id,
|
||||||
AssignmentUserSubmission.assignment_id == assignment_id,
|
AssignmentUserSubmission.assignment_id == assignment.id,
|
||||||
)
|
)
|
||||||
assignment_user_submission = db_session.exec(statement).first()
|
assignment_user_submission = db_session.exec(statement).first()
|
||||||
|
|
||||||
|
|
@ -1279,18 +1366,6 @@ async def delete_assignment_submission(
|
||||||
detail="Assignment User Submission not found",
|
detail="Assignment User Submission not found",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Check if assignment exists
|
|
||||||
statement = select(Assignment).where(
|
|
||||||
Assignment.id == assignment_user_submission.assignment_id
|
|
||||||
)
|
|
||||||
assignment = db_session.exec(statement).first()
|
|
||||||
|
|
||||||
if not assignment:
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=404,
|
|
||||||
detail="Assignment not found",
|
|
||||||
)
|
|
||||||
|
|
||||||
# Check if course exists
|
# Check if course exists
|
||||||
statement = select(Course).where(Course.id == assignment.course_id)
|
statement = select(Course).where(Course.id == assignment.course_id)
|
||||||
course = db_session.exec(statement).first()
|
course = db_session.exec(statement).first()
|
||||||
|
|
@ -1311,6 +1386,197 @@ async def delete_assignment_submission(
|
||||||
return {"message": "Assignment User Submission deleted"}
|
return {"message": "Assignment User Submission deleted"}
|
||||||
|
|
||||||
|
|
||||||
|
## > Assignments Submissions Grading
|
||||||
|
async def grade_assignment_submission(
|
||||||
|
request: Request,
|
||||||
|
user_id: str,
|
||||||
|
assignment_uuid: str,
|
||||||
|
current_user: PublicUser | AnonymousUser,
|
||||||
|
db_session: Session,
|
||||||
|
):
|
||||||
|
|
||||||
|
# Check if assignment exists
|
||||||
|
statement = select(Assignment).where(Assignment.assignment_uuid == assignment_uuid)
|
||||||
|
assignment = db_session.exec(statement).first()
|
||||||
|
|
||||||
|
if not assignment:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=404,
|
||||||
|
detail="Assignment not found",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check if assignment user submission exists
|
||||||
|
statement = select(AssignmentUserSubmission).where(
|
||||||
|
AssignmentUserSubmission.user_id == user_id,
|
||||||
|
AssignmentUserSubmission.assignment_id == assignment.id,
|
||||||
|
)
|
||||||
|
assignment_user_submission = db_session.exec(statement).first()
|
||||||
|
|
||||||
|
if not assignment_user_submission:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=404,
|
||||||
|
detail="Assignment User Submission not found",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Get all the task submissions for the user
|
||||||
|
task_subs = select(AssignmentTaskSubmission).where(
|
||||||
|
AssignmentTaskSubmission.user_id == user_id,
|
||||||
|
AssignmentTaskSubmission.activity_id == assignment.activity_id,
|
||||||
|
)
|
||||||
|
task_submissions = db_session.exec(task_subs).all()
|
||||||
|
|
||||||
|
# Calculate the grade
|
||||||
|
grade = 0
|
||||||
|
for task_submission in task_submissions:
|
||||||
|
grade += task_submission.grade
|
||||||
|
|
||||||
|
# Update the assignment user submission
|
||||||
|
assignment_user_submission.grade = grade
|
||||||
|
|
||||||
|
# Insert Assignment User Submission in DB
|
||||||
|
db_session.add(assignment_user_submission)
|
||||||
|
db_session.commit()
|
||||||
|
db_session.refresh(assignment_user_submission)
|
||||||
|
|
||||||
|
# Change the status of the submission
|
||||||
|
assignment_user_submission.submission_status = AssignmentUserSubmissionStatus.GRADED
|
||||||
|
|
||||||
|
# Insert Assignment User Submission in DB
|
||||||
|
db_session.add(assignment_user_submission)
|
||||||
|
db_session.commit()
|
||||||
|
db_session.refresh(assignment_user_submission)
|
||||||
|
|
||||||
|
# return OK
|
||||||
|
return {
|
||||||
|
"message": "Assignment User Submission graded with the grade of " + str(grade)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async def get_grade_assignment_submission(
|
||||||
|
request: Request,
|
||||||
|
user_id: str,
|
||||||
|
assignment_uuid: str,
|
||||||
|
current_user: PublicUser | AnonymousUser,
|
||||||
|
db_session: Session,
|
||||||
|
):
|
||||||
|
|
||||||
|
# Check if assignment exists
|
||||||
|
statement = select(Assignment).where(Assignment.assignment_uuid == assignment_uuid)
|
||||||
|
assignment = db_session.exec(statement).first()
|
||||||
|
|
||||||
|
if not assignment:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=404,
|
||||||
|
detail="Assignment not found",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check if assignment user submission exists
|
||||||
|
statement = select(AssignmentUserSubmission).where(
|
||||||
|
AssignmentUserSubmission.user_id == user_id,
|
||||||
|
AssignmentUserSubmission.assignment_id == assignment.id,
|
||||||
|
)
|
||||||
|
assignment_user_submission = db_session.exec(statement).first()
|
||||||
|
|
||||||
|
if not assignment_user_submission:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=404,
|
||||||
|
detail="Assignment User Submission not found",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Get the max grade value from the sum of every assignmenttask
|
||||||
|
statement = select(AssignmentTask).where(
|
||||||
|
AssignmentTask.assignment_id == assignment.id
|
||||||
|
)
|
||||||
|
assignment_tasks = db_session.exec(statement).all()
|
||||||
|
max_grade = 0
|
||||||
|
|
||||||
|
for task in assignment_tasks:
|
||||||
|
max_grade += task.max_grade_value
|
||||||
|
|
||||||
|
# Now get the grade from the user submission
|
||||||
|
statement = select(AssignmentUserSubmission).where(
|
||||||
|
AssignmentUserSubmission.user_id == user_id,
|
||||||
|
AssignmentUserSubmission.assignment_id == assignment.id,
|
||||||
|
)
|
||||||
|
assignment_user_submission = db_session.exec(statement).first()
|
||||||
|
|
||||||
|
if not assignment_user_submission:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=404,
|
||||||
|
detail="Assignment User Submission not found",
|
||||||
|
)
|
||||||
|
|
||||||
|
# return the grade
|
||||||
|
return {
|
||||||
|
"grade": int(assignment_user_submission.grade),
|
||||||
|
"max_grade": max_grade,
|
||||||
|
"grading_type": assignment.grading_type,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async def mark_activity_as_done_for_user(
|
||||||
|
request: Request,
|
||||||
|
user_id: str,
|
||||||
|
assignment_uuid: str,
|
||||||
|
current_user: PublicUser | AnonymousUser,
|
||||||
|
db_session: Session,
|
||||||
|
):
|
||||||
|
# Get Assignment
|
||||||
|
statement = select(Assignment).where(Assignment.assignment_uuid == assignment_uuid)
|
||||||
|
assignment = db_session.exec(statement).first()
|
||||||
|
|
||||||
|
if not assignment:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=404,
|
||||||
|
detail="Assignment not found",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check if activity exists
|
||||||
|
statement = select(Activity).where(Activity.id == assignment.activity_id)
|
||||||
|
activity = db_session.exec(statement).first()
|
||||||
|
|
||||||
|
if not activity:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=404,
|
||||||
|
detail="Activity not found",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check if user exists
|
||||||
|
statement = select(User).where(User.id == user_id)
|
||||||
|
user = db_session.exec(statement).first()
|
||||||
|
|
||||||
|
if not user:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=404,
|
||||||
|
detail="User not found",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check if user is enrolled in the course
|
||||||
|
trailsteps = select(TrailStep).where(
|
||||||
|
TrailStep.activity_id == activity.id,
|
||||||
|
TrailStep.user_id == user_id,
|
||||||
|
)
|
||||||
|
trailstep = db_session.exec(trailsteps).first()
|
||||||
|
|
||||||
|
if not trailstep:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=404,
|
||||||
|
detail="User not enrolled in the course",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Mark activity as done
|
||||||
|
trailstep.complete = True
|
||||||
|
trailstep.update_date = str(datetime.now())
|
||||||
|
|
||||||
|
# Insert TrailStep in DB
|
||||||
|
db_session.add(trailstep)
|
||||||
|
db_session.commit()
|
||||||
|
db_session.refresh(trailstep)
|
||||||
|
|
||||||
|
# return OK
|
||||||
|
return {"message": "Activity marked as done for user"}
|
||||||
|
|
||||||
|
|
||||||
## 🔒 RBAC Utils ##
|
## 🔒 RBAC Utils ##
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -244,7 +244,7 @@ async def add_activity_to_trail(
|
||||||
course_id=course.id if course.id is not None else 0,
|
course_id=course.id if course.id is not None else 0,
|
||||||
trail_id=trail.id if trail.id is not None else 0,
|
trail_id=trail.id if trail.id is not None else 0,
|
||||||
org_id=course.org_id,
|
org_id=course.org_id,
|
||||||
complete=False,
|
complete=True,
|
||||||
teacher_verified=False,
|
teacher_verified=False,
|
||||||
grade="",
|
grade="",
|
||||||
user_id=user.id,
|
user_id=user.id,
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import Link from 'next/link'
|
||||||
import { getAPIUrl, getUriWithOrg } from '@services/config/config'
|
import { getAPIUrl, getUriWithOrg } from '@services/config/config'
|
||||||
import Canva from '@components/Objects/Activities/DynamicCanva/DynamicCanva'
|
import Canva from '@components/Objects/Activities/DynamicCanva/DynamicCanva'
|
||||||
import VideoActivity from '@components/Objects/Activities/Video/Video'
|
import VideoActivity from '@components/Objects/Activities/Video/Video'
|
||||||
import { BookOpenCheck, Check, MoreVertical, UserRoundPen } from 'lucide-react'
|
import { BookOpenCheck, Check, CheckCircle, MoreVertical, UserRoundPen } from 'lucide-react'
|
||||||
import { markActivityAsComplete } from '@services/courses/activity'
|
import { markActivityAsComplete } from '@services/courses/activity'
|
||||||
import DocumentPdfActivity from '@components/Objects/Activities/DocumentPdf/DocumentPdf'
|
import DocumentPdfActivity from '@components/Objects/Activities/DocumentPdf/DocumentPdf'
|
||||||
import ActivityIndicators from '@components/Pages/Courses/ActivityIndicators'
|
import ActivityIndicators from '@components/Pages/Courses/ActivityIndicators'
|
||||||
|
|
@ -17,7 +17,7 @@ import AIActivityAsk from '@components/Objects/Activities/AI/AIActivityAsk'
|
||||||
import AIChatBotProvider from '@components/Contexts/AI/AIChatBotContext'
|
import AIChatBotProvider from '@components/Contexts/AI/AIChatBotContext'
|
||||||
import { useLHSession } from '@components/Contexts/LHSessionContext'
|
import { useLHSession } from '@components/Contexts/LHSessionContext'
|
||||||
import React, { useEffect } from 'react'
|
import React, { useEffect } from 'react'
|
||||||
import { getAssignmentFromActivityUUID, submitAssignmentForGrading } from '@services/courses/assignments'
|
import { getAssignmentFromActivityUUID, getFinalGrade, submitAssignmentForGrading } from '@services/courses/assignments'
|
||||||
import AssignmentStudentActivity from '@components/Objects/Activities/Assignment/AssignmentStudentActivity'
|
import AssignmentStudentActivity from '@components/Objects/Activities/Assignment/AssignmentStudentActivity'
|
||||||
import { AssignmentProvider } from '@components/Contexts/Assignments/AssignmentContext'
|
import { AssignmentProvider } from '@components/Contexts/Assignments/AssignmentContext'
|
||||||
import { AssignmentsTaskProvider } from '@components/Contexts/Assignments/AssignmentsTaskContext'
|
import { AssignmentsTaskProvider } from '@components/Contexts/Assignments/AssignmentsTaskContext'
|
||||||
|
|
@ -240,7 +240,7 @@ export function MarkStatus(props: {
|
||||||
)
|
)
|
||||||
if (run) {
|
if (run) {
|
||||||
return run.steps.find(
|
return run.steps.find(
|
||||||
(step: any) => step.activity_id == props.activity.id
|
(step: any) => (step.activity_id == props.activity.id) && (step.complete == true)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -252,7 +252,7 @@ export function MarkStatus(props: {
|
||||||
<i>
|
<i>
|
||||||
<Check size={17}></Check>
|
<Check size={17}></Check>
|
||||||
</i>{' '}
|
</i>{' '}
|
||||||
<i className="not-italic text-xs font-bold">Already completed</i>
|
<i className="not-italic text-xs font-bold">Complete</i>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div
|
<div
|
||||||
|
|
@ -279,6 +279,7 @@ function AssignmentTools(props: {
|
||||||
}) {
|
}) {
|
||||||
const submission = useAssignmentSubmission() as any
|
const submission = useAssignmentSubmission() as any
|
||||||
const session = useLHSession() as any;
|
const session = useLHSession() as any;
|
||||||
|
const [finalGrade, setFinalGrade] = React.useState(null) as any;
|
||||||
|
|
||||||
const submitForGradingUI = async () => {
|
const submitForGradingUI = async () => {
|
||||||
if (props.assignment) {
|
if (props.assignment) {
|
||||||
|
|
@ -296,38 +297,91 @@ function AssignmentTools(props: {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getGradingBasedOnMethod = async () => {
|
||||||
|
const res = await getFinalGrade(
|
||||||
|
session.data?.user?.id,
|
||||||
|
props.assignment?.assignment_uuid,
|
||||||
|
session.data?.tokens?.access_token
|
||||||
|
);
|
||||||
|
|
||||||
|
if (res.success) {
|
||||||
|
const { grade, max_grade, grading_type } = res.data;
|
||||||
|
let displayGrade;
|
||||||
|
|
||||||
|
switch (grading_type) {
|
||||||
|
case 'ALPHABET':
|
||||||
|
displayGrade = convertNumericToAlphabet(grade, max_grade);
|
||||||
|
break;
|
||||||
|
case 'NUMERIC':
|
||||||
|
displayGrade = `${grade}/${max_grade}`;
|
||||||
|
break;
|
||||||
|
case 'PERCENTAGE':
|
||||||
|
const percentage = (grade / max_grade) * 100;
|
||||||
|
displayGrade = `${percentage.toFixed(2)}%`;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
displayGrade = 'Unknown grading type';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use displayGrade here, e.g., update state or display it
|
||||||
|
setFinalGrade(displayGrade);
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Helper function to convert numeric grade to alphabet grade
|
||||||
|
function convertNumericToAlphabet(grade : any, maxGrade : any) {
|
||||||
|
const percentage = (grade / maxGrade) * 100;
|
||||||
|
if (percentage >= 90) return 'A';
|
||||||
|
if (percentage >= 80) return 'B';
|
||||||
|
if (percentage >= 70) return 'C';
|
||||||
|
if (percentage >= 60) return 'D';
|
||||||
|
return 'F';
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
getGradingBasedOnMethod();
|
||||||
}
|
}
|
||||||
, [submission, props.assignment])
|
, [submission, props.assignment])
|
||||||
|
|
||||||
return <>
|
if (!submission || submission.length === 0) {
|
||||||
{submission && submission.length == 0 && (
|
return (
|
||||||
<ConfirmationModal
|
<ConfirmationModal
|
||||||
confirmationButtonText="Submit Assignment"
|
confirmationButtonText="Submit Assignment"
|
||||||
confirmationMessage="Are you sure you want to submit your assignment for grading?, once submitted you will not be able to make any changes"
|
confirmationMessage="Are you sure you want to submit your assignment for grading? Once submitted, you will not be able to make any changes."
|
||||||
dialogTitle="Submit your assignment for grading"
|
dialogTitle="Submit your assignment for grading"
|
||||||
dialogTrigger={
|
dialogTrigger={
|
||||||
<div
|
<div className="bg-cyan-800 rounded-full px-5 drop-shadow-md flex items-center space-x-2 p-2.5 text-white hover:cursor-pointer transition delay-150 duration-300 ease-in-out">
|
||||||
className="bg-cyan-800 rounded-full px-5 drop-shadow-md flex items-center space-x-2 p-2.5 text-white hover:cursor-pointer transition delay-150 duration-300 ease-in-out">
|
<BookOpenCheck size={17} />
|
||||||
<i>
|
<span className="text-xs font-bold">Submit for grading</span>
|
||||||
<BookOpenCheck size={17}></BookOpenCheck>
|
</div>
|
||||||
</i>{' '}
|
}
|
||||||
<i className="not-italic text-xs font-bold">Submit for grading</i>
|
functionToExecute={submitForGradingUI}
|
||||||
</div>}
|
|
||||||
functionToExecute={() => submitForGradingUI()}
|
|
||||||
status="info"
|
status="info"
|
||||||
/>
|
/>
|
||||||
)}
|
)
|
||||||
{submission && submission.length > 0 && (
|
|
||||||
<div
|
|
||||||
className="bg-amber-800 rounded-full px-5 drop-shadow-md flex items-center space-x-2 p-2.5 text-white transition delay-150 duration-300 ease-in-out">
|
|
||||||
<i>
|
|
||||||
<UserRoundPen size={17}></UserRoundPen>
|
|
||||||
</i>{' '}
|
|
||||||
<i className="not-italic text-xs font-bold">Grading in progress</i>
|
|
||||||
</div>)
|
|
||||||
}
|
}
|
||||||
</>
|
|
||||||
|
if (submission[0].submission_status === 'SUBMITTED') {
|
||||||
|
return (
|
||||||
|
<div className="bg-amber-800 rounded-full px-5 drop-shadow-md flex items-center space-x-2 p-2.5 text-white transition delay-150 duration-300 ease-in-out">
|
||||||
|
<UserRoundPen size={17} />
|
||||||
|
<span className="text-xs font-bold">Grading in progress</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (submission[0].submission_status === 'GRADED') {
|
||||||
|
return (
|
||||||
|
<div className="bg-teal-600 rounded-full px-5 drop-shadow-md flex items-center space-x-2 p-2.5 text-white transition delay-150 duration-300 ease-in-out">
|
||||||
|
<CheckCircle size={17} />
|
||||||
|
<span className="text-xs flex space-x-2 font-bold items-center"><span>Graded </span> <span className='bg-white text-teal-800 px-1 py-0.5 rounded-md'>{finalGrade}</span></span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default return in case none of the conditions are met
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ActivityClient
|
export default ActivityClient
|
||||||
|
|
|
||||||
|
|
@ -195,7 +195,7 @@ export default function TaskFileObject({ view, user_id, assignmentTaskUUID }: Ta
|
||||||
<div className='flex flex-col space-y-1'>
|
<div className='flex flex-col space-y-1'>
|
||||||
<div className='flex py-5 text-sm justify-center mx-auto space-x-2 text-slate-500'>
|
<div className='flex py-5 text-sm justify-center mx-auto space-x-2 text-slate-500'>
|
||||||
<Download size={20} />
|
<Download size={20} />
|
||||||
<p>Please download the file and grade it manually, then input the grade below</p>
|
<p>Please download the file and grade it manually, then input the grade above</p>
|
||||||
</div>
|
</div>
|
||||||
{userSubmissions.fileUUID && !isLoading && assignmentTaskUUID && (
|
{userSubmissions.fileUUID && !isLoading && assignmentTaskUUID && (
|
||||||
<Link
|
<Link
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,48 @@
|
||||||
import { useAssignments } from '@components/Contexts/Assignments/AssignmentContext';
|
import { useAssignments } from '@components/Contexts/Assignments/AssignmentContext';
|
||||||
import { Download, Info, Medal } from 'lucide-react';
|
import { Apple, ArrowRightFromLine, BookOpenCheck, Check, Download, Info, Medal, MoveRight, X } from 'lucide-react';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import TaskQuizObject from '../../_components/TaskEditor/Subs/TaskTypes/TaskQuizObject';
|
import TaskQuizObject from '../../_components/TaskEditor/Subs/TaskTypes/TaskQuizObject';
|
||||||
import TaskFileObject from '../../_components/TaskEditor/Subs/TaskTypes/TaskFileObject';
|
import TaskFileObject from '../../_components/TaskEditor/Subs/TaskTypes/TaskFileObject';
|
||||||
import { useOrg } from '@components/Contexts/OrgContext';
|
import { useOrg } from '@components/Contexts/OrgContext';
|
||||||
import { getTaskRefFileDir } from '@services/media/media';
|
import { getTaskRefFileDir } from '@services/media/media';
|
||||||
|
import { deleteUserSubmission, markActivityAsDoneForUser, putFinalGrade } from '@services/courses/assignments';
|
||||||
|
import { useLHSession } from '@components/Contexts/LHSessionContext';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
import toast from 'react-hot-toast';
|
||||||
|
|
||||||
function EvaluateAssignment({ user_id }: any) {
|
function EvaluateAssignment({ user_id }: any) {
|
||||||
const assignments = useAssignments() as any;
|
const assignments = useAssignments() as any;
|
||||||
|
const session = useLHSession() as any;
|
||||||
const org = useOrg() as any;
|
const org = useOrg() as any;
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
async function gradeAssignment() {
|
||||||
|
const res = await putFinalGrade(user_id, assignments?.assignment_object.assignment_uuid, session.data?.tokens?.access_token);
|
||||||
|
if (res.success) {
|
||||||
|
toast.success(res.data.message)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
toast.error(res.data.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function markActivityAsDone() {
|
||||||
|
const res = await markActivityAsDoneForUser(user_id, assignments?.assignment_object.assignment_uuid, session.data?.tokens?.access_token)
|
||||||
|
if (res.success) {
|
||||||
|
toast.success(res.data.message)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
toast.error(res.data.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function rejectAssignment() {
|
||||||
|
const res = await deleteUserSubmission(user_id, assignments?.assignment_object.assignment_uuid, session.data?.tokens?.access_token)
|
||||||
|
toast.success('Assignment rejected successfully')
|
||||||
|
window.location.reload()
|
||||||
|
}
|
||||||
|
|
||||||
console.log(assignments);
|
|
||||||
return (
|
return (
|
||||||
<div className='flex-col space-y-4 px-3 py-3 overflow-y-auto min-h-fit'>
|
<div className='flex-col space-y-4 px-3 py-3 overflow-y-auto min-h-fit'>
|
||||||
{assignments && assignments?.assignment_tasks?.sort((a: any, b: any) => a.id - b.id).map((task: any, index: number) => {
|
{assignments && assignments?.assignment_tasks?.sort((a: any, b: any) => a.id - b.id).map((task: any, index: number) => {
|
||||||
|
|
@ -53,18 +84,29 @@ function EvaluateAssignment({ user_id }: any) {
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className='min-h-full'>
|
||||||
{task.assignment_type === 'QUIZ' && <TaskQuizObject key={task.assignment_task_uuid} view='grading' user_id={user_id} assignmentTaskUUID={task.assignment_task_uuid} />}
|
{task.assignment_type === 'QUIZ' && <TaskQuizObject key={task.assignment_task_uuid} view='grading' user_id={user_id} assignmentTaskUUID={task.assignment_task_uuid} />}
|
||||||
{task.assignment_type === 'FILE_SUBMISSION' && <TaskFileObject key={task.assignment_task_uuid} view='custom-grading' user_id={user_id} assignmentTaskUUID={task.assignment_task_uuid} />}
|
{task.assignment_type === 'FILE_SUBMISSION' && <TaskFileObject key={task.assignment_task_uuid} view='custom-grading' user_id={user_id} assignmentTaskUUID={task.assignment_task_uuid} />}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
<div className='flex flex-row-reverse font-semibold items-center'>
|
<div className='flex space-x-4 font-semibold items-center justify-between'>
|
||||||
<button className='flex space-x-2 px-4 py-2 bg-teal-600/80 text-white rounded-lg nice-shadow items-center'>
|
<button onClick={rejectAssignment} className='flex space-x-2 px-4 py-2 text-sm bg-rose-600/80 text-white rounded-lg nice-shadow items-center'>
|
||||||
<Medal size={18} />
|
<X size={18} />
|
||||||
|
<span>Reject Assignment</span>
|
||||||
|
</button>
|
||||||
|
<div className='flex space-x-3 items-center'>
|
||||||
|
<button onClick={gradeAssignment} className='flex space-x-2 px-4 py-2 text-sm bg-violet-600/80 text-white rounded-lg nice-shadow items-center'>
|
||||||
|
<BookOpenCheck size={18} />
|
||||||
<span>Set final grade</span>
|
<span>Set final grade</span>
|
||||||
</button>
|
</button>
|
||||||
|
<MoveRight className='text-gray-400' size={18} />
|
||||||
|
<button onClick={markActivityAsDone} className='flex space-x-2 px-4 py-2 text-sm bg-teal-600/80 text-white rounded-lg nice-shadow items-center'>
|
||||||
|
<Check size={18} />
|
||||||
|
<span>Mark Activity as Done for User</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -165,7 +165,6 @@ export async function updateReferenceFile(
|
||||||
assignmentUUID: string,
|
assignmentUUID: string,
|
||||||
access_token: string
|
access_token: string
|
||||||
) {
|
) {
|
||||||
|
|
||||||
// Send file thumbnail as form data
|
// Send file thumbnail as form data
|
||||||
const formData = new FormData()
|
const formData = new FormData()
|
||||||
|
|
||||||
|
|
@ -180,14 +179,12 @@ export async function updateReferenceFile(
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export async function updateSubFile(
|
export async function updateSubFile(
|
||||||
file: any,
|
file: any,
|
||||||
assignmentTaskUUID: string,
|
assignmentTaskUUID: string,
|
||||||
assignmentUUID: string,
|
assignmentUUID: string,
|
||||||
access_token: string
|
access_token: string
|
||||||
) {
|
) {
|
||||||
|
|
||||||
// Send file thumbnail as form data
|
// Send file thumbnail as form data
|
||||||
const formData = new FormData()
|
const formData = new FormData()
|
||||||
|
|
||||||
|
|
@ -215,3 +212,69 @@ export async function submitAssignmentForGrading(
|
||||||
const res = await getResponseMetadata(result)
|
const res = await getResponseMetadata(result)
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function deleteUserSubmission(
|
||||||
|
user_id: string,
|
||||||
|
assignmentUUID: string,
|
||||||
|
access_token: string
|
||||||
|
) {
|
||||||
|
const result: any = await fetch(
|
||||||
|
`${getAPIUrl()}assignments/${assignmentUUID}/submissions/${user_id}`,
|
||||||
|
RequestBodyWithAuthHeader('DELETE', null, null, access_token)
|
||||||
|
)
|
||||||
|
const res = await getResponseMetadata(result)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function putUserSubmission(
|
||||||
|
body: any,
|
||||||
|
user_id: string,
|
||||||
|
assignmentUUID: string,
|
||||||
|
access_token: string
|
||||||
|
) {
|
||||||
|
const result: any = await fetch(
|
||||||
|
`${getAPIUrl()}assignments/${assignmentUUID}/submissions/${user_id}`,
|
||||||
|
RequestBodyWithAuthHeader('PUT', body, null, access_token)
|
||||||
|
)
|
||||||
|
const res = await getResponseMetadata(result)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function putFinalGrade(
|
||||||
|
user_id: string,
|
||||||
|
assignmentUUID: string,
|
||||||
|
access_token: string
|
||||||
|
) {
|
||||||
|
const result: any = await fetch(
|
||||||
|
`${getAPIUrl()}assignments/${assignmentUUID}/submissions/${user_id}/grade`,
|
||||||
|
RequestBodyWithAuthHeader('POST', null, null, access_token)
|
||||||
|
)
|
||||||
|
const res = await getResponseMetadata(result)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getFinalGrade(
|
||||||
|
user_id: string,
|
||||||
|
assignmentUUID: string,
|
||||||
|
access_token: string
|
||||||
|
) {
|
||||||
|
const result: any = await fetch(
|
||||||
|
`${getAPIUrl()}assignments/${assignmentUUID}/submissions/${user_id}/grade`,
|
||||||
|
RequestBodyWithAuthHeader('GET', null, null, access_token)
|
||||||
|
)
|
||||||
|
const res = await getResponseMetadata(result)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function markActivityAsDoneForUser(
|
||||||
|
user_id: string,
|
||||||
|
assignmentUUID: string,
|
||||||
|
access_token: string
|
||||||
|
) {
|
||||||
|
const result: any = await fetch(
|
||||||
|
`${getAPIUrl()}assignments/${assignmentUUID}/submissions/${user_id}/done`,
|
||||||
|
RequestBodyWithAuthHeader('POST', null, null, access_token)
|
||||||
|
)
|
||||||
|
const res = await getResponseMetadata(result)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue