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

task completed #50

Closed
wants to merge 1 commit into from
Closed
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
13 changes: 13 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
FROM python:3.8-slim-buster

# Set working directory
WORKDIR /app

# Copy the entire local directory into the image
COPY . /app

# Activate virtual environment and install dependencies
RUN pip install -r requirements.txt
EXPOSE 4040
# Run bash script
CMD ["bash", "run.sh"]
48 changes: 48 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,54 @@ flask db upgrade -d core/migrations/
```
bash run.sh
```
### Installing Docker
1. Install docker application
### Creating Docker file
2. Create a docker file with name `dockerfile` without any extension
3. Add
```
FROM python:3.8-slim-buster

# Set working directory
WORKDIR /app

# Copy the entire local directory into the image
COPY . /app

# Activate virtual environment and install dependencies
RUN pip install -r requirements.txt
EXPOSE 4040
# Run bash script
CMD ["bash", "run.sh"]
```
in the dockerfile.
### Modifying run.sh
4. Open run.sh file and uncomment line
```
# flask db upgrade -d core/migrations/
```
### Installing Ubuntu terminal and starting server
4. Install Ubuntu terminal and Run ` sudo apt-get install dos2unix` in ubuntu terminal
6. Run `sudo apt-get update`
7. Run `dos2unix run.sh`
### Building Docker Image
8.Open git bash terminal in the project repository and Run command
`docker build -t fylein-app .`
in your git bash terminal
9. Run command
`docker run -p 4040:7755 fylein-app`
10. Everytime the image is to updated, we have to rebuild the docker container and hence have to run step 4 and 5 again, instead we can create a file named `docker-compose.yml` and add content
`version: '3'
services:
fylein-app:
build: .
ports:
- "4040:7755"
volumes:
- .:/src`
in it and then run `docker compose-up` which auto updates the image.


### Run Tests

```
Expand Down
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
35 changes: 35 additions & 0 deletions core/apis/assignments/principal.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
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, AssignmentStateEnum
from core.models.teachers import Teacher

from .schema import AssignmentSchema, AssignmentGradeSchema
principal_assignments_resources = Blueprint('principal_assignments_resources', __name__)

@principal_assignments_resources.route('/assignments', methods = ['GET'], strict_slashes = False)

@decorators.authenticate_principal
def get_all_assignments(p):
"""Returns graded assignments and submitted assignments"""
principal_assignments_get = Assignment.filter(Assignment.state != AssignmentStateEnum.DRAFT).all()
principal_assignments_get_dump = AssignmentSchema().dump(principal_assignments_get, many=True)
return APIResponse.respond(data = principal_assignments_get_dump)


@principal_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"""
assignment_payload = AssignmentGradeSchema().load(incoming_payload)
marking_grade = Assignment.mark_grade_by_principal(_id= assignment_payload.id,grade=assignment_payload.grade, auth_principal=p)
db.session.commit()
marking_grade_dump = AssignmentSchema().dump(marking_grade)
return APIResponse.respond(data=marking_grade_dump)




2 changes: 1 addition & 1 deletion core/apis/assignments/teacher.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
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 teachers_resources
14 changes: 14 additions & 0 deletions core/apis/teachers/principal.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
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 teachers_details

teachers_resources = Blueprint('teachers_resources', __name__)

@teachers_resources.route('/teachers', methods = ['GET'], strict_slashes = False)
@decorators.authenticate_principal
def list_teachers(p):
all_teachers_dump = teachers_details().dump(Teacher.get_teachers(), many=True)
return APIResponse.respond(data = all_teachers_dump)
27 changes: 27 additions & 0 deletions core/apis/teachers/schema.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from marshmallow import Schema, EXCLUDE, fields, post_load
from marshmallow_sqlalchemy import SQLAlchemyAutoSchema, auto_field
from marshmallow_enum import EnumField
from core.models.teachers import Teacher


class teachers_details(SQLAlchemyAutoSchema):

class Meta:

model = Teacher

unknown = EXCLUDE

id = auto_field(required=False, allow_none=False)

user_id = auto_field(required=True, allow_none=False)
created_at = auto_field(dump_only=True)
updated_at = auto_field(dump_only=True)

@post_load

def initiate_class(self,data_dict, many,partial):

return Teacher (**data_dict)


20 changes: 17 additions & 3 deletions core/models/assignments.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ def get_by_id(cls, _id):

@classmethod
def upsert(cls, assignment_new: 'Assignment'):
assertions.assert_valid(assignment_new.content is not None, 'content cannot be null')
if assignment_new.id is not None:
assignment = Assignment.get_by_id(assignment_new.id)
assertions.assert_found(assignment, 'No assignment with this id was found')
Expand All @@ -65,8 +66,10 @@ def submit(cls, _id, teacher_id, auth_principal: AuthPrincipal):
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')

assertions.assert_valid(assignment.state == AssignmentStateEnum.DRAFT,'only a draft assignment can be submitted')

assignment.teacher_id = teacher_id
assignment.state = AssignmentStateEnum.SUBMITTED
db.session.flush()

return assignment
Expand All @@ -77,6 +80,7 @@ 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(assignment.teacher_id == auth_principal.teacher_id, f'assignment {_id} was submitted to teacher {assignment.teacher_id} and not teacher {auth_principal.teacher_id}')

assignment.grade = grade
assignment.state = AssignmentStateEnum.GRADED
Expand All @@ -89,5 +93,15 @@ 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 mark_grade_by_principal(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(assignment.state is not AssignmentStateEnum.DRAFT, 'cannot grade draft assignments')
assignment.grade = grade
assignment.state = AssignmentStateEnum.GRADED
db.session.flush()
return assignment
4 changes: 4 additions & 0 deletions core/models/teachers.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,7 @@ class Teacher(db.Model):

def __repr__(self):
return '<Teacher %r>' % self.id

@classmethod
def get_teachers(cls):
return db.session.query(cls).filter().all()
7 changes: 4 additions & 3 deletions core/server.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
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

from core.apis.teachers import 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.register_blueprint(teachers_resources, url_prefix='/principal')

@app.route('/')
def ready():
Expand Down
8 changes: 8 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
version: '3'
services:
fylein-app:
build: .
ports:
- "4040:7755"
volumes:
- .:/app
Original file line number Diff line number Diff line change
@@ -1 +1,19 @@
-- Write query to find the number of grade A's given by the teacher who has graded the most assignments
WITH graded_assignments AS (
SELECT
teacher_id,
COUNT(*) AS grade_a_count
FROM
assignments
WHERE
grade = 'A'
GROUP BY
teacher_id
ORDER BY
grade_a_count DESC
LIMIT 1
)
SELECT
COALESCE(grade_a_count, 0) AS count
FROM
graded_assignments;
9 changes: 9 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,10 @@
-- Write query to get number of graded assignments for each student:
SELECT
student_id,
COUNT(*) AS num_graded_assignments
FROM
assignments
WHERE
grade IS NOT NULL
GROUP BY
student_id;
14 changes: 14 additions & 0 deletions tests/students_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,17 @@ 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_edit_submitted_assignment_error(client,h_student_1):
"""Test Failiure Reason: Cannot edit submitted assignments"""
response = client.post(
'/student/assignments',
headers = h_student_1,
json={
'id' : 2,
'content' : "All is Well"
})
error_response = response.json
assert response.status_code == 400

17 changes: 17 additions & 0 deletions tests/teachers_test.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from core.models.assignments import AssignmentStateEnum, GradeEnum

def test_get_assignments_teacher_1(client, h_teacher_1):
response = client.get(
'/teacher/assignments',
Expand Down Expand Up @@ -99,3 +101,18 @@ def test_grade_assignment_draft_assignment(client, h_teacher_1):
data = response.json

assert data['error'] == 'FyleError'


def test_grade_assignment_successfully(client,h_teacher_1):
"""Grading a submitted Assignment"""
response = client.post(
'teacher/assignments/grade',
headers = h_teacher_1,
json = {
"id": 1,
"grade": GradeEnum.B.value
}
)

assert response.status_code == 200
assert response.json['data']["grade"] == GradeEnum.B.value