Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Assignment Done #155

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added core/.DS_Store
Binary file not shown.
1 change: 1 addition & 0 deletions core/apis/assignments/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
from .student import student_assignments_resources
from .teacher import teacher_assignments_resources
from .principal import principal_assignments_resources
45 changes: 45 additions & 0 deletions core/apis/assignments/principal.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
from flask import Blueprint,jsonify
from core import db
from core.apis import decorators
from core.apis.responses import APIResponse
from core.models.assignments import Assignment,Teacher,AssignmentStateEnum
from .schema import AssignmentSchema,TeacherSchema,AssignmentGradeSchema

# Creating a Blueprint variable for routing
principal_assignments_resources = Blueprint('principal_assignments_resources', __name__)


# List all submitted and graded assignments
@principal_assignments_resources.route('/assignments', methods=['GET'], strict_slashes=False)
@decorators.authenticate_principal
def list_submitted_graded_assignments(p):
"""Returns list of graded and submitted assignments"""
students_assignments = Assignment.get_graded_submitted_assignments()
students_assignments_dump = AssignmentSchema().dump(students_assignments, many=True)
return APIResponse.respond(data=students_assignments_dump)

# List all the teachers
@principal_assignments_resources.route('/teachers', methods=['GET'], strict_slashes=False)
@decorators.authenticate_principal
def get_teachers(p):
"""Returns list of teachers"""
teachers = Teacher.query.all()
teachers_dump = TeacherSchema().dump(teachers, many=True)
return APIResponse.respond(data=teachers_dump)


# Grade or re-grade an assignment
@principal_assignments_resources.route('/assignments/grade', methods=['POST'], strict_slashes=False)
@decorators.accept_payload
@decorators.authenticate_principal
def edit_grade_of_assignment(p,incoming_payload):
"""Grade or re-grade an assignment"""
edit_grade_assignments_payload = AssignmentGradeSchema().load(incoming_payload)
edited_graded_assignment=Assignment.mark_grade(
_id=edit_grade_assignments_payload.id,
grade=edit_grade_assignments_payload.grade,
auth_principal=p
)
db.session.commit()
edited_graded_assignment_dump=AssignmentSchema().dump(edited_graded_assignment)
return APIResponse.respond(data=edited_graded_assignment_dump)
17 changes: 16 additions & 1 deletion core/apis/assignments/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
from marshmallow_enum import EnumField
from core.models.assignments import Assignment, GradeEnum
from core.libs.helpers import GeneralObject

from core.models.teachers import Teacher
from core.models.users import User

class AssignmentSchema(SQLAlchemyAutoSchema):
class Meta:
Expand Down Expand Up @@ -49,3 +50,17 @@ class Meta:
def initiate_class(self, data_dict, many, partial):
# pylint: disable=unused-argument,no-self-use
return GeneralObject(**data_dict)

# Schema for the User model to serialization and deserialization
class UserSchema(SQLAlchemyAutoSchema):
class Meta:
model = User
load_instance = True

# Schema for Teacher model to serialization and deserialization
class TeacherSchema(SQLAlchemyAutoSchema):
user = fields.Nested(UserSchema, exclude=('teachers',))
class Meta:
model = Teacher
include_relationships = True
load_instance = True
17 changes: 10 additions & 7 deletions core/apis/assignments/student.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
from core import db
from core.apis import decorators
from core.apis.responses import APIResponse
from core.models.assignments import Assignment

from core.models.assignments import Assignment,AssignmentStateEnum
from core.libs.assertions import base_assert
from .schema import AssignmentSchema, AssignmentSubmitSchema

student_assignments_resources = Blueprint('student_assignments_resources', __name__)


Expand Down Expand Up @@ -37,12 +38,14 @@ def upsert_assignment(p, incoming_payload):
def submit_assignment(p, incoming_payload):
"""Submit an assignment"""
submit_assignment_payload = AssignmentSubmitSchema().load(incoming_payload)

# assignment=Assignment.get_by_id(submit_assignment_payload.id)
# if(assignment.state != AssignmentStateEnum.SUBMITTED.value):
submitted_assignment = Assignment.submit(
_id=submit_assignment_payload.id,
teacher_id=submit_assignment_payload.teacher_id,
auth_principal=p
)
_id=submit_assignment_payload.id,
teacher_id=submit_assignment_payload.teacher_id,
auth_principal=p
)
db.session.commit()
submitted_assignment_dump = AssignmentSchema().dump(submitted_assignment)
return APIResponse.respond(data=submitted_assignment_dump)

32 changes: 19 additions & 13 deletions core/apis/assignments/teacher.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from flask import Blueprint
from flask import Blueprint,jsonify,request
from core.libs.assertions import base_assert
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__)

Expand All @@ -12,7 +12,8 @@
@decorators.authenticate_principal
def list_assignments(p):
"""Returns list of assignments"""
teachers_assignments = Assignment.get_assignments_by_teacher()
authorization_header = request.headers.get('Authorization')
teachers_assignments = Assignment.get_assignments_by_teacher(teacher_id=authorization_header)
teachers_assignments_dump = AssignmentSchema().dump(teachers_assignments, many=True)
return APIResponse.respond(data=teachers_assignments_dump)

Expand All @@ -21,14 +22,19 @@ def list_assignments(p):
@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)
headers=request.headers.get('Authorization')
assignment=Assignment.get_by_id(grade_assignment_payload.id)
if assignment != None:
teacher_id=assignment.teacher_id
if(headers==teacher_id):
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)
return base_assert(400,"Error")
return base_assert(404,"No Assignment Found")
4 changes: 1 addition & 3 deletions core/apis/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,6 @@ def wrapper(*args, **kwargs):
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')

return func(p, *args, **kwargs)
return wrapper
return wrapper
1 change: 1 addition & 0 deletions core/apis/teachers/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .principal import principal_teachers_resources
18 changes: 18 additions & 0 deletions core/apis/teachers/principal.py
Original file line number Diff line number Diff line change
@@ -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.fetch_all()
print(teachers_list[0],end=" ")
teachers_list_dump = TeacherSchema().dump(teachers_list, many=True)
return APIResponse.respond(data=teachers_list_dump)
14 changes: 14 additions & 0 deletions core/apis/teachers/schema.py
Original file line number Diff line number Diff line change
@@ -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)
25 changes: 17 additions & 8 deletions core/models/assignments.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class Assignment(db.Model):
id = db.Column(db.Integer, db.Sequence('assignments_id_seq'), primary_key=True)
student_id = db.Column(db.Integer, db.ForeignKey(Student.id), nullable=False)
teacher_id = db.Column(db.Integer, db.ForeignKey(Teacher.id), nullable=True)
content = db.Column(db.Text)
content = db.Column(db.Text,nullable=False)
grade = db.Column(BaseEnum(GradeEnum))
state = db.Column(BaseEnum(AssignmentStateEnum), default=AssignmentStateEnum.DRAFT, nullable=False)
created_at = db.Column(db.TIMESTAMP(timezone=True), default=helpers.get_utc_now, nullable=False)
Expand Down Expand Up @@ -58,25 +58,28 @@ def upsert(cls, assignment_new: 'Assignment'):

db.session.flush()
return assignment

# Validating conditions before submitting the assignment
@classmethod
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.content is not None, 'assignment with empty content cannot be submitted')

assignment.teacher_id = teacher_id
assertions.assert_valid(assignment.state is not AssignmentStateEnum.SUBMITTED.value,"only draft assignments can be submitted")
assertions.assert_valid(teacher_id != None or assignment.teacher_id==teacher_id,'Wrong Teacher')
assignment.teacher_id = teacher_id
assignment.state = AssignmentStateEnum.SUBMITTED
db.session.flush()

return assignment


# Validate condition before marking grade
@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')
# assertions.assert_valid(grade is not None, 'assignment with empty grade cannot be graded')
assertions.assert_valid(assignment.state ==AssignmentStateEnum.SUBMITTED.value or assignment.state==AssignmentStateEnum.GRADED.value,'only assignment in submitted state can be graded')

assignment.grade = grade
assignment.state = AssignmentStateEnum.GRADED
Expand All @@ -89,5 +92,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):
if teacher_id!=None:
return cls.query.filter(teacher_id=teacher_id)
@classmethod
def get_graded_submitted_assignments(cls):
return cls.query.filter(
cls.state.in_([AssignmentStateEnum.SUBMITTED, AssignmentStateEnum.GRADED])
).all()
8 changes: 4 additions & 4 deletions core/server.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
from flask import jsonify
from marshmallow.exceptions import ValidationError
from core.apis.assignments import principal_assignments_resources,student_assignments_resources, teacher_assignments_resources
from core import app
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

from core.apis.teachers import principal_teachers_resources
from sqlalchemy.exc import IntegrityError

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('/')
def ready():
Expand Down Expand Up @@ -41,4 +41,4 @@ def handle_error(err):
error=err.__class__.__name__, message=str(err)
), err.code

raise err
raise err
Original file line number Diff line number Diff line change
@@ -1 +1,15 @@
-- Write query to find the number of grade A's given by the teacher who has graded the most assignments
with top_teacher as (
select teacher_id
from assignments
where state = 'GRADED'
group by teacher_id
order by count(*) desc
limit 1
),
graded_a as (
select *
from assignments
where grade = 'A' and teacher_id = (select teacher_id from top_teacher)
)
select count(*) as grade_a
from graded_a;
4 changes: 4 additions & 0 deletions tests/SQL/number_of_graded_assignments_for_each_student.sql
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
-- Write query to get number of graded assignments for each student:
select student_id,count(*) as graded_assignments_count
from assignments
WHERE state = 'GRADED'
GROUP BY student_id;
27 changes: 15 additions & 12 deletions tests/SQL/sql_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,21 +80,24 @@ def test_get_assignments_in_graded_state_for_each_student():

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]
# Execute the SQL query
result_proxy = db.session.execute(text(sql))

# Fetch the results
sql_result = result_proxy.fetchall()

# Check if the query returned any results
if sql_result:
# Check if the count matches the created assignments
assert sql_result[0][0] == grade_a_count_1
else:
# If the query didn't return any results, assert that the count is 0
assert 0 == grade_a_count_1
2 changes: 1 addition & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,4 @@ def h_principal():
})
}

return headers
return headers
Loading