-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'development' into backend/fix/projectvisiblestudents
- Loading branch information
Showing
22 changed files
with
440 additions
and
83 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
128 changes: 128 additions & 0 deletions
128
backend/project/endpoints/projects/groups/group_student.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
"""Endpoint for joining and leaving groups in a project""" | ||
|
||
|
||
from os import getenv | ||
from urllib.parse import urljoin | ||
from dotenv import load_dotenv | ||
from flask import request | ||
from flask_restful import Resource | ||
from sqlalchemy.exc import SQLAlchemyError | ||
|
||
from project.utils.query_agent import insert_into_model | ||
from project.models.group import Group | ||
from project.models.project import Project | ||
from project.utils.authentication import authorize_student_submission | ||
|
||
from project import db | ||
|
||
load_dotenv() | ||
API_URL = getenv("API_HOST") | ||
RESPONSE_URL = urljoin(f"{API_URL}/", "groups") | ||
|
||
|
||
class GroupStudent(Resource): | ||
"""Api endpoint to allow students to join and leave project groups""" | ||
@authorize_student_submission | ||
def post(self, project_id, group_id, uid=None): | ||
""" | ||
This function will allow students to join project groups if not full | ||
""" | ||
try: | ||
project = db.session.query(Project).filter_by( | ||
project_id=project_id).first() | ||
if project.groups_locked: | ||
return { | ||
"message": "Groups are locked for this project", | ||
"url": RESPONSE_URL | ||
}, 400 | ||
|
||
group = db.session.query(Group).filter_by( | ||
project_id=project_id, group_id=group_id).first() | ||
if group is None: | ||
return { | ||
"message": "Group does not exist", | ||
"url": RESPONSE_URL | ||
}, 404 | ||
|
||
joined_groups = db.session.query(GroupStudent).filter_by( | ||
uid=uid, project_id=project_id).all() | ||
if len(joined_groups) > 0: | ||
return { | ||
"message": "Student is already in a group", | ||
"url": RESPONSE_URL | ||
}, 400 | ||
|
||
joined_students = db.session.query(GroupStudent).filter_by( | ||
group_id=group_id, project_id=project_id).all() | ||
if len(joined_students) >= group.group_size: | ||
return { | ||
"message": "Group is full", | ||
"url": RESPONSE_URL | ||
}, 400 | ||
|
||
req = request.json | ||
req["project_id"] = project_id | ||
req["group_id"] = group_id | ||
req["uid"] = uid | ||
return insert_into_model( | ||
GroupStudent, | ||
req, | ||
RESPONSE_URL, | ||
"group_id", | ||
required_fields=["project_id", "group_id", "uid"] | ||
) | ||
except SQLAlchemyError: | ||
data = { | ||
"url": urljoin(f"{API_URL}/", "projects") | ||
} | ||
data["message"] = "An error occurred while fetching the projects" | ||
return data, 500 | ||
|
||
|
||
@authorize_student_submission | ||
def delete(self, project_id, group_id, uid=None): | ||
""" | ||
This function will allow students to leave project groups | ||
""" | ||
data = { | ||
"url": urljoin(f"{API_URL}/", "projects") | ||
} | ||
try: | ||
project = db.session.query(Project).filter_by( | ||
project_id=project_id).first() | ||
if project.groups_locked: | ||
return { | ||
"message": "Groups are locked for this project", | ||
"url": RESPONSE_URL | ||
}, 400 | ||
|
||
group = db.session.query(Group).filter_by( | ||
project_id=project_id, group_id=group_id).first() | ||
if group is None: | ||
return { | ||
"message": "Group does not exist", | ||
"url": RESPONSE_URL | ||
}, 404 | ||
|
||
if uid is None: | ||
return { | ||
"message": "Failed to verify uid of user", | ||
"url": RESPONSE_URL | ||
}, 400 | ||
|
||
student_group = db.session.query(GroupStudent).filter_by( | ||
group_id=group_id, project_id=project_id, uid=uid).first() | ||
if student_group is None: | ||
return { | ||
"message": "Student is not in the group", | ||
"url": RESPONSE_URL | ||
}, 404 | ||
|
||
db.session.delete(student_group) | ||
db.session.commit() | ||
data["message"] = "Student has succesfully left the group" | ||
return data, 200 | ||
|
||
except SQLAlchemyError: | ||
data["message"] = "An error occurred while fetching the projects" | ||
return data, 500 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
"""Endpoint for creating/deleting groups in a project""" | ||
from os import getenv | ||
from urllib.parse import urljoin | ||
from dotenv import load_dotenv | ||
from flask import request | ||
from flask_restful import Resource | ||
from sqlalchemy.exc import SQLAlchemyError | ||
|
||
from project.models.project import Project | ||
from project.models.group import Group | ||
from project.utils.query_agent import query_selected_from_model, insert_into_model | ||
from project.utils.authentication import ( | ||
authorize_teacher_or_student_of_project, | ||
authorize_teacher_of_project | ||
) | ||
from project import db | ||
|
||
load_dotenv() | ||
API_URL = getenv("API_HOST") | ||
RESPONSE_URL = urljoin(f"{API_URL}/", "groups") | ||
|
||
|
||
class Groups(Resource): | ||
"""Api endpoint for the /project/project_id/groups link""" | ||
|
||
@authorize_teacher_of_project | ||
def patch(self, project_id): | ||
""" | ||
This function will set locked state of project groups, | ||
need to pass locked field in the body | ||
""" | ||
req = request.json | ||
locked = req.get("locked") | ||
if locked is None: | ||
return { | ||
"message": "Bad request: locked field is required", | ||
"url": RESPONSE_URL | ||
}, 400 | ||
|
||
try: | ||
project = db.session.query(Project).filter_by( | ||
project_id=project_id).first() | ||
if project is None: | ||
return { | ||
"message": "Project does not exist", | ||
"url": RESPONSE_URL | ||
}, 404 | ||
project.groups_locked = locked | ||
db.session.commit() | ||
|
||
return { | ||
"message": "Groups are locked", | ||
"url": RESPONSE_URL | ||
}, 200 | ||
except SQLAlchemyError: | ||
return { | ||
"message": "Database error", | ||
"url": RESPONSE_URL | ||
}, 500 | ||
|
||
@authorize_teacher_or_student_of_project | ||
def get(self, project_id): | ||
""" | ||
Get function for /project/project_id/groups this will be the main endpoint | ||
to get all groups for a project | ||
""" | ||
return query_selected_from_model( | ||
Group, | ||
RESPONSE_URL, | ||
url_mapper={"group_id": RESPONSE_URL}, | ||
filters={"project_id": project_id} | ||
) | ||
|
||
@authorize_teacher_of_project | ||
def post(self, project_id): | ||
""" | ||
This function will create a new group for a project | ||
if the body of the post contains a group_size and project_id exists | ||
""" | ||
|
||
req = request.json | ||
req["project_id"] = project_id | ||
return insert_into_model( | ||
Group, | ||
req, | ||
RESPONSE_URL, | ||
"group_id", | ||
required_fields=["project_id", "group_size"] | ||
) | ||
|
||
@authorize_teacher_of_project | ||
def delete(self, project_id): | ||
""" | ||
This function will delete a group | ||
if group_id is provided and request is from teacher | ||
""" | ||
|
||
req = request.json | ||
group_id = req.get("group_id") | ||
if group_id is None: | ||
return { | ||
"message": "Bad request: group_id is required", | ||
"url": RESPONSE_URL | ||
}, 400 | ||
|
||
try: | ||
project = db.session.query(Project).filter_by( | ||
project_id=project_id).first() | ||
if project is None: | ||
return { | ||
"message": "Project associated with group does not exist", | ||
"url": RESPONSE_URL | ||
}, 404 | ||
|
||
group = db.session.query(Group).filter_by( | ||
project_id=project_id, group_id=group_id).first() | ||
db.session.delete(group) | ||
db.session.commit() | ||
return { | ||
"message": "Group deleted", | ||
"url": RESPONSE_URL | ||
}, 204 | ||
except SQLAlchemyError: | ||
return { | ||
"message": "Database error", | ||
"url": RESPONSE_URL | ||
}, 500 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
"""Group model""" | ||
from dataclasses import dataclass | ||
from sqlalchemy import Integer, Column, ForeignKey | ||
from project import db | ||
|
||
|
||
@dataclass | ||
class Group(db.Model): | ||
""" | ||
This class will contain the model for the groups | ||
""" | ||
__tablename__ = "groups" | ||
|
||
group_id: int = Column(Integer, autoincrement=True, primary_key=True) | ||
project_id: int = Column(Integer, ForeignKey( | ||
"projects.project_id"), autoincrement=False, primary_key=True) | ||
group_size: int = Column(Integer, nullable=False) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
"""Model for relation between groups and students""" | ||
from dataclasses import dataclass | ||
from sqlalchemy import Integer, Column, ForeignKey, String | ||
from project.db_in import db | ||
|
||
@dataclass | ||
class GroupStudent(db.Model): | ||
"""Model for relation between groups and students""" | ||
__tablename__ = "group_students" | ||
|
||
uid: str = Column(String(255), ForeignKey("users.uid"), primary_key=True) | ||
group_id: int = Column(Integer, ForeignKey("groups.group_id"), primary_key=True) | ||
project_id: int = Column(Integer, ForeignKey("groups.project_id"), primary_key=True) |
Oops, something went wrong.