From a0a2a322090f16bddec4112e317516f02d76b8fc Mon Sep 17 00:00:00 2001 From: Ritwiksrivastava0809 Date: Wed, 18 Sep 2024 20:56:44 +0530 Subject: [PATCH 1/4] feat : added missing apis --- core/apis/assignments/__init__.py | 1 + core/apis/assignments/principal.py | 43 ++++++++++++++++++++++++++++++ core/apis/assignments/schema.py | 11 ++++++++ core/apis/assignments/student.py | 13 +++++---- core/apis/assignments/teacher.py | 2 +- core/models/assignments.py | 22 +++++++++++---- core/models/teachers.py | 4 +++ core/server.py | 5 ++-- 8 files changed, 88 insertions(+), 13 deletions(-) diff --git a/core/apis/assignments/__init__.py b/core/apis/assignments/__init__.py index fe4ee3c8d..54462f2df 100644 --- a/core/apis/assignments/__init__.py +++ b/core/apis/assignments/__init__.py @@ -1,2 +1,3 @@ from .student import student_assignments_resources from .teacher import teacher_assignments_resources +from .principal import principal_assignments_resources \ No newline at end of file diff --git a/core/apis/assignments/principal.py b/core/apis/assignments/principal.py index e69de29bb..df2ad0c71 100644 --- a/core/apis/assignments/principal.py +++ b/core/apis/assignments/principal.py @@ -0,0 +1,43 @@ +from flask import Blueprint +from core import db +from core.apis import decorators +from core.apis.responses import APIResponse +from core.models.assignments import Assignment +from core.models.teachers import Teacher + +from .schema import AssignmentSchema, AssignmentGradeSchema, TeacherSchema +principal_assignments_resources = Blueprint('principal_assignments_resources', __name__) + +@principal_assignments_resources.route('/assignments', methods=['GET'], strict_slashes=False) +@decorators.authenticate_principal +def get_assignments(p): + """Lists all submitted and graded assignments""" + all_submitted_and_graded_assignments = Assignment.get_all_submitted_and_graded_assignments() + all_submitted_and_graded_assignments_dump = AssignmentSchema().dump(all_submitted_and_graded_assignments, many=True) + print(all_submitted_and_graded_assignments_dump) + return APIResponse.respond(data=all_submitted_and_graded_assignments_dump) + + +@principal_assignments_resources.route('/teachers', methods=['GET'], strict_slashes=False) +@decorators.authenticate_principal +def get_all_teachers(p): + """Lists all the teachers""" + all_teachers = Teacher.get_all_teachers() + all_teachers_dump = TeacherSchema().dump(all_teachers, many=True) + return APIResponse.respond(data=all_teachers_dump) + +@principal_assignments_resources.route('/assignments/grade', methods=['POST'], strict_slashes=False) +@decorators.accept_payload +@decorators.authenticate_principal +def grade_or_regrade_assignments(p, incoming_payload): + """Grades or regrades an assignment by the principal""" + grade_or_regrade_assignment_payload = AssignmentGradeSchema().load(incoming_payload) + graded_or_regraded_assignment = Assignment.mark_grade( + _id=grade_or_regrade_assignment_payload.id, + grade=grade_or_regrade_assignment_payload.grade, + auth_principal=p, + ) + + db.session.commit() + graded_or_regraded_assignment_dump = AssignmentSchema().dump(graded_or_regraded_assignment) + return APIResponse.respond(data=graded_or_regraded_assignment_dump) \ No newline at end of file diff --git a/core/apis/assignments/schema.py b/core/apis/assignments/schema.py index d6f4c7daf..851c5d6ae 100644 --- a/core/apis/assignments/schema.py +++ b/core/apis/assignments/schema.py @@ -2,6 +2,7 @@ from marshmallow_sqlalchemy import SQLAlchemyAutoSchema, auto_field from marshmallow_enum import EnumField from core.models.assignments import Assignment, GradeEnum +from core.models.teachers import Teacher from core.libs.helpers import GeneralObject @@ -49,3 +50,13 @@ class Meta: def initiate_class(self, data_dict, many, partial): # pylint: disable=unused-argument,no-self-use return GeneralObject(**data_dict) + +class TeacherSchema(SQLAlchemyAutoSchema): + class Meta: + model = Teacher + unknown = EXCLUDE + + id = auto_field(required=False, allow_none=True) + user_id = auto_field(dump_only=True) + created_at = auto_field(dump_only=True) + updated_at = auto_field(dump_only=True) \ No newline at end of file diff --git a/core/apis/assignments/student.py b/core/apis/assignments/student.py index f3fb4f8c6..cd8a818e3 100644 --- a/core/apis/assignments/student.py +++ b/core/apis/assignments/student.py @@ -1,4 +1,4 @@ -from flask import Blueprint +from flask import Blueprint, jsonify from core import db from core.apis import decorators from core.apis.responses import APIResponse @@ -25,10 +25,13 @@ def upsert_assignment(p, incoming_payload): assignment = AssignmentSchema().load(incoming_payload) assignment.student_id = p.student_id - upserted_assignment = Assignment.upsert(assignment) - db.session.commit() - upserted_assignment_dump = AssignmentSchema().dump(upserted_assignment) - return APIResponse.respond(data=upserted_assignment_dump) + if assignment.content is not None: + upserted_assignment = Assignment.upsert(assignment) + db.session.commit() + upserted_assignment_dump = AssignmentSchema().dump(upserted_assignment) + return APIResponse.respond(data=upserted_assignment_dump) + else: + return jsonify(error='Content cannot be null'), 400 @student_assignments_resources.route('/assignments/submit', methods=['POST'], strict_slashes=False) diff --git a/core/apis/assignments/teacher.py b/core/apis/assignments/teacher.py index 20fcaaa40..c250e01c8 100644 --- a/core/apis/assignments/teacher.py +++ b/core/apis/assignments/teacher.py @@ -12,7 +12,7 @@ @decorators.authenticate_principal def list_assignments(p): """Returns list of assignments""" - teachers_assignments = Assignment.get_assignments_by_teacher() + teachers_assignments = Assignment.get_assignments_by_teacher(p.teacher_id) teachers_assignments_dump = AssignmentSchema().dump(teachers_assignments, many=True) return APIResponse.respond(data=teachers_assignments_dump) diff --git a/core/models/assignments.py b/core/models/assignments.py index 6a4d6cb5f..1e6425555 100644 --- a/core/models/assignments.py +++ b/core/models/assignments.py @@ -64,20 +64,26 @@ def submit(cls, _id, teacher_id, auth_principal: AuthPrincipal): assignment = Assignment.get_by_id(_id) assertions.assert_found(assignment, 'No assignment with this id was found') assertions.assert_valid(assignment.student_id == auth_principal.student_id, 'This assignment belongs to some other student') + assertions.assert_valid(assignment.state == AssignmentStateEnum.DRAFT, 'only a draft assignment can be submitted') assertions.assert_valid(assignment.content is not None, 'assignment with empty content cannot be submitted') assignment.teacher_id = teacher_id + assignment.state = AssignmentStateEnum.SUBMITTED db.session.flush() return assignment - @classmethod def mark_grade(cls, _id, grade, auth_principal: AuthPrincipal): assignment = Assignment.get_by_id(_id) assertions.assert_found(assignment, 'No assignment with this id was found') - assertions.assert_valid(grade is not None, 'assignment with empty grade cannot be graded') - + if auth_principal.teacher_id: + assertions.assert_valid(assignment.teacher_id == auth_principal.teacher_id, f'This assign was supposed to be evaluated by {assignment.teacher_id }') + assertions.assert_valid(grade is not None, 'assignment with empty grade cannot be graded') + assertions.assert_valid(assignment.state == AssignmentStateEnum.SUBMITTED, 'Only a submitted assignment can be graded') + elif auth_principal.principal_id: + assertions.assert_valid(assignment.state != AssignmentStateEnum.DRAFT, 'Only a submitted or already graded assignment can be graded') + assignment.grade = grade assignment.state = AssignmentStateEnum.GRADED db.session.flush() @@ -89,5 +95,11 @@ def get_assignments_by_student(cls, student_id): return cls.filter(cls.student_id == student_id).all() @classmethod - def get_assignments_by_teacher(cls): - return cls.query.all() + def get_assignments_by_teacher(cls, teacher_id): + return cls.filter(cls.teacher_id == teacher_id).all() + + @classmethod + def get_all_submitted_and_graded_assignments(cls): + return cls.filter(cls.state == AssignmentStateEnum.SUBMITTED, cls.state == AssignmentStateEnum.GRADED).all() + + \ No newline at end of file diff --git a/core/models/teachers.py b/core/models/teachers.py index 316deae82..ade88494e 100644 --- a/core/models/teachers.py +++ b/core/models/teachers.py @@ -11,3 +11,7 @@ class Teacher(db.Model): def __repr__(self): return '' % self.id + + @classmethod + def get_all_teachers(cls): + return db.session.query(cls).all() \ No newline at end of file diff --git a/core/server.py b/core/server.py index c24eb4826..6fa9dfc5f 100644 --- a/core/server.py +++ b/core/server.py @@ -1,7 +1,7 @@ from flask import jsonify from marshmallow.exceptions import ValidationError from core import app -from core.apis.assignments import student_assignments_resources, teacher_assignments_resources +from core.apis.assignments import student_assignments_resources, teacher_assignments_resources, principal_assignments_resources from core.libs import helpers from core.libs.exceptions import FyleError from werkzeug.exceptions import HTTPException @@ -10,6 +10,7 @@ app.register_blueprint(student_assignments_resources, url_prefix='/student') app.register_blueprint(teacher_assignments_resources, url_prefix='/teacher') +app.register_blueprint(principal_assignments_resources, url_prefix='/principal') @app.route('/') @@ -41,4 +42,4 @@ def handle_error(err): error=err.__class__.__name__, message=str(err) ), err.code - raise err + raise err \ No newline at end of file From 5f1f88466f49ecc236a6a39dc959c15311d9c28d Mon Sep 17 00:00:00 2001 From: Ritwiksrivastava0809 Date: Wed, 18 Sep 2024 20:59:00 +0530 Subject: [PATCH 2/4] feat : created sql queries --- ...rade_A_assignments_by_teacher_with_max_grading.sql | 11 +++++++++++ .../number_of_graded_assignments_for_each_student.sql | 4 ++++ 2 files changed, 15 insertions(+) diff --git a/tests/SQL/count_grade_A_assignments_by_teacher_with_max_grading.sql b/tests/SQL/count_grade_A_assignments_by_teacher_with_max_grading.sql index b6ab9396e..dacb63368 100644 --- a/tests/SQL/count_grade_A_assignments_by_teacher_with_max_grading.sql +++ b/tests/SQL/count_grade_A_assignments_by_teacher_with_max_grading.sql @@ -1 +1,12 @@ -- Write query to find the number of grade A's given by the teacher who has graded the most assignments +WITH teacher as ( + SELECT teacher_id, COUNT(*) AS a_grade_count + FROM assignments + WHERE grade IS NOT NULL AND teacher_id IS NOT NULL + GROUP BY teacher_id + ORDER BY a_grade_count DESC + LIMIT 1 +) +SELECT count(*) as a_grade_count +FROM teacher JOIN assignments using(teacher_id) +WHERE grade = 'A'; \ No newline at end of file diff --git a/tests/SQL/number_of_graded_assignments_for_each_student.sql b/tests/SQL/number_of_graded_assignments_for_each_student.sql index a62fd173e..d44bfa188 100644 --- a/tests/SQL/number_of_graded_assignments_for_each_student.sql +++ b/tests/SQL/number_of_graded_assignments_for_each_student.sql @@ -1 +1,5 @@ -- Write query to get number of graded assignments for each student: +SELECT student_id, count(*) +FROM assignments +WHERE state IS 'GRADED' +GROUP BY student_id \ No newline at end of file From b22426efa55bad610b9ce4809ec4b1ac3c4bc1a8 Mon Sep 17 00:00:00 2001 From: Ritwiksrivastava0809 Date: Wed, 18 Sep 2024 21:01:34 +0530 Subject: [PATCH 3/4] feat: added docker files --- .dockerignore | 8 ++++++++ docker-compose.yml | 16 ++++++++++++++++ dockerfile | 15 +++++++++++++++ tests/principals_test.py | 8 ++++++++ 4 files changed, 47 insertions(+) create mode 100644 .dockerignore create mode 100644 docker-compose.yml create mode 100644 dockerfile diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..b4eaea051 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,8 @@ +env +*.pyc +*.pyo +*.pyd +__pycache__ +.pytest_cache +*.db +*.sqlite3 \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 000000000..03525b3d6 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,16 @@ +version: "3.12" + +services: + web: + build: + context: . + dockerfile: Dockerfile + image: fyle:latest + ports: + - "7755:7755" + volumes: + - .:/app + environment: + - FLASK_ENV=development + - GUNICORN_PORT=7755 + command: ["gunicorn", "-c", "gunicorn_config.py", "core.server:app"] \ No newline at end of file diff --git a/dockerfile b/dockerfile new file mode 100644 index 000000000..724df8cc5 --- /dev/null +++ b/dockerfile @@ -0,0 +1,15 @@ +FROM python:3.8-slim + +WORKDIR /app + +COPY . /app + +RUN pip install --no-cache-dir -r requirements.txt + +ENV FLASK_APP=core/server.py + +RUN flask db upgrade -d core/migrations/ + +EXPOSE 7755 + +CMD ["bash", "run.sh"] \ No newline at end of file diff --git a/tests/principals_test.py b/tests/principals_test.py index da0bb1695..85a43861e 100644 --- a/tests/principals_test.py +++ b/tests/principals_test.py @@ -13,6 +13,14 @@ def test_get_assignments(client, h_principal): for assignment in data: assert assignment['state'] in [AssignmentStateEnum.SUBMITTED, AssignmentStateEnum.GRADED] +def test_get_teachers(client, h_principal): + response = client.get( + '/principal/teachers', + headers=h_principal + ) + + assert response.status_code == 200 + def test_grade_assignment_draft_assignment(client, h_principal): """ From 3383f80a6fd28eb621d664d0cc70cf3e403e133c Mon Sep 17 00:00:00 2001 From: Ritwiksrivastava0809 Date: Wed, 18 Sep 2024 21:07:42 +0530 Subject: [PATCH 4/4] fix : added extra test --- core/apis/assignments/principal.py | 11 +---- core/apis/assignments/schema.py | 12 +----- core/apis/teachers/__init__.py | 1 + core/apis/teachers/principal.py | 18 ++++++++ core/apis/teachers/schema.py | 14 +++++++ core/server.py | 2 + tests/assertions_test.py | 33 +++++++++++++++ tests/exceptions_test.py | 14 +++++++ tests/server_test.py | 10 +++++ tests/students_test.py | 66 ++++++++++++++++++++++++++++++ tests/teachers_test.py | 31 ++++++++++++-- 11 files changed, 188 insertions(+), 24 deletions(-) create mode 100644 tests/assertions_test.py create mode 100644 tests/exceptions_test.py create mode 100644 tests/server_test.py diff --git a/core/apis/assignments/principal.py b/core/apis/assignments/principal.py index df2ad0c71..f8119e4d8 100644 --- a/core/apis/assignments/principal.py +++ b/core/apis/assignments/principal.py @@ -3,9 +3,8 @@ from core.apis import decorators from core.apis.responses import APIResponse from core.models.assignments import Assignment -from core.models.teachers import Teacher -from .schema import AssignmentSchema, AssignmentGradeSchema, TeacherSchema +from .schema import AssignmentSchema, AssignmentGradeSchema principal_assignments_resources = Blueprint('principal_assignments_resources', __name__) @principal_assignments_resources.route('/assignments', methods=['GET'], strict_slashes=False) @@ -18,14 +17,6 @@ def get_assignments(p): return APIResponse.respond(data=all_submitted_and_graded_assignments_dump) -@principal_assignments_resources.route('/teachers', methods=['GET'], strict_slashes=False) -@decorators.authenticate_principal -def get_all_teachers(p): - """Lists all the teachers""" - all_teachers = Teacher.get_all_teachers() - all_teachers_dump = TeacherSchema().dump(all_teachers, many=True) - return APIResponse.respond(data=all_teachers_dump) - @principal_assignments_resources.route('/assignments/grade', methods=['POST'], strict_slashes=False) @decorators.accept_payload @decorators.authenticate_principal diff --git a/core/apis/assignments/schema.py b/core/apis/assignments/schema.py index 851c5d6ae..6224f6d05 100644 --- a/core/apis/assignments/schema.py +++ b/core/apis/assignments/schema.py @@ -49,14 +49,4 @@ class Meta: @post_load def initiate_class(self, data_dict, many, partial): # pylint: disable=unused-argument,no-self-use - return GeneralObject(**data_dict) - -class TeacherSchema(SQLAlchemyAutoSchema): - class Meta: - model = Teacher - unknown = EXCLUDE - - id = auto_field(required=False, allow_none=True) - user_id = auto_field(dump_only=True) - created_at = auto_field(dump_only=True) - updated_at = auto_field(dump_only=True) \ No newline at end of file + return GeneralObject(**data_dict) \ No newline at end of file diff --git a/core/apis/teachers/__init__.py b/core/apis/teachers/__init__.py index e69de29bb..1ee3e9b1e 100644 --- a/core/apis/teachers/__init__.py +++ b/core/apis/teachers/__init__.py @@ -0,0 +1 @@ +from .principal import principal_teachers_resources \ No newline at end of file diff --git a/core/apis/teachers/principal.py b/core/apis/teachers/principal.py index e69de29bb..e2daa1e15 100644 --- a/core/apis/teachers/principal.py +++ b/core/apis/teachers/principal.py @@ -0,0 +1,18 @@ +from flask import Blueprint +from core import db +from core.apis import decorators +from core.apis.responses import APIResponse +from core.models.teachers import Teacher + +from .schema import TeacherSchema +principal_teachers_resources = Blueprint('principal_teachers_resources', __name__) + + +@principal_teachers_resources.route('/teachers', methods=['GET'], strict_slashes=False) +@decorators.authenticate_principal +def list_teachers(p): + """Returns list of teachers""" + teachers_list = Teacher.get_all_teachers() + print(teachers_list[0],end=" ") + teachers_list_dump = TeacherSchema().dump(teachers_list, many=True) + return APIResponse.respond(data=teachers_list_dump) \ No newline at end of file diff --git a/core/apis/teachers/schema.py b/core/apis/teachers/schema.py index e69de29bb..59d0d3f49 100644 --- a/core/apis/teachers/schema.py +++ b/core/apis/teachers/schema.py @@ -0,0 +1,14 @@ +from marshmallow import Schema, EXCLUDE, fields, post_load +from marshmallow_sqlalchemy import SQLAlchemyAutoSchema, auto_field +from core.models.teachers import Teacher + + +class TeacherSchema(SQLAlchemyAutoSchema): + class Meta: + model = Teacher + unknown = EXCLUDE + + id = auto_field(required=False, allow_none=True) + created_at = auto_field(dump_only=True) + updated_at = auto_field(dump_only=True) + user_id = auto_field(dump_only=True) \ No newline at end of file diff --git a/core/server.py b/core/server.py index 6fa9dfc5f..699408151 100644 --- a/core/server.py +++ b/core/server.py @@ -2,6 +2,7 @@ from marshmallow.exceptions import ValidationError from core import app from core.apis.assignments import student_assignments_resources, teacher_assignments_resources, principal_assignments_resources +from core.apis.teachers import principal_teachers_resources from core.libs import helpers from core.libs.exceptions import FyleError from werkzeug.exceptions import HTTPException @@ -11,6 +12,7 @@ app.register_blueprint(student_assignments_resources, url_prefix='/student') app.register_blueprint(teacher_assignments_resources, url_prefix='/teacher') app.register_blueprint(principal_assignments_resources, url_prefix='/principal') +app.register_blueprint(principal_teachers_resources, url_prefix='/principal') @app.route('/') diff --git a/tests/assertions_test.py b/tests/assertions_test.py new file mode 100644 index 000000000..7d131987b --- /dev/null +++ b/tests/assertions_test.py @@ -0,0 +1,33 @@ +from core.libs import assertions +from core.libs.exceptions import FyleError + + +def test_assert_auth(): + try: + assertions.assert_auth(False) + except FyleError as e: + assert e.status_code == 401 + assert e.message == 'UNAUTHORIZED' + + +def test_assert_true(): + try: + assertions.assert_true(False) + except FyleError as e: + assert e.status_code == 403 + assert e.message == 'FORBIDDEN' + + +def test_assert_valid(): + try: + assertions.assert_valid(False) + except FyleError as e: + assert e.status_code == 400 + + +def test_assert_found(): + try: + assertions.assert_found(None) + except FyleError as e: + assert e.status_code == 404 + assert e.message == 'NOT_FOUND' \ No newline at end of file diff --git a/tests/exceptions_test.py b/tests/exceptions_test.py new file mode 100644 index 000000000..460a44e61 --- /dev/null +++ b/tests/exceptions_test.py @@ -0,0 +1,14 @@ +from core.libs.exceptions import FyleError + + +def test_fyle_error(): + error = FyleError(404, 'page not found') + assert error.status_code == 404 + assert error.message == 'page not found' + + +def test_fyle_error_to_dict(): + error = FyleError(404, 'page not found') + error_dict = error.to_dict() + assert isinstance(error_dict, dict) + assert error_dict['message'] == 'page not found' \ No newline at end of file diff --git a/tests/server_test.py b/tests/server_test.py new file mode 100644 index 000000000..2193e6f82 --- /dev/null +++ b/tests/server_test.py @@ -0,0 +1,10 @@ +def test_index(client): + response = client.get("/") + assert response.status_code == 200 + assert response.json["status"] == "ready" + + +def test_invalid_endpoint(client, h_principal): + response = client.get("/other", headers=h_principal) + assert response.status_code == 404 + assert response.json["error"] == "NotFound" \ No newline at end of file diff --git a/tests/students_test.py b/tests/students_test.py index 2a1fa708d..a0f11eb18 100644 --- a/tests/students_test.py +++ b/tests/students_test.py @@ -86,3 +86,69 @@ def test_assignment_resubmit_error(client, h_student_1): assert response.status_code == 400 assert error_response['error'] == 'FyleError' assert error_response["message"] == 'only a draft assignment can be submitted' + + +def test_post_assignment_student_1_my(client, h_student_1): + content = 'MY TEST' + + response = client.post( + '/student/assignments', + headers=h_student_1, + json={ + 'content': content + }) + + assert response.status_code == 200 + data = response.json['data'] + assert data['content'] == content + assert data['state'] == 'DRAFT' + assert data['teacher_id'] is None + + +def test_post_assignment_student_1_update_my(client, h_student_1): + content = 'MY TEST UPDATED' + + response = client.post( + '/student/assignments', + headers=h_student_1, + json={ + 'id': 7, + 'content': content + }) + + try: + assert response.status_code == 200 + data = response.json['data'] + assert data['content'] == content + assert data['state'] == 'DRAFT' + assert data['teacher_id'] is None + except AssertionError: + assert response.status_code == 400 + data = response.json + assert data['error'] == 'FyleError' + assert data['message'] == 'only assignment in draft state can be edited' + + +def test_submit_assignment_student_1_my(client, h_student_1): + """ + can be failure case: only a draft assignment can be submitted ( an assignment can't be submitted more than once. ) + """ + response = client.post( + '/student/assignments/submit', + headers=h_student_1, + json={ + 'id': 7, + 'teacher_id': 1 + }) + + try: + assert response.status_code == 200 + data = response.json['data'] + assert data['student_id'] == 1 + assert data['state'] == 'SUBMITTED' + assert data['teacher_id'] == 1 + except AssertionError: + assert response.status_code == 400 + data = response.json + assert data['error'] == 'FyleError' + assert data['message'] == 'only a draft assignment can be submitted' \ No newline at end of file diff --git a/tests/teachers_test.py b/tests/teachers_test.py index 8b5c818c5..ffb4c37e1 100644 --- a/tests/teachers_test.py +++ b/tests/teachers_test.py @@ -1,3 +1,5 @@ +from core.models.assignments import GradeEnum, AssignmentStateEnum + def test_get_assignments_teacher_1(client, h_teacher_1): response = client.get( '/teacher/assignments', @@ -34,7 +36,7 @@ def test_grade_assignment_cross(client, h_teacher_2): headers=h_teacher_2, json={ "id": 1, - "grade": "A" + "grade": GradeEnum.A.value } ) @@ -72,7 +74,7 @@ def test_grade_assignment_bad_assignment(client, h_teacher_1): headers=h_teacher_1, json={ "id": 100000, - "grade": "A" + "grade": GradeEnum.A.value } ) @@ -91,7 +93,7 @@ def test_grade_assignment_draft_assignment(client, h_teacher_1): headers=h_teacher_1 , json={ "id": 2, - "grade": "A" + "grade": GradeEnum.A.value } ) @@ -99,3 +101,26 @@ def test_grade_assignment_draft_assignment(client, h_teacher_1): data = response.json assert data['error'] == 'FyleError' + +def test_grade_assignment_by_teacher_my(client, h_teacher_1): + """ + can be failure case: an assignment can't be graded more than once by a teacher + """ + response = client.post( + '/teacher/assignments/grade', + headers=h_teacher_1, + json={ + "id": 7, + "grade": GradeEnum.A.value + } + ) + + try: + assert response.status_code == 200 + assert response.json['data']['state'] == AssignmentStateEnum.GRADED.value + assert response.json['data']['grade'] == GradeEnum.A + except AssertionError: + assert response.status_code == 400 + data = response.json + assert data['error'] == 'FyleError' + assert data['message'] == 'Assignment is already graded' \ No newline at end of file