diff --git a/BackEndFlask/controller/Routes/Completed_assessment_routes.py b/BackEndFlask/controller/Routes/Completed_assessment_routes.py index 1f664562f..b2ada7505 100644 --- a/BackEndFlask/controller/Routes/Completed_assessment_routes.py +++ b/BackEndFlask/controller/Routes/Completed_assessment_routes.py @@ -123,6 +123,26 @@ def get_all_completed_assessments(): except Exception as e: return create_bad_response(f"An error occurred retrieving all completed assessments: {e}", "completed_assessments", 400) +@bp.route('/completed_assessment', methods = ['GET']) +@jwt_required() +@bad_token_check() +@AuthCheck() +def get_completed_assessment_by_team_or_user_id(): + try: + completed_assessment_id = request.args.get("completed_assessment_id") + unit = request.args.get("unit") + if not completed_assessment_id: + return create_bad_response("No completed_assessment_id provided", "completed_assessments", 400) + + if unit == "team": + one_completed_assessment = get_completed_assessment_with_team_name(completed_assessment_id) + elif unit == "user": + one_completed_assessment = get_completed_assessment_with_user_name(completed_assessment_id) + else: + create_bad_response("Invalid unit provided", "completed_assessments", 400) + return create_good_response(completed_assessment_schema.dump(one_completed_assessment), 200, "completed_assessments") + except Exception as e: + return create_bad_response(f"An error occurred fetching a completed assessment: {e}", "completed_assessments", 400) @bp.route('/completed_assessment', methods = ['POST']) @jwt_required() diff --git a/BackEndFlask/controller/Routes/Team_routes.py b/BackEndFlask/controller/Routes/Team_routes.py index 95e187988..f8d8f4d11 100644 --- a/BackEndFlask/controller/Routes/Team_routes.py +++ b/BackEndFlask/controller/Routes/Team_routes.py @@ -8,8 +8,11 @@ get_team_by_course_id, create_team, get_teams_by_observer_id, - replace_team + replace_team, + delete_team ) +from models.assessment_task import get_assessment_tasks_by_team_id +from models.completed_assessment import completed_assessment_team_or_user_exists from models.team_user import * from controller.security.CustomDecorators import AuthCheck, bad_token_check from models.queries import ( @@ -94,14 +97,14 @@ def get_nonfull_adhoc_teams(): try: if request.args and request.args.get("assessment_task_id"): assessment_task_id = int(request.args.get("assessment_task_id")) - - valid_teams = [{"team_name": f"Team {team}", "team_id": team} for team in get_all_nonfull_adhoc_teams(assessment_task_id)] - - return create_good_response(valid_teams, 200, "teams") - + + valid_teams = [{"team_name": f"Team {team}", "team_id": team} for team in get_all_nonfull_adhoc_teams(assessment_task_id)] + + return create_good_response(valid_teams, 200, "teams") + except Exception as e: return create_bad_response(f"An error occurred getting nonfull adhoc teams {e}", "teams", 400) - + @bp.route('/team', methods = ['POST']) @jwt_required() @@ -167,6 +170,34 @@ def update_team_user_by_edit(): except Exception as e: return create_bad_response(f"An error occurred updating a team: {e}", "teams", 400) +@bp.route('/team', methods = ['DELETE']) +@jwt_required() +@bad_token_check() +@AuthCheck() +def delete_selected_teams(): + try: + if request.args and request.args.get("team_id"): + team_id = int(request.args.get("team_id")) + team = get_team(team_id) + if not team: + return create_bad_response("Team does not exist", "teams", 400) + + associated_tasks = completed_assessment_team_or_user_exists(team_id, user_id=None) + if associated_tasks is None: + associated_tasks = [] + if len(associated_tasks) > 0: + refetched_tasks = completed_assessment_team_or_user_exists(team_id, user_id=None) + if not refetched_tasks: + delete_team(team_id) + return create_good_response([], 200, "teams") + else: + return create_bad_response("Cannot delete team with associated tasks", "teams", 400) + else: + delete_team(team_id) + return create_good_response([], 200, "teams") + + except Exception as e: + return create_bad_response(f"An error occurred deleting a team: {e}", "teams", 400) class TeamSchema(ma.Schema): class Meta: diff --git a/BackEndFlask/models/assessment_task.py b/BackEndFlask/models/assessment_task.py index eca3cbde5..243e201df 100644 --- a/BackEndFlask/models/assessment_task.py +++ b/BackEndFlask/models/assessment_task.py @@ -63,7 +63,7 @@ def get_assessment_tasks_by_role_id(role_id): @error_log def get_assessment_tasks_by_team_id(team_id): - return db.session.query(AssessmentTask).join(Team, AssessmentTask.course_id == Team.course_id).filter( + db.session.query(AssessmentTask).join(Team, AssessmentTask.course_id == Team.course_id).filter( Team.team_id == team_id and ( @@ -72,7 +72,6 @@ def get_assessment_tasks_by_team_id(team_id): (AssessmentTask.due_date >= Team.date_created and AssessmentTask.due_date <= Team.active_until) ) ).all() - @error_log def get_assessment_task(assessment_task_id): one_assessment_task = AssessmentTask.query.filter_by(assessment_task_id=assessment_task_id).first() diff --git a/BackEndFlask/models/completed_assessment.py b/BackEndFlask/models/completed_assessment.py index 36644eeb5..bcd4fef93 100644 --- a/BackEndFlask/models/completed_assessment.py +++ b/BackEndFlask/models/completed_assessment.py @@ -53,6 +53,14 @@ def completed_assessment_exists(team_id, assessment_task_id, user_id): else: return CompletedAssessment.query.filter_by(user_id=user_id, assessment_task_id=assessment_task_id).first() +@error_log +def completed_assessment_team_or_user_exists(team_id, user_id): + if team_id is not None: + return CompletedAssessment.query.filter_by(team_id=team_id).all() + elif user_id is not None: + return CompletedAssessment.query.filter_by(user_id=user_id).all() + else: + return [] @error_log def create_completed_assessment(completed_assessment_data): diff --git a/FrontEndReact/src/View/Admin/View/ViewTeams/AdminViewTeams.js b/FrontEndReact/src/View/Admin/View/ViewTeams/AdminViewTeams.js index 14f5d98da..19869dfca 100644 --- a/FrontEndReact/src/View/Admin/View/ViewTeams/AdminViewTeams.js +++ b/FrontEndReact/src/View/Admin/View/ViewTeams/AdminViewTeams.js @@ -1,118 +1,152 @@ -import 'bootstrap/dist/css/bootstrap.css'; -import React, { Component } from 'react'; -import ErrorMessage from '../../../Error/ErrorMessage.js'; -import ViewTeams from './ViewTeams.js'; -import { genericResourceGET, parseUserNames } from '../../../../utility.js'; -import { Box, Button, Typography } from '@mui/material'; -import Loading from '../../../Loading/Loading.js'; -import SuccessMessage from '../../../Success/SuccessMessage.js'; - - +import "bootstrap/dist/css/bootstrap.css"; +import React, { Component } from "react"; +import ErrorMessage from "../../../Error/ErrorMessage.js"; +import ViewTeams from "./ViewTeams.js"; +import { genericResourceGET, + parseUserNames } from "../../../../utility.js"; +import { Box, Button, Typography } from "@mui/material"; +import Loading from "../../../Loading/Loading.js"; +import SuccessMessage from "../../../Success/SuccessMessage.js"; class AdminViewTeams extends Component { - constructor(props) { - super(props); - - this.state = { - errorMessage: null, - isLoaded: false, - teams: null, - users: null - } - } - - componentDidMount() { - var navbar = this.props.navbar; - var state = navbar.state; - var chosenCourse = state.chosenCourse; - - genericResourceGET(`/team?course_id=${chosenCourse["course_id"]}`, "teams", this); - - var url = ( - chosenCourse["use_tas"] ? - `/user?course_id=${chosenCourse["course_id"]}&role_id=4` : - `/user?uid=${chosenCourse["admin_id"]}` - ); - - genericResourceGET(url, "users", this); + constructor(props) { + super(props); + + this.state = { + errorMessage: null, + isLoaded: false, + teams: null, + users: null, + prevTeamsLength: 0, + successMessage: null + }; + } + + + fetchData = () => { + var navbar = this.props.navbar; + var state = navbar.state; + var chosenCourse = state.chosenCourse; + + genericResourceGET( + `/team?course_id=${chosenCourse["course_id"]}`, + "teams", + this, + ); + + var url = chosenCourse["use_tas"] + ? `/user?course_id=${chosenCourse["course_id"]}&role_id=4` + : `/user?uid=${chosenCourse["admin_id"]}`; + + genericResourceGET(url, "users", this); + }; + + componentDidMount() { + this.fetchData(); + } + + + componentDidUpdate(){ + if (this.state.teams && this.state.teams.length !== this.state.prevTeamsLength) { + this.setState({ prevTeamsLength: this.state.teams.length }); + this.fetchData(); } - - render() { - const { - errorMessage, - isLoaded, - teams, - users - } = this.state; - - var navbar = this.props.navbar; - - navbar.adminViewTeams.teams = teams; - navbar.adminViewTeams.users = users ? parseUserNames(users) : []; - - var setNewTab = navbar.setNewTab; - var setAddTeamTabWithUsers = navbar.setAddTeamTabWithUsers; - - if (errorMessage) { - return( -
Admin
: + return observerId === chosenCourse["admin_id"] ? ( +Admin
+ ) : ({users[observerId]}
- ) - } - } + ); + }, + }, }, { name: "date_created", label: "Date Created", options: { filter: true, - setCellHeaderProps: () => { return { width:"20%"}}, - setCellProps: () => { return { width:"20%"} }, + setCellHeaderProps: () => { + return { width: "20%" }; + }, + setCellProps: () => { + return { width: "20%" }; + }, customBodyRender: (date) => { var year = ""; var month = ""; var day = ""; - for(var dateIndex = 0; dateIndex < date.length; dateIndex++) { - if(date[dateIndex]!=='-') { - if(dateIndex >= 0 && dateIndex < 4) { - year += date[dateIndex]; - } + for (var dateIndex = 0; dateIndex < date.length; dateIndex++) { + if (date[dateIndex] !== "-") { + if (dateIndex >= 0 && dateIndex < 4) { + year += date[dateIndex]; + } - if(dateIndex === 5 || dateIndex === 6) { - month += date[dateIndex]; - } + if (dateIndex === 5 || dateIndex === 6) { + month += date[dateIndex]; + } - if(dateIndex > 6 && dateIndex < date.length) { - day += date[dateIndex]; - } + if (dateIndex > 6 && dateIndex < date.length) { + day += date[dateIndex]; } + } } - return( -{month+'/'+day+'/'+year}
- ) - } - } + return{month + "/" + day + "/" + year}
; + }, + }, }, { name: "team_id", @@ -83,22 +116,73 @@ class ViewTeams extends Component{ options: { filter: false, sort: false, - setCellHeaderProps: () => { return { align:"center", width:"10%", className:"button-column-alignment"}}, - setCellProps: () => { return { align:"center", width:"10%", className:"button-column-alignment"} }, + setCellHeaderProps: () => { + return { + align: "center", + width: "10%", + className: "button-column-alignment", + }; + }, + setCellProps: () => { + return { + align: "center", + width: "10%", + className: "button-column-alignment", + }; + }, + customBodyRender: (teamId) => { + return ( +