From c778a8b279bbb448a4c08d6fa5e572930ed79118 Mon Sep 17 00:00:00 2001 From: Anand <51914102+Jupiter-is-BIG@users.noreply.github.com> Date: Wed, 20 Mar 2024 05:02:48 -0700 Subject: [PATCH] Admin student (#46) * added enrollment handler route * fixed unit testing * HOTFIX: FIXES IN GET STUDENT COURSE DATA * fixed course seats check --- app/routes/course.py | 104 ++++++++++++++++-- app/schemas/course_schema.py | 12 ++ app/utils/utils.py | 2 + test/course/test_admin_decision_enrollment.py | 25 +++++ 4 files changed, 136 insertions(+), 7 deletions(-) create mode 100644 test/course/test_admin_decision_enrollment.py diff --git a/app/routes/course.py b/app/routes/course.py index 38449e8..3f017ca 100644 --- a/app/routes/course.py +++ b/app/routes/course.py @@ -175,7 +175,7 @@ async def get_student_courses( .join(course_model.Student, course_model.Enrollment.student_id == course_model.Student.id) .join(user_model.Teacher, user_model.Teacher.id == course_model.Course.teacher_id) .join(user_model.User, user_model.User.id == user_model.Teacher.user_id) - .filter(course_model.Enrollment.status == course_model.StatusEnum.approved) + .filter(and_(course_model.Enrollment.status == course_model.StatusEnum.approved,course_model.Enrollment.student_id==student.id)) .all() ) @@ -251,7 +251,7 @@ async def get_student_status_courses( .join(course_model.Student, course_model.Enrollment.student_id == course_model.Student.id) .join(user_model.Teacher, user_model.Teacher.id == course_model.Course.teacher_id) .join(user_model.User, user_model.User.id == user_model.Teacher.user_id) - .filter(or_(course_model.Enrollment.status == course_model.StatusEnum.pending, course_model.Enrollment.status == course_model.StatusEnum.declined)) + .filter(and_(or_(course_model.Enrollment.status == course_model.StatusEnum.pending, course_model.Enrollment.status == course_model.StatusEnum.declined),course_model.Enrollment.student_id==student.id)) .all() ) @@ -323,6 +323,12 @@ async def enroll( detail="No such active course found" ) + if course.taken_seats == course.total_seats: + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail="No seats available in the course" + ) + check_enrollment = ( db.query(course_model.Enrollment) .filter(and_(course_model.Enrollment.student_id==student.id, course_model.Enrollment.course_id==course_id)) @@ -379,11 +385,8 @@ async def enrollment_status( detail="User is not an admin" ) - - query = ( - db - .query(user_model.User.first_name, user_model.User.last_name, user_model.User.email, course_model.Course.dept, course_model.Course.code, course_model.Course.year, course_model.Course.term) + db.query(course_model.Enrollment.student_id, course_model.Enrollment.course_id, user_model.User.first_name, user_model.User.last_name, user_model.User.email, course_model.Course.dept, course_model.Course.code, course_model.Course.year, course_model.Course.term) .join(user_model.Student, user_model.User.id == user_model.Student.user_id) .join(course_model.Enrollment, user_model.Student.id == course_model.Enrollment.student_id) .join(course_model.Course, course_model.Course.id == course_model.Enrollment.course_id) @@ -392,4 +395,91 @@ async def enrollment_status( .all() ) - return query \ No newline at end of file + return query + + + +@router.patch( + "/enrollment_update/{dir}", + status_code=201, +) +async def enrollment_update( + dir: int, # dir: 0 approve, 1: reject + user: course_schema.CourseEnrollmentUpdate, + db: Session = Depends(get_db), +): + """ updates the enrollment status of an enrollment request """ + + admin = db.query(user_model.User).filter(and_(user_model.User.email == user.email,user_model.User.password == hash(user.password))).first() + + if not admin: + raise HTTPException( + status_code=status.HTTP_409_CONFLICT, + detail="Wrong email and password combination" + ) + + if not admin.verified: + raise HTTPException( + status_code=status.HTTP_417_EXPECTATION_FAILED, + detail="User is not verified" + ) + + if admin.role != user_model.RoleEnum.admin: + raise HTTPException( + status_code=status.HTTP_417_EXPECTATION_FAILED, + detail="User is not an admin" + ) + + + course = db.query(course_model.Course).filter(course_model.Course.id == user.course_id).first() + if not course: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="no such course found" + ) + + decision = None + + + if dir == 0: + decision = course_model.StatusEnum.approved + if course.total_seats == course.taken_seats: + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail="no seats available" + ) + elif dir == 1: + decision = course_model.StatusEnum.declined + else: + raise HTTPException( + status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, + detail="dir must be either 0 or 1" + ) + + query = ( + db + .query(course_model.Enrollment) + .filter(and_(course_model.Enrollment.student_id==user.student_id, course_model.Enrollment.course_id==user.course_id)) + .first() + ) + + if not query: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="no such enrollment request found" + ) + + if query.status == course_model.StatusEnum.approved and dir == 1: + course.taken_seats -= 1 + elif query.status != course_model.StatusEnum.approved and dir == 0: + course.taken_seats += 1 + + db.commit() + db.refresh(course) + + query.status = decision + query.comment = user.comment + db.commit() + db.refresh(query) + + return {"message": "enrollment request processed successfully"} \ No newline at end of file diff --git a/app/schemas/course_schema.py b/app/schemas/course_schema.py index 51222a9..9f00098 100644 --- a/app/schemas/course_schema.py +++ b/app/schemas/course_schema.py @@ -42,6 +42,8 @@ class Config: from_attributes = True class CourseEnrollmentView(BaseModel): + student_id: int + course_id: int first_name: str last_name: Optional[str] email: str @@ -50,5 +52,15 @@ class CourseEnrollmentView(BaseModel): year: int term: course_model.TermEnum + class Config: + from_attributes = True + +class CourseEnrollmentUpdate(BaseModel): + email: str + password: str + student_id: int + course_id: int + comment: Optional[str] + class Config: from_attributes = True \ No newline at end of file diff --git a/app/utils/utils.py b/app/utils/utils.py index 77628fe..88feea7 100644 --- a/app/utils/utils.py +++ b/app/utils/utils.py @@ -193,6 +193,7 @@ def populatedb(db: Session = Depends(get_db)): year=2024, credits=3, total_seats=100, + taken_seats=1, status=course_model.CourseStatusEnum.active, teacher_id = teacher_2.id ) @@ -205,6 +206,7 @@ def populatedb(db: Session = Depends(get_db)): year=2024, credits=3, total_seats=60, + taken_seats=1, status=course_model.CourseStatusEnum.active, teacher_id = teacher_2.id ) diff --git a/test/course/test_admin_decision_enrollment.py b/test/course/test_admin_decision_enrollment.py new file mode 100644 index 0000000..68ce053 --- /dev/null +++ b/test/course/test_admin_decision_enrollment.py @@ -0,0 +1,25 @@ +from test.test_fixtures import * + +def test_approve_enrollment(authorized_client, test_enrollment, test_verified_admin_1): + res = authorized_client.patch(f"/course/enrollment_update/0",json={'email': 'admin1@korse.com', 'password': 'password', 'student_id': test_enrollment[2].student_id, 'course_id': test_enrollment[2].course_id, 'comment': None}) + + assert res.status_code == 201 + assert test_enrollment[2].status == course_model.StatusEnum.approved + +def test_decline_enrollment(authorized_client, test_enrollment, test_verified_admin_1): + res = authorized_client.patch(f"/course/enrollment_update/1",json={'email': 'admin1@korse.com', 'password': 'password', 'student_id': test_enrollment[2].student_id, 'course_id': test_enrollment[2].course_id, 'comment': None}) + + assert res.status_code == 201 + assert test_enrollment[2].status == course_model.StatusEnum.declined + +def test_invalid_enrollment(authorized_client, test_enrollment, test_verified_admin_1): + res = authorized_client.patch(f"/course/enrollment_update/2",json={'email': 'admin1@korse.com', 'password': 'password', 'student_id': test_enrollment[2].student_id, 'course_id': test_enrollment[2].course_id, 'comment': None}) + + assert res.status_code == 422 + assert res.json()["detail"] == "dir must be either 0 or 1" + +def test_enrollment_for_non_exisitant_entry(authorized_client, test_enrollment, test_verified_admin_1): + res = authorized_client.patch(f"/course/enrollment_update/0",json={'email': 'admin1@korse.com', 'password': 'password', 'student_id': test_enrollment[2].student_id + 1, 'course_id': test_enrollment[2].course_id - 1, 'comment': None}) + + assert res.status_code == 404 + assert res.json()["detail"] == "no such enrollment request found" \ No newline at end of file