From 89da416e9b066d8516ec765e199a12fb4d16b6e4 Mon Sep 17 00:00:00 2001 From: sumanth-fyle <99784932+sumanth-fyle@users.noreply.github.com> Date: Tue, 17 May 2022 12:51:26 +0530 Subject: [PATCH 01/22] Intern task (#3) * WIP * WIP --- README.md | 3 ++- core/models/assignments.py | 2 -- requirements.txt | 3 +-- tests/students_test.py | 14 ++++++++++++++ 4 files changed, 17 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index c7522f41b..5641c4ea5 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,8 @@ Described [here](./Application.md) ### Your tasks 1. Add missing APIs mentioned [here](./Application.md#Missing-APIs) and get the automated tests to pass 2. Add a test for grading API -3. Get the test coverage to 94% or above +3. All tests should pass +4. Get the test coverage to 94% or above ## Submission diff --git a/core/models/assignments.py b/core/models/assignments.py index 783222bb0..1253cb85d 100644 --- a/core/models/assignments.py +++ b/core/models/assignments.py @@ -64,8 +64,6 @@ def submit(cls, _id, teacher_id, principal: Principal): assignment = Assignment.get_by_id(_id) assertions.assert_found(assignment, 'No assignment with this id was found') assertions.assert_valid(assignment.student_id == 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 diff --git a/requirements.txt b/requirements.txt index faf33a829..980640973 100644 --- a/requirements.txt +++ b/requirements.txt @@ -20,7 +20,6 @@ marshmallow-enum==1.5.1 marshmallow-sqlalchemy==0.26.1 packaging==21.0 pluggy==1.0.0 -psycopg2-binary==2.8.6 py==1.10.0 pyparsing==2.4.7 pytest==6.2.5 @@ -30,4 +29,4 @@ toml==0.10.2 Werkzeug==2.0.1 zipp==3.5.0 zope.event==4.5.0 -zope.interface==5.4.0 +zope.interface==5.4.0 \ No newline at end of file diff --git a/tests/students_test.py b/tests/students_test.py index 612c20857..323d16235 100644 --- a/tests/students_test.py +++ b/tests/students_test.py @@ -57,3 +57,17 @@ def test_submit_assignment_student_1(client, h_student_1): assert data['student_id'] == 1 assert data['state'] == 'SUBMITTED' assert data['teacher_id'] == 2 + + +def test_assingment_resubmitt_error(client, h_student_1): + response = client.post( + '/student/assignments/submit', + headers=h_student_1, + json={ + 'id': 2, + 'teacher_id': 2 + }) + error_response = response.json + assert response.status_code == 400 + assert error_response['error'] == 'FyleError' + assert error_response["message"] == 'only a draft assignment can be submitted' From f22150f6a48a6ba47013e47c9b67d62029d0f4d9 Mon Sep 17 00:00:00 2001 From: Siva Narayanan Date: Fri, 16 Dec 2022 10:21:09 +0530 Subject: [PATCH 02/22] Update README.md --- README.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/README.md b/README.md index 5641c4ea5..5f7bb1c49 100644 --- a/README.md +++ b/README.md @@ -30,11 +30,7 @@ Once you are done with your task, please use [this form](https://forms.gle/fZex7 ## What happens next? -You will hear back within 48 hours from us via email. We may request for some changes based on reviewing your code. - -Subsequently, we will schedule a phone interview with a Fyle Engineer. - -If that goes well, we'll make an offer. +You will hear back within 48 hours from us via email. --- From 14f11de16446762eda661cd7c8f12bbfe05abc3c Mon Sep 17 00:00:00 2001 From: Siva Narayanan Date: Fri, 16 Dec 2022 10:25:10 +0530 Subject: [PATCH 03/22] Update Application.md --- Application.md | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/Application.md b/Application.md index 80dab0ccf..e18eb702d 100644 --- a/Application.md +++ b/Application.md @@ -1,5 +1,6 @@ ## Application -### There are 4 resources: + +There are 4 resources: - Users - Students - Teachers @@ -7,14 +8,22 @@ 4 Users (2 students and 2 teachers) have already been created for you in the db fixture -## User Actions: - A student can create and edit a draft assignment - A student can list all his created assignments - A student can submit a draft assignment to a teacher - A teacher can list all assignments submitted to him - A teacher can grade an assignment submitted to him +## Challenge + +Your tasks +- Add missing APIs mentioned here and get the automated tests to pass +- Add a test for grading API +- All tests should pass +- Get the test coverage to 94% or above + ## Available APIs + ### Auth - header: "X-Principal" - value: {"user_id":1, "student_id":1} @@ -22,7 +31,8 @@ For APIs to work you need a principal header to establish identity and context ### GET /student/assignments -#### List all assignments created by a student + +List all assignments created by a student ``` headers: X-Principal: {"user_id":1, "student_id":1} @@ -55,7 +65,8 @@ response: ``` ### POST /student/assignments -#### Create an assignment + +Create an assignment ``` headers: X-Principal: {"user_id":2, "student_id":2} @@ -81,7 +92,8 @@ response: ``` ### POST /student/assignments -#### Edit an assignment + +Edit an assignment ``` headers: X-Principal: {"user_id":2, "student_id":2} @@ -108,7 +120,8 @@ response: ``` ### POST /student/assignments/submit -#### Submit an assignment + +Submit an assignment ``` headers: X-Principal: {"user_id":1, "student_id":1} @@ -135,14 +148,12 @@ response: ``` ## Missing APIs -### Auth -- header: "X-Principal" -- value: {"user_id":3, "teacher_id":1} -For APIs to work you need a principal header to establish identity and context +You'll need to implement these APIs ### GET /teacher/assignments -#### List all assignments submitted to this teacher + +List all assignments submitted to this teacher ``` headers: X-Principal: {"user_id":3, "teacher_id":1} @@ -165,7 +176,8 @@ response: ``` ### POST /teacher/assignments/grade -#### Grade an assignment + +Grade an assignment ``` headers: X-Principal: {"user_id":3, "teacher_id":1} @@ -189,4 +201,4 @@ response: "updated_at": "2021-09-17T03:20:42.896947" } } -``` \ No newline at end of file +``` From b98ec14fedc8c7c1582bebecb69f3c570f20c732 Mon Sep 17 00:00:00 2001 From: Siva Narayanan Date: Fri, 16 Dec 2022 10:32:25 +0530 Subject: [PATCH 04/22] Update Application.md --- Application.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Application.md b/Application.md index e18eb702d..101ca28b1 100644 --- a/Application.md +++ b/Application.md @@ -22,6 +22,11 @@ Your tasks - All tests should pass - Get the test coverage to 94% or above +Once you are done with your task, please use [this form](https://forms.gle/fZex7LDo6kj1Syg7A) to complete your submission. + +You will hear back within 48 hours from us via email. + + ## Available APIs ### Auth From 6e61bf92b8a758450d98a7004a17561b9fa653ec Mon Sep 17 00:00:00 2001 From: Siva Narayanan Date: Fri, 16 Dec 2022 10:42:46 +0530 Subject: [PATCH 05/22] Update README.md --- README.md | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 5f7bb1c49..92cc99db6 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,7 @@ ## Who is this for? -This challenge is meant for candidates who wish to work / intern at Fyle and work with our engineering teams. - -If it is for internship, you should be able to commit to at least 3 months. +This challenge is meant for candidates who wish intern at Fyle and work with our engineering team. You should be able to commit to at least 3 months of dedicated time for internship. ## Why work at Fyle? @@ -15,46 +13,40 @@ We are an extremely transparent organization. Check out our [careers page](https ## Challenge outline -This is a web application designed in a context of a single classroom. -Described [here](./Application.md) - -### Your tasks -1. Add missing APIs mentioned [here](./Application.md#Missing-APIs) and get the automated tests to pass -2. Add a test for grading API -3. All tests should pass -4. Get the test coverage to 94% or above +This challenge involves writing a backend service for a classroom. The challenge is described in detail [here](./Application.md) -## Submission - -Once you are done with your task, please use [this form](https://forms.gle/fZex7LDo6kj1Syg7A) to complete your submission. ## What happens next? You will hear back within 48 hours from us via email. ---- ## Installation + 1. Fork this repository to your github account 2. Clone the forked repository and proceed with steps mentioned below ### Install requirements + ``` virtualenv env --python=python3.8 source env/bin/activate pip install -r requirements.txt ``` ### Reset DB + ``` export FLASK_APP=core/server.py rm core/store.sqlite3 flask db upgrade -d core/migrations/ ``` ### Start Server + ``` bash run.sh ``` ### Run Tests + ``` pytest -vvv -s tests/ From b45a95ce28ef3b19cbcab3d66566bbd75d1d35b8 Mon Sep 17 00:00:00 2001 From: Siva Narayanan Date: Fri, 16 Dec 2022 10:51:12 +0530 Subject: [PATCH 06/22] Update Application.md --- Application.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Application.md b/Application.md index 101ca28b1..adb070e9e 100644 --- a/Application.md +++ b/Application.md @@ -22,7 +22,7 @@ Your tasks - All tests should pass - Get the test coverage to 94% or above -Once you are done with your task, please use [this form](https://forms.gle/fZex7LDo6kj1Syg7A) to complete your submission. +Once you are done with your task, please use [this form](https://forms.gle/PinUUYFTKQo3BCTe9) to complete your submission. You will hear back within 48 hours from us via email. From 4bc8cf3dda1e154a688300293be41b1a3ec4be19 Mon Sep 17 00:00:00 2001 From: kartikey rajvaidya Date: Fri, 31 Mar 2023 18:54:06 +0530 Subject: [PATCH 07/22] added --- Application.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Application.md b/Application.md index adb070e9e..bca43dd2a 100644 --- a/Application.md +++ b/Application.md @@ -22,7 +22,7 @@ Your tasks - All tests should pass - Get the test coverage to 94% or above -Once you are done with your task, please use [this form](https://forms.gle/PinUUYFTKQo3BCTe9) to complete your submission. +Once you are done with your task, please use [this form](https://forms.gle/HJQNhoBWMG5xWHGY8) to complete your submission. You will hear back within 48 hours from us via email. From 1f0b2253e100afee7e301422a9c7774291e9c4be Mon Sep 17 00:00:00 2001 From: kartikey rajvaidya Date: Sat, 1 Apr 2023 02:03:57 +0530 Subject: [PATCH 08/22] added --- Application.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Application.md b/Application.md index bca43dd2a..162b1731b 100644 --- a/Application.md +++ b/Application.md @@ -22,7 +22,7 @@ Your tasks - All tests should pass - Get the test coverage to 94% or above -Once you are done with your task, please use [this form](https://forms.gle/HJQNhoBWMG5xWHGY8) to complete your submission. +Once you are done with your task, please use [this form](https://forms.gle/nWVJe1kLPgNmpVoM8) to complete your submission. You will hear back within 48 hours from us via email. From 326af6b8dc9d4b381e10bd9563c9485bfad19dfe Mon Sep 17 00:00:00 2001 From: Vikas Prasad Date: Tue, 6 Jun 2023 11:42:18 +0530 Subject: [PATCH 09/22] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 92cc99db6..87b3a3ca6 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ## Who is this for? -This challenge is meant for candidates who wish intern at Fyle and work with our engineering team. You should be able to commit to at least 3 months of dedicated time for internship. +This challenge is meant for candidates who wish to intern at Fyle and work with our engineering team. You should be able to commit to at least 3 months of dedicated time for internship. ## Why work at Fyle? From 8835e4c3a51bfc7cf0c1ebe5e2b1933f173780a5 Mon Sep 17 00:00:00 2001 From: Vikas Prasad Date: Tue, 6 Jun 2023 11:54:36 +0530 Subject: [PATCH 10/22] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 87b3a3ca6..6a48eb670 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ## Who is this for? -This challenge is meant for candidates who wish to intern at Fyle and work with our engineering team. You should be able to commit to at least 3 months of dedicated time for internship. +This challenge is meant for candidates who wish to intern at Fyle and work with our engineering team. You should be able to commit to at least 6 months of dedicated time for internship. ## Why work at Fyle? From 766dbfdb109eb1cd27020db9b4bd4c8a876ed442 Mon Sep 17 00:00:00 2001 From: Vikas Prasad Date: Thu, 22 Jun 2023 13:11:56 +0530 Subject: [PATCH 11/22] fix typo --- tests/students_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/students_test.py b/tests/students_test.py index 323d16235..8f91a7de2 100644 --- a/tests/students_test.py +++ b/tests/students_test.py @@ -59,7 +59,7 @@ def test_submit_assignment_student_1(client, h_student_1): assert data['teacher_id'] == 2 -def test_assingment_resubmitt_error(client, h_student_1): +def test_assignment_resubmit_error(client, h_student_1): response = client.post( '/student/assignments/submit', headers=h_student_1, From e97b4d106709240a8f7be1bd8677aa064a782979 Mon Sep 17 00:00:00 2001 From: Vikas Prasad Date: Thu, 22 Jun 2023 13:43:04 +0530 Subject: [PATCH 12/22] fix typo --- tests/teachers_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/teachers_test.py b/tests/teachers_test.py index 0f0fd38aa..097bd790f 100644 --- a/tests/teachers_test.py +++ b/tests/teachers_test.py @@ -47,7 +47,7 @@ def test_grade_assignment_cross(client, h_teacher_2): def test_grade_assignment_bad_grade(client, h_teacher_1): """ - failure case: API should not allow only grades available in enum + failure case: API should allow only grades available in enum """ response = client.post( '/teacher/assignments/grade', From d066d7d266c36dd50801ebf44cf1a49afd7bc4e1 Mon Sep 17 00:00:00 2001 From: Kirti Gautam <53394284+KirtiGautam@users.noreply.github.com> Date: Tue, 9 Jan 2024 20:43:01 +0530 Subject: [PATCH 13/22] feat: New code challenge and add SQL tests (#14) --- Application.md | 95 ++++++++++++++-- core/apis/assignments/__init__.py | 1 + core/apis/assignments/principal.py | 0 core/apis/assignments/schema.py | 15 ++- core/apis/assignments/student.py | 8 +- core/apis/assignments/teacher.py | 34 ++++++ core/apis/decorators.py | 14 ++- core/apis/teachers/__init__.py | 0 core/apis/teachers/principal.py | 0 core/apis/teachers/schema.py | 0 .../versions/2087a1db8595_assignments.py | 8 +- .../versions/52a401750a76_principals.py | 46 ++++++++ core/models/assignments.py | 24 +++- core/models/principals.py | 13 +++ core/server.py | 3 +- ...ssignments_by_teacher_with_max_grading.sql | 1 + tests/SQL/number_of_assignments_per_state.sql | 1 + tests/SQL/sql_test.py | 107 ++++++++++++++++++ tests/conftest.py | 12 ++ tests/principals_test.py | 62 ++++++++++ tests/students_test.py | 15 +++ tests/teachers_test.py | 4 +- 22 files changed, 435 insertions(+), 28 deletions(-) create mode 100644 core/apis/assignments/principal.py create mode 100644 core/apis/teachers/__init__.py create mode 100644 core/apis/teachers/principal.py create mode 100644 core/apis/teachers/schema.py create mode 100644 core/migrations/versions/52a401750a76_principals.py create mode 100644 core/models/principals.py create mode 100644 tests/SQL/count_grade_A_assignments_by_teacher_with_max_grading.sql create mode 100644 tests/SQL/number_of_assignments_per_state.sql create mode 100644 tests/SQL/sql_test.py create mode 100644 tests/principals_test.py diff --git a/Application.md b/Application.md index 162b1731b..fa49e4482 100644 --- a/Application.md +++ b/Application.md @@ -2,12 +2,16 @@ There are 4 resources: - Users +- Principal - Students - Teachers - Assignments -4 Users (2 students and 2 teachers) have already been created for you in the db fixture +5 Users (1 Principal, 2 students and 2 teachers) have already been created for you in the db fixture +- A principal can view all the teachers +- A principal can view all the assignments submitted and/or graded by teachers. +- A principal can re-grade the assignments already graded by the teacher. - A student can create and edit a draft assignment - A student can list all his created assignments - A student can submit a draft assignment to a teacher @@ -18,9 +22,13 @@ There are 4 resources: Your tasks - Add missing APIs mentioned here and get the automated tests to pass -- Add a test for grading API +- Add tests for grading API - All tests should pass - Get the test coverage to 94% or above +- There are certain SQL tests present inside `tests/SQL/`. You have to write SQL in following files: + - count_grade_A_assignments_by_teacher_with_max_grading.sql + - number_of_assignments_per_state.sql +- Optionally, Dockerize your application by creating a Dockerfile and a docker-compose.yml file, providing clear documentation on building and running the application with Docker, to stand out in your submission Once you are done with your task, please use [this form](https://forms.gle/nWVJe1kLPgNmpVoM8) to complete your submission. @@ -150,12 +158,8 @@ response: "updated_at": "2021-09-17T03:17:20.147349" } } -``` - -## Missing APIs - -You'll need to implement these APIs +``` ### GET /teacher/assignments List all assignments submitted to this teacher @@ -207,3 +211,80 @@ response: } } ``` + +## Missing APIs + +You'll need to implement these APIs + +### GET /principal/assignments + +List all assignments submitted to this teacher +``` +headers: +X-Principal: {"user_id":5, "principal_id":1} + +response: +{ + "data": [ + { + "content": "ESSAY T1", + "created_at": "2021-09-17T03:14:01.580126", + "grade": null, + "id": 1, + "state": "SUBMITTED", + "student_id": 1, + "teacher_id": 1, + "updated_at": "2021-09-17T03:14:01.584644" + } + ] +} +``` + +### GET /principal/teachers + +Get all the teachers +``` +headers: +X-Principal: {"user_id":5, "principal_id":1} + + +response: +{ + "data": [ + { + "created_at": "2024-01-08T07:58:53.131970", + "id": 1, + "updated_at": "2024-01-08T07:58:53.131972", + "user_id": 3 + } + ] +} +``` + +### POST /principal/assignments/grade + +Grade or re-grade an assignment +``` +headers: +X-Principal: {"user_id":5, "principal_id":1} + +payload: +{ + "id": 1, + "grade": "A" +} + +response: +{ + "data": { + "content": "ESSAY T1", + "created_at": "2021-09-17T03:14:01.580126", + "grade": "A", + "id": 1, + "state": "GRADED", + "student_id": 1, + "teacher_id": 1, + "updated_at": "2021-09-17T03:20:42.896947" + } +} +``` diff --git a/core/apis/assignments/__init__.py b/core/apis/assignments/__init__.py index e6541679b..fe4ee3c8d 100644 --- a/core/apis/assignments/__init__.py +++ b/core/apis/assignments/__init__.py @@ -1 +1,2 @@ from .student import student_assignments_resources +from .teacher import teacher_assignments_resources diff --git a/core/apis/assignments/principal.py b/core/apis/assignments/principal.py new file mode 100644 index 000000000..e69de29bb diff --git a/core/apis/assignments/schema.py b/core/apis/assignments/schema.py index b5262bc62..d6f4c7daf 100644 --- a/core/apis/assignments/schema.py +++ b/core/apis/assignments/schema.py @@ -11,7 +11,7 @@ class Meta: unknown = EXCLUDE id = auto_field(required=False, allow_none=True) - content = auto_field(required=True) + content = auto_field() created_at = auto_field(dump_only=True) updated_at = auto_field(dump_only=True) teacher_id = auto_field(dump_only=True) @@ -36,3 +36,16 @@ class Meta: def initiate_class(self, data_dict, many, partial): # pylint: disable=unused-argument,no-self-use return GeneralObject(**data_dict) + + +class AssignmentGradeSchema(Schema): + class Meta: + unknown = EXCLUDE + + id = fields.Integer(required=True, allow_none=False) + grade = EnumField(GradeEnum, required=True, allow_none=False) + + @post_load + def initiate_class(self, data_dict, many, partial): + # pylint: disable=unused-argument,no-self-use + return GeneralObject(**data_dict) diff --git a/core/apis/assignments/student.py b/core/apis/assignments/student.py index 9ce55dc7c..f3fb4f8c6 100644 --- a/core/apis/assignments/student.py +++ b/core/apis/assignments/student.py @@ -9,7 +9,7 @@ @student_assignments_resources.route('/assignments', methods=['GET'], strict_slashes=False) -@decorators.auth_principal +@decorators.authenticate_principal def list_assignments(p): """Returns list of assignments""" students_assignments = Assignment.get_assignments_by_student(p.student_id) @@ -19,7 +19,7 @@ def list_assignments(p): @student_assignments_resources.route('/assignments', methods=['POST'], strict_slashes=False) @decorators.accept_payload -@decorators.auth_principal +@decorators.authenticate_principal def upsert_assignment(p, incoming_payload): """Create or Edit an assignment""" assignment = AssignmentSchema().load(incoming_payload) @@ -33,7 +33,7 @@ def upsert_assignment(p, incoming_payload): @student_assignments_resources.route('/assignments/submit', methods=['POST'], strict_slashes=False) @decorators.accept_payload -@decorators.auth_principal +@decorators.authenticate_principal def submit_assignment(p, incoming_payload): """Submit an assignment""" submit_assignment_payload = AssignmentSubmitSchema().load(incoming_payload) @@ -41,7 +41,7 @@ def submit_assignment(p, incoming_payload): submitted_assignment = Assignment.submit( _id=submit_assignment_payload.id, teacher_id=submit_assignment_payload.teacher_id, - principal=p + auth_principal=p ) db.session.commit() submitted_assignment_dump = AssignmentSchema().dump(submitted_assignment) diff --git a/core/apis/assignments/teacher.py b/core/apis/assignments/teacher.py index e69de29bb..c250e01c8 100644 --- a/core/apis/assignments/teacher.py +++ b/core/apis/assignments/teacher.py @@ -0,0 +1,34 @@ +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 .schema import AssignmentSchema, AssignmentGradeSchema +teacher_assignments_resources = Blueprint('teacher_assignments_resources', __name__) + + +@teacher_assignments_resources.route('/assignments', methods=['GET'], strict_slashes=False) +@decorators.authenticate_principal +def list_assignments(p): + """Returns list of assignments""" + 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) + + +@teacher_assignments_resources.route('/assignments/grade', methods=['POST'], strict_slashes=False) +@decorators.accept_payload +@decorators.authenticate_principal +def grade_assignment(p, incoming_payload): + """Grade an assignment""" + grade_assignment_payload = AssignmentGradeSchema().load(incoming_payload) + + graded_assignment = Assignment.mark_grade( + _id=grade_assignment_payload.id, + grade=grade_assignment_payload.grade, + auth_principal=p + ) + db.session.commit() + graded_assignment_dump = AssignmentSchema().dump(graded_assignment) + return APIResponse.respond(data=graded_assignment_dump) diff --git a/core/apis/decorators.py b/core/apis/decorators.py index 0534ea209..8b3431d40 100644 --- a/core/apis/decorators.py +++ b/core/apis/decorators.py @@ -4,11 +4,12 @@ from functools import wraps -class Principal: - def __init__(self, user_id, student_id=None, teacher_id=None): +class AuthPrincipal: + def __init__(self, user_id, student_id=None, teacher_id=None, principal_id=None): self.user_id = user_id self.student_id = student_id self.teacher_id = teacher_id + self.principal_id = principal_id def accept_payload(func): @@ -19,22 +20,25 @@ def wrapper(*args, **kwargs): return wrapper -def auth_principal(func): +def authenticate_principal(func): @wraps(func) def wrapper(*args, **kwargs): p_str = request.headers.get('X-Principal') assertions.assert_auth(p_str is not None, 'principal not found') p_dict = json.loads(p_str) - p = Principal( + p = AuthPrincipal( user_id=p_dict['user_id'], student_id=p_dict.get('student_id'), - teacher_id=p_dict.get('teacher_id') + teacher_id=p_dict.get('teacher_id'), + principal_id=p_dict.get('principal_id') ) if request.path.startswith('/student'): assertions.assert_true(p.student_id is not None, 'requester should be a student') elif request.path.startswith('/teacher'): assertions.assert_true(p.teacher_id is not None, 'requester should be a teacher') + elif request.path.startswith('/principal'): + assertions.assert_true(p.principal_id is not None, 'requester should be a principal') else: assertions.assert_found(None, 'No such api') diff --git a/core/apis/teachers/__init__.py b/core/apis/teachers/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/core/apis/teachers/principal.py b/core/apis/teachers/principal.py new file mode 100644 index 000000000..e69de29bb diff --git a/core/apis/teachers/schema.py b/core/apis/teachers/schema.py new file mode 100644 index 000000000..e69de29bb diff --git a/core/migrations/versions/2087a1db8595_assignments.py b/core/migrations/versions/2087a1db8595_assignments.py index e6aefa10b..d0f529acc 100644 --- a/core/migrations/versions/2087a1db8595_assignments.py +++ b/core/migrations/versions/2087a1db8595_assignments.py @@ -8,7 +8,7 @@ from alembic import op import sqlalchemy as sa from core import db -from core.apis.decorators import Principal +from core.apis.decorators import AuthPrincipal from core.models.users import User from core.models.students import Student from core.models.teachers import Teacher @@ -82,19 +82,19 @@ def upgrade(): Assignment.submit( _id=assignment_1.id, teacher_id=teacher_1.id, - principal=Principal(user_id=student_1.user_id, student_id=student_1.id) + auth_principal=AuthPrincipal(user_id=student_1.user_id, student_id=student_1.id) ) Assignment.submit( _id=assignment_3.id, teacher_id=teacher_2.id, - principal=Principal(user_id=student_2.user_id, student_id=student_2.id) + auth_principal=AuthPrincipal(user_id=student_2.user_id, student_id=student_2.id) ) Assignment.submit( _id=assignment_4.id, teacher_id=teacher_2.id, - principal=Principal(user_id=student_2.user_id, student_id=student_2.id) + auth_principal=AuthPrincipal(user_id=student_2.user_id, student_id=student_2.id) ) db.session.commit() diff --git a/core/migrations/versions/52a401750a76_principals.py b/core/migrations/versions/52a401750a76_principals.py new file mode 100644 index 000000000..1dd6a139d --- /dev/null +++ b/core/migrations/versions/52a401750a76_principals.py @@ -0,0 +1,46 @@ +"""principals + +Revision ID: 52a401750a76 +Revises: 2087a1db8595 +Create Date: 2024-01-07 19:15:22.771993 + +""" +from alembic import op +import sqlalchemy as sa + +from core import db +from core.models.users import User +from core.models.principals import Principal + + +# revision identifiers, used by Alembic. +revision = '52a401750a76' +down_revision = '2087a1db8595' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('principals', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('user_id', sa.Integer(), nullable=True), + sa.Column('created_at', sa.TIMESTAMP(timezone=True), nullable=False), + sa.Column('updated_at', sa.TIMESTAMP(timezone=True), nullable=False), + sa.ForeignKeyConstraint(['user_id'], ['users.id'], ), + sa.PrimaryKeyConstraint('id') + ) + principal_user = User(email='principal@fylebe.com', username='principal') + db.session.add(principal_user) + + principal = Principal(user_id=User.get_by_email('principal@fylebe.com').id) + + db.session.add(principal) + db.session.commit() + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('principals') + # ### end Alembic commands ### diff --git a/core/models/assignments.py b/core/models/assignments.py index 1253cb85d..e7a5bdcf8 100644 --- a/core/models/assignments.py +++ b/core/models/assignments.py @@ -1,6 +1,6 @@ import enum from core import db -from core.apis.decorators import Principal +from core.apis.decorators import AuthPrincipal from core.libs import helpers, assertions from core.models.teachers import Teacher from core.models.students import Student @@ -60,14 +60,26 @@ def upsert(cls, assignment_new: 'Assignment'): return assignment @classmethod - def submit(cls, _id, teacher_id, principal: Principal): + 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 == principal.student_id, 'This assignment belongs to some other student') + assertions.assert_valid(assignment.student_id == auth_principal.student_id, 'This assignment belongs to some other student') 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') + + assignment.grade = grade + assignment.state = AssignmentStateEnum.GRADED db.session.flush() return assignment @@ -75,3 +87,7 @@ def submit(cls, _id, teacher_id, principal: Principal): @classmethod def get_assignments_by_student(cls, student_id): return cls.filter(cls.student_id == student_id).all() + + @classmethod + def get_assignments_by_teacher(cls, teacher_id): + return cls.filter(cls.teacher_id == teacher_id).all() diff --git a/core/models/principals.py b/core/models/principals.py new file mode 100644 index 000000000..317a9bcd2 --- /dev/null +++ b/core/models/principals.py @@ -0,0 +1,13 @@ +from core import db +from core.libs import helpers + + +class Principal(db.Model): + __tablename__ = 'principals' + id = db.Column(db.Integer, db.Sequence('principals_id_seq'), primary_key=True) + user_id = db.Column(db.Integer, db.ForeignKey('users.id')) + created_at = db.Column(db.TIMESTAMP(timezone=True), default=helpers.get_utc_now, nullable=False) + updated_at = db.Column(db.TIMESTAMP(timezone=True), default=helpers.get_utc_now, nullable=False, onupdate=helpers.get_utc_now) + + def __repr__(self): + return '' % self.id diff --git a/core/server.py b/core/server.py index c8afa65fe..c24eb4826 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 +from core.apis.assignments import student_assignments_resources, teacher_assignments_resources from core.libs import helpers from core.libs.exceptions import FyleError from werkzeug.exceptions import HTTPException @@ -9,6 +9,7 @@ from sqlalchemy.exc import IntegrityError app.register_blueprint(student_assignments_resources, url_prefix='/student') +app.register_blueprint(teacher_assignments_resources, url_prefix='/teacher') @app.route('/') 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 new file mode 100644 index 000000000..b6ab9396e --- /dev/null +++ b/tests/SQL/count_grade_A_assignments_by_teacher_with_max_grading.sql @@ -0,0 +1 @@ +-- Write query to find the number of grade A's given by the teacher who has graded the most assignments diff --git a/tests/SQL/number_of_assignments_per_state.sql b/tests/SQL/number_of_assignments_per_state.sql new file mode 100644 index 000000000..349efb0e9 --- /dev/null +++ b/tests/SQL/number_of_assignments_per_state.sql @@ -0,0 +1 @@ +-- Write query to get number of assignments for each state diff --git a/tests/SQL/sql_test.py b/tests/SQL/sql_test.py new file mode 100644 index 000000000..d8f3f9cf4 --- /dev/null +++ b/tests/SQL/sql_test.py @@ -0,0 +1,107 @@ +import random +from sqlalchemy import text + +from core import db +from core.models.assignments import Assignment, AssignmentStateEnum, GradeEnum + + +def create_n_graded_assignments_for_teacher(number: int = 0, teacher_id: int = 1) -> int: + """ + Creates 'n' graded assignments for a specified teacher and returns the count of assignments with grade 'A'. + + Parameters: + - number (int): The number of assignments to be created. + - teacher_id (int): The ID of the teacher for whom the assignments are created. + + Returns: + - int: Count of assignments with grade 'A'. + """ + # Count the existing assignments with grade 'A' for the specified teacher + grade_a_counter: int = Assignment.filter( + Assignment.teacher_id == teacher_id, + Assignment.grade == GradeEnum.A + ).count() + + # Create 'n' graded assignments + for _ in range(number): + # Randomly select a grade from GradeEnum + grade = random.choice(list(GradeEnum)) + + # Create a new Assignment instance + assignment = Assignment( + teacher_id=teacher_id, + student_id=1, + grade=grade, + content='test content', + state=AssignmentStateEnum.GRADED + ) + + # Add the assignment to the database session + db.session.add(assignment) + + # Update the grade_a_counter if the grade is 'A' + if grade == GradeEnum.A: + grade_a_counter = grade_a_counter + 1 + + # Commit changes to the database + db.session.commit() + + # Return the count of assignments with grade 'A' + return grade_a_counter + + +def test_get_assignments_in_various_states(): + """Test to get assignments in various states""" + + # Define the expected result before any changes + expected_result = [('DRAFT', 2), ('GRADED', 2), ('SUBMITTED', 2)] + + # Execute the SQL query and compare the result with the expected result + with open('tests/SQL/number_of_assignments_per_state.sql', encoding='utf8') as fo: + sql = fo.read() + + sql_result = db.session.execute(text(sql)).fetchall() + for itr, result in enumerate(expected_result): + assert result[0] == sql_result[itr][0] + assert result[1] == sql_result[itr][1] + + # Modify an assignment state and grade, then re-run the query and check the updated result + expected_result = [('DRAFT', 2), ('GRADED', 3), ('SUBMITTED', 1)] + + # Find an assignment in the 'SUBMITTED' state, change its state to 'GRADED' and grade to 'C' + submitted_assignment: Assignment = Assignment.filter(Assignment.state == AssignmentStateEnum.SUBMITTED).first() + submitted_assignment.state = AssignmentStateEnum.GRADED + submitted_assignment.grade = GradeEnum.C + + # Flush the changes to the database session + db.session.flush() + # Commit the changes to the database + db.session.commit() + + # Execute the SQL query again and compare the updated result with the expected result + sql_result = db.session.execute(text(sql)).fetchall() + for itr, result in enumerate(expected_result): + assert result[0] == sql_result[itr][0] + assert result[1] == sql_result[itr][1] + + +def test_get_grade_A_assignments_for_teacher_with_max_grading(): + """Test to get count of grade A assignments for teacher which has graded maximum assignments""" + + # Read the SQL query from a file + with open('tests/SQL/count_grade_A_assignments_by_teacher_with_max_grading.sql', encoding='utf8') as fo: + sql = fo.read() + + # Create and grade 5 assignments for the default teacher (teacher_id=1) + grade_a_count_1 = create_n_graded_assignments_for_teacher(5) + + # Execute the SQL query and check if the count matches the created assignments + sql_result = db.session.execute(text(sql)).fetchall() + assert grade_a_count_1 == sql_result[0][0] + + # Create and grade 10 assignments for a different teacher (teacher_id=2) + grade_a_count_2 = create_n_graded_assignments_for_teacher(10, 2) + + # Execute the SQL query again and check if the count matches the newly created assignments + sql_result = db.session.execute(text(sql)).fetchall() + assert grade_a_count_2 == sql_result[0][0] diff --git a/tests/conftest.py b/tests/conftest.py index cc9efeec0..1e1ce8a13 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -54,3 +54,15 @@ def h_teacher_2(): } return headers + + +@pytest.fixture +def h_principal(): + headers = { + 'X-Principal': json.dumps({ + 'principal_id': 1, + 'user_id': 5 + }) + } + + return headers diff --git a/tests/principals_test.py b/tests/principals_test.py new file mode 100644 index 000000000..da0bb1695 --- /dev/null +++ b/tests/principals_test.py @@ -0,0 +1,62 @@ +from core.models.assignments import AssignmentStateEnum, GradeEnum + + +def test_get_assignments(client, h_principal): + response = client.get( + '/principal/assignments', + headers=h_principal + ) + + assert response.status_code == 200 + + data = response.json['data'] + for assignment in data: + assert assignment['state'] in [AssignmentStateEnum.SUBMITTED, AssignmentStateEnum.GRADED] + + +def test_grade_assignment_draft_assignment(client, h_principal): + """ + failure case: If an assignment is in Draft state, it cannot be graded by principal + """ + response = client.post( + '/principal/assignments/grade', + json={ + 'id': 5, + 'grade': GradeEnum.A.value + }, + headers=h_principal + ) + + assert response.status_code == 400 + + +def test_grade_assignment(client, h_principal): + response = client.post( + '/principal/assignments/grade', + json={ + 'id': 4, + 'grade': GradeEnum.C.value + }, + headers=h_principal + ) + + assert response.status_code == 200 + + assert response.json['data']['state'] == AssignmentStateEnum.GRADED.value + assert response.json['data']['grade'] == GradeEnum.C + + +def test_regrade_assignment(client, h_principal): + response = client.post( + '/principal/assignments/grade', + json={ + 'id': 4, + 'grade': GradeEnum.B.value + }, + headers=h_principal + ) + + assert response.status_code == 200 + + assert response.json['data']['state'] == AssignmentStateEnum.GRADED.value + assert response.json['data']['grade'] == GradeEnum.B diff --git a/tests/students_test.py b/tests/students_test.py index 8f91a7de2..2a1fa708d 100644 --- a/tests/students_test.py +++ b/tests/students_test.py @@ -24,6 +24,21 @@ def test_get_assignments_student_2(client, h_student_2): assert assignment['student_id'] == 2 +def test_post_assignment_null_content(client, h_student_1): + """ + failure case: content cannot be null + """ + + response = client.post( + '/student/assignments', + headers=h_student_1, + json={ + 'content': None + }) + + assert response.status_code == 400 + + def test_post_assignment_student_1(client, h_student_1): content = 'ABCD TESTPOST' diff --git a/tests/teachers_test.py b/tests/teachers_test.py index 097bd790f..0b5f8a1b9 100644 --- a/tests/teachers_test.py +++ b/tests/teachers_test.py @@ -9,7 +9,7 @@ def test_get_assignments_teacher_1(client, h_teacher_1): data = response.json['data'] for assignment in data: assert assignment['teacher_id'] == 1 - assert assignment['state'] == 'SUBMITTED' + assert assignment['state'] in ['SUBMITTED', 'GRADED'] def test_get_assignments_teacher_2(client, h_teacher_2): @@ -23,7 +23,7 @@ def test_get_assignments_teacher_2(client, h_teacher_2): data = response.json['data'] for assignment in data: assert assignment['teacher_id'] == 2 - assert assignment['state'] == 'SUBMITTED' + assert assignment['state'] in ['SUBMITTED', 'GRADED'] def test_grade_assignment_cross(client, h_teacher_2): From dc0b5f9fb5b713211308582d1ab9c13d2c3506b2 Mon Sep 17 00:00:00 2001 From: Kirti Gautam Date: Wed, 10 Jan 2024 13:39:47 +0530 Subject: [PATCH 14/22] Minor fixes --- Application.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Application.md b/Application.md index fa49e4482..b6bb282b6 100644 --- a/Application.md +++ b/Application.md @@ -1,6 +1,6 @@ ## Application -There are 4 resources: +There are 5 resources: - Users - Principal - Students @@ -218,7 +218,7 @@ You'll need to implement these APIs ### GET /principal/assignments -List all assignments submitted to this teacher +List all submitted and graded assignments ``` headers: X-Principal: {"user_id":5, "principal_id":1} @@ -242,7 +242,7 @@ response: ### GET /principal/teachers -Get all the teachers +List all the teachers ``` headers: X-Principal: {"user_id":5, "principal_id":1} From 6ac9ddc47e94029637b279881cb7a89312f25f58 Mon Sep 17 00:00:00 2001 From: Kirti Gautam Date: Thu, 11 Jan 2024 12:34:40 +0530 Subject: [PATCH 15/22] update form link --- Application.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Application.md b/Application.md index b6bb282b6..ab1842968 100644 --- a/Application.md +++ b/Application.md @@ -30,7 +30,7 @@ Your tasks - number_of_assignments_per_state.sql - Optionally, Dockerize your application by creating a Dockerfile and a docker-compose.yml file, providing clear documentation on building and running the application with Docker, to stand out in your submission -Once you are done with your task, please use [this form](https://forms.gle/nWVJe1kLPgNmpVoM8) to complete your submission. +Once you are done with your task, please use [this form](https://forms.gle/dJLNMyBmBCJSv6EH7) to complete your submission. You will hear back within 48 hours from us via email. From 6faa1390690d8321e4d1659797df648f42e550b8 Mon Sep 17 00:00:00 2001 From: Kirti Gautam <53394284+KirtiGautam@users.noreply.github.com> Date: Thu, 11 Jan 2024 16:20:30 +0530 Subject: [PATCH 16/22] Update Application.md --- Application.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Application.md b/Application.md index ab1842968..c6ff9a8a4 100644 --- a/Application.md +++ b/Application.md @@ -20,6 +20,8 @@ There are 5 resources: ## Challenge +Please fork the repository into your account and continue the development in a new branch. + Your tasks - Add missing APIs mentioned here and get the automated tests to pass - Add tests for grading API From 610d9cc802a5136474136643d9a86250eac19a47 Mon Sep 17 00:00:00 2001 From: Kirti Gautam <53394284+KirtiGautam@users.noreply.github.com> Date: Thu, 11 Jan 2024 16:22:02 +0530 Subject: [PATCH 17/22] Update Application.md --- Application.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Application.md b/Application.md index c6ff9a8a4..1ee5aead1 100644 --- a/Application.md +++ b/Application.md @@ -20,7 +20,7 @@ There are 5 resources: ## Challenge -Please fork the repository into your account and continue the development in a new branch. +Please fork the repository into your account and continue the development in your fork. Your tasks - Add missing APIs mentioned here and get the automated tests to pass From 28afbac0f2fa5f6cdf087c25934319da5e4448a3 Mon Sep 17 00:00:00 2001 From: Kirti Gautam <53394284+KirtiGautam@users.noreply.github.com> Date: Fri, 12 Jan 2024 09:44:21 +0530 Subject: [PATCH 18/22] Update Application.md --- Application.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Application.md b/Application.md index 1ee5aead1..e6afd8c47 100644 --- a/Application.md +++ b/Application.md @@ -32,7 +32,7 @@ Your tasks - number_of_assignments_per_state.sql - Optionally, Dockerize your application by creating a Dockerfile and a docker-compose.yml file, providing clear documentation on building and running the application with Docker, to stand out in your submission -Once you are done with your task, please use [this form](https://forms.gle/dJLNMyBmBCJSv6EH7) to complete your submission. +***Once you are done with your task, please use [this form](https://forms.gle/dJLNMyBmBCJSv6EH7) to complete your submission.*** You will hear back within 48 hours from us via email. From e878eacb71a392a81d56e9a347eb2204beefaea4 Mon Sep 17 00:00:00 2001 From: Kirti Gautam <53394284+KirtiGautam@users.noreply.github.com> Date: Fri, 12 Jan 2024 11:04:53 +0530 Subject: [PATCH 19/22] Update Application.md --- Application.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Application.md b/Application.md index e6afd8c47..3bb8412e8 100644 --- a/Application.md +++ b/Application.md @@ -25,6 +25,7 @@ Please fork the repository into your account and continue the development in you Your tasks - Add missing APIs mentioned here and get the automated tests to pass - Add tests for grading API +- Please be aware that intentional bugs have been incorporated into the application, leading to test failures. Kindly address and rectify these issues as part of the assignment. - All tests should pass - Get the test coverage to 94% or above - There are certain SQL tests present inside `tests/SQL/`. You have to write SQL in following files: From a013d9a16af8b4fee08911fafc83f88af8c9d147 Mon Sep 17 00:00:00 2001 From: Nilesh Pant <58652823+NileshPant1999@users.noreply.github.com> Date: Wed, 17 Apr 2024 15:48:35 +0530 Subject: [PATCH 20/22] General Updates(#45) --- Application.md | 2 +- core/apis/assignments/teacher.py | 2 +- core/models/assignments.py | 4 +- tests/SQL/number_of_assignments_per_state.sql | 1 - ...of_graded_assignments_for_each_student.sql | 1 + tests/SQL/sql_test.py | 37 ++++++++----------- tests/teachers_test.py | 1 - 7 files changed, 20 insertions(+), 28 deletions(-) delete mode 100644 tests/SQL/number_of_assignments_per_state.sql create mode 100644 tests/SQL/number_of_graded_assignments_for_each_student.sql diff --git a/Application.md b/Application.md index 3bb8412e8..7620bf4d0 100644 --- a/Application.md +++ b/Application.md @@ -30,7 +30,7 @@ Your tasks - Get the test coverage to 94% or above - There are certain SQL tests present inside `tests/SQL/`. You have to write SQL in following files: - count_grade_A_assignments_by_teacher_with_max_grading.sql - - number_of_assignments_per_state.sql + - number_of_graded_assignments_for_each_student.sql - Optionally, Dockerize your application by creating a Dockerfile and a docker-compose.yml file, providing clear documentation on building and running the application with Docker, to stand out in your submission ***Once you are done with your task, please use [this form](https://forms.gle/dJLNMyBmBCJSv6EH7) to complete your submission.*** diff --git a/core/apis/assignments/teacher.py b/core/apis/assignments/teacher.py index c250e01c8..20fcaaa40 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(p.teacher_id) + teachers_assignments = Assignment.get_assignments_by_teacher() 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 e7a5bdcf8..6a4d6cb5f 100644 --- a/core/models/assignments.py +++ b/core/models/assignments.py @@ -89,5 +89,5 @@ def get_assignments_by_student(cls, student_id): return cls.filter(cls.student_id == student_id).all() @classmethod - def get_assignments_by_teacher(cls, teacher_id): - return cls.filter(cls.teacher_id == teacher_id).all() + def get_assignments_by_teacher(cls): + return cls.query.all() diff --git a/tests/SQL/number_of_assignments_per_state.sql b/tests/SQL/number_of_assignments_per_state.sql deleted file mode 100644 index 349efb0e9..000000000 --- a/tests/SQL/number_of_assignments_per_state.sql +++ /dev/null @@ -1 +0,0 @@ --- Write query to get number of assignments for each state diff --git a/tests/SQL/number_of_graded_assignments_for_each_student.sql b/tests/SQL/number_of_graded_assignments_for_each_student.sql new file mode 100644 index 000000000..a62fd173e --- /dev/null +++ b/tests/SQL/number_of_graded_assignments_for_each_student.sql @@ -0,0 +1 @@ +-- Write query to get number of graded assignments for each student: diff --git a/tests/SQL/sql_test.py b/tests/SQL/sql_test.py index d8f3f9cf4..0c66405be 100644 --- a/tests/SQL/sql_test.py +++ b/tests/SQL/sql_test.py @@ -50,39 +50,32 @@ def create_n_graded_assignments_for_teacher(number: int = 0, teacher_id: int = 1 return grade_a_counter -def test_get_assignments_in_various_states(): - """Test to get assignments in various states""" +def test_get_assignments_in_graded_state_for_each_student(): + """Test to get graded assignments for each student""" - # Define the expected result before any changes - expected_result = [('DRAFT', 2), ('GRADED', 2), ('SUBMITTED', 2)] - - # Execute the SQL query and compare the result with the expected result - with open('tests/SQL/number_of_assignments_per_state.sql', encoding='utf8') as fo: - sql = fo.read() - - sql_result = db.session.execute(text(sql)).fetchall() - for itr, result in enumerate(expected_result): - assert result[0] == sql_result[itr][0] - assert result[1] == sql_result[itr][1] - - # Modify an assignment state and grade, then re-run the query and check the updated result - expected_result = [('DRAFT', 2), ('GRADED', 3), ('SUBMITTED', 1)] + # Find all the assignments for student 1 and change its state to 'GRADED' + submitted_assignments: Assignment = Assignment.filter(Assignment.student_id == 1) - # Find an assignment in the 'SUBMITTED' state, change its state to 'GRADED' and grade to 'C' - submitted_assignment: Assignment = Assignment.filter(Assignment.state == AssignmentStateEnum.SUBMITTED).first() - submitted_assignment.state = AssignmentStateEnum.GRADED - submitted_assignment.grade = GradeEnum.C + # Iterate over each assignment and update its state + for assignment in submitted_assignments: + assignment.state = AssignmentStateEnum.GRADED # Or any other desired state # Flush the changes to the database session db.session.flush() # Commit the changes to the database db.session.commit() - # Execute the SQL query again and compare the updated result with the expected result + # Define the expected result before any changes + expected_result = [(1, 3)] + + # Execute the SQL query and compare the result with the expected result + with open('tests/SQL/number_of_graded_assignments_for_each_student.sql', encoding='utf8') as fo: + sql = fo.read() + + # Execute the SQL query compare the result with the expected result sql_result = db.session.execute(text(sql)).fetchall() for itr, result in enumerate(expected_result): assert result[0] == sql_result[itr][0] - assert result[1] == sql_result[itr][1] def test_get_grade_A_assignments_for_teacher_with_max_grading(): diff --git a/tests/teachers_test.py b/tests/teachers_test.py index 0b5f8a1b9..8b5c818c5 100644 --- a/tests/teachers_test.py +++ b/tests/teachers_test.py @@ -9,7 +9,6 @@ def test_get_assignments_teacher_1(client, h_teacher_1): data = response.json['data'] for assignment in data: assert assignment['teacher_id'] == 1 - assert assignment['state'] in ['SUBMITTED', 'GRADED'] def test_get_assignments_teacher_2(client, h_teacher_2): From a7d8c88c05f8116846a8cdd92ac3e81200adcd80 Mon Sep 17 00:00:00 2001 From: Kirti Gautam <53394284+KirtiGautam@users.noreply.github.com> Date: Fri, 10 May 2024 11:09:43 +0530 Subject: [PATCH 21/22] README updates (#60) --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 6a48eb670..f021bb03f 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,8 @@ We are an extremely transparent organization. Check out our [careers page](https ## Challenge outline +**You are allowed to use any online/AI tool such as ChatGPT, Gemini, etc. to complete the challenge. However, we expect you to fully understand the code and logic involved.** + This challenge involves writing a backend service for a classroom. The challenge is described in detail [here](./Application.md) From 93b702bee740354e7c6a29776c95fe0e0ff95419 Mon Sep 17 00:00:00 2001 From: sumanth-fyle <99784932+sumanth-fyle@users.noreply.github.com> Date: Fri, 12 Jul 2024 15:11:49 +0530 Subject: [PATCH 22/22] Update Application.md --- Application.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Application.md b/Application.md index 7620bf4d0..a6a535d46 100644 --- a/Application.md +++ b/Application.md @@ -33,7 +33,7 @@ Your tasks - number_of_graded_assignments_for_each_student.sql - Optionally, Dockerize your application by creating a Dockerfile and a docker-compose.yml file, providing clear documentation on building and running the application with Docker, to stand out in your submission -***Once you are done with your task, please use [this form](https://forms.gle/dJLNMyBmBCJSv6EH7) to complete your submission.*** +***Once you are done with your task, please use [this form](https://forms.gle/TbpVhYojG84cyUD77) to complete your submission.*** You will hear back within 48 hours from us via email.