CSC/ECE 517 Fall 2023 - E2373. Reimplementation of teams controller
Project Overview
Expertiza Background
Expertiza is an online platform designed to facilitate peer review and submission of various types of academic work, including articles, codes, and websites. The application allows instructors to add assignments, grade them, and assign students to teams based on their topic preferences. The platform also enables students to provide feedback on each other's work through peer reviews, which can be helpful in improving the quality of their projects. Expertiza is backed by the National Science Foundation.
Problem Statement
The teams_controller.rb file contains operations on teams like listing of teams of an assignment/course, updating a team, deleting teams of an assignment/course, copying teams between two assignments or courses among others. The model team.rb contains a lot of functions, with probable violations of single responsibility principle and this needs to be reimplemented with care. The files do not benefit from the complex names the functions are given.
What needs to be done:
Emphasize on Single Responsibility Principle - There are a questionable amount of functions that are directed at copying teams and team members between parents (courses/assignments). There are a lot of class functions in the model, which could be bad design. While reimplementing, make sure that SRP is maintained. See if any code is repetitive, and can be cut down.
Improve naming - Some functions have confusing names. For example, the function “bequeath_copy” can definitely be given a better name if the functionality needs to be reimplemented. The over-populated “copy” related functions in this controller decrease the readability of the code.
Better comments - Bigger files and functions require better comments for readability.
Class Diagram
Functionality
In this project, we aim to:
- Interact with teams assignment database (Basic CRUD) - Interactions with a particular team assignment
Project Design
Model
This model is used to manage teams and their assignments. Here is a brief description of its key features:
Associations: It has various associations with other models, including users, join_team_requests, team_node, signed_up_teams, and bids.
CSV Import and Export: It contains methods for importing team data from a CSV file and exporting team data to a CSV file. The import method creates or finds teams and associates users with them based on the CSV data.
Team Creation and Management: It includes methods to create a new team and add or remove users from teams. It checks for existing team membership before adding a user to a team.
Team Name Generation: There is a method to generate a unique team name, which can be prefixed with a provided string.
Version History: The model uses the has_paper_trail gem, which helps in tracking changes to the model's records over time.
Code snippet can be found here:
class TeamsAssignment < ApplicationRecord
require 'csv' # Require the 'csv' library has_many :users, through: :teams_users has_many :join_team_requests, dependent: :destroy has_one :team_node, foreign_key: :node_object_id, dependent: :destroy has_many :signed_up_teams, dependent: :destroy has_many :bids, dependent: :destroy has_paper_trail
# Import teams from a CSV file def self.import_teams_from_csv(file) CSV.foreach(file.path, headers: true) do |row| team = self.find_or_create_by(name: row['name']) team.users << User.find_by(name: row['user_name']) end end
def self.export_teams_to_csv(file) CSV.open(file.path, 'w') do |csv| csv << ['name', 'user_name'] # Add relevant column names
all.each do |team| team.users.each do |user| csv << [team.name, user.name] end end end end
# Method to create a new team def create_team(name) team = Team.create(name: name, parent_id: self.id) TeamNode.create(parent_id: self.id, node_object_id: team.id) team end
# Method to add a user to a team def add_user_to_team(team, user) # Check if the user is already a member of the team return if team.users.include?(user)
TeamsUser.create(team_id: team.id, user_id: user.id) # Optionally, you can add this user to any related associations, like CourseParticipants or AssignmentParticipants end
# Method to remove a user from a team def remove_user_from_team(team, user) team_user = TeamsUser.find_by(team_id: team.id, user_id: user.id) team_user&.destroy end
# Generate the team name def self.generate_team_name(team_name_prefix = ) counter = 1 loop do team_name = "#{team_name_prefix} Team_#{counter}" return team_name unless TeamsAssignment.find_by(name: team_name)
counter += 1 end end
end
Schema
create_table "teams_assignments", id: :integer, force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1" do |t|
t.string "name" t.integer "parent_id" t.string "type" t.text "comments_for_advertisement" t.boolean "advertise_for_partner" t.text "submitted_hyperlinks" t.integer "directory_num" t.integer "grade_for_submission" t.text "comment_for_submission" t.integer "pair_programming_request", limit: 1 end
Teams controller
Api::V1::TeamsAssignmentController, which is responsible for handling HTTP requests related to a model named TeamAssignment. Here is a brief description of its key features:
Before Actions: The controller specifies before actions using the before_action method. It sets up the @team_assignment instance variable for specific controller actions (show, update, destroy, and copy). It also uses rescue_from to handle exceptions.
Index Action: The index action handles a GET request to list all team assignments. It retrieves all team assignments from the database and renders them as JSON.
Show Action: The show action handles a GET request to retrieve a specific team assignment by its ID. It renders the team assignment as JSON.
Create Action: The create action handles a POST request to create a new team assignment. It creates a new TeamAssignment instance with parameters from the request and saves it. It returns a JSON response with the created team assignment or error messages.
Update Action: The update action handles a PUT or PATCH request to update an existing team assignment. It finds the team assignment, updates it with parameters from the request, and returns a JSON response with the updated team assignment or error messages.
Destroy Action: The destroy action handles a DELETE request to delete a specific team assignment by its ID. It removes the team assignment from the database and returns a JSON response indicating the success of the deletion.
Copy Action: The copy action handles a custom action to create a copy of a team assignment. It calls a method copy_team_assignment on the team assignment and returns a JSON response indicating the success or failure of the copy operation.
Private Methods: There are several private methods used to encapsulate common functionality. set_team_assignment finds and sets the @team_assignment instance variable, team_assignment_params permits trusted parameters for creating or updating team assignments, and team_assignment_not_found and parameter_missing handle specific error cases.
This controller provides a RESTful API for managing team assignments, including listing, creating, updating, and deleting team assignments, as well as a custom copy operation. It also includes error handling for common scenarios.
Code snippet:
class Api::V1::TeamsAssignmentController < ApplicationController
before_action :set_paper_trail_whodunnit before_action :set_team_assignment, only: %i[ show update destroy copy ] rescue_from ActiveRecord::RecordNotFound, with: :team_assignment_not_found rescue_from ActionController::ParameterMissing, with: :parameter_missing
# GET /team_assignments # List all the team_assignments def index team_assignment = TeamAssignment.all render json: team_assignment, status: :ok end
# GET /team_assignments/1 # Get a team_assignment def show render json: @team_assignment, status: :ok end
# POST /team_assignments # Create a team_assignment def create team_assignment = TeamAssignment.new(team_assignment_params) if team_assignment.save render json: team_assignment, status: :created else render json: team_assignment.errors, status: :unprocessable_entity end end
# PATCH/PUT /team_assignments/1 # Update a team_assignment def update if @team_assignment.update(team_assignment_params) render json: @team_assignment, status: :ok else render json: @team_assignment.errors, status: :unprocessable_entity end end
# DELETE /team_assignments/1 # Delete a team_assignment def destroy @team_assignment.destroy render json: { message: "Team assignment with id #{params[:id]}, deleted" }, status: :no_content end
# Creates a copy of the team_assignment def copy if @team_assignment.copy_team_assignment render json: { message: "The team assignment #{@team_assignment.name} has been successfully copied" }, status: :ok else render json: { message: "The team assignment was not able to be copied" }, status: :unprocessable_entity end end
private
# Use callbacks to share common setup or constraints between actions. def set_team_assignment @team_assignment = TeamAssignment.find(params[:id]) end
# Only allow a list of trusted parameters through. def team_assignment_params params.require(:team_assignment).permit( :users, :join_team_requests, :team_node, :signed_up_teams, :bids ) end
def team_assignment_not_found render json: { error: "Team assignment with id #{params[:id]} not found" }, status: :not_found end
def parameter_missing render json: { error: "Parameter missing" }, status: :unprocessable_entity end
end
Testing Plan
Model tests
Factory used:
factory :teams_assignment do name { "Sample Team Assignment" } parent_id { 1 } # You can adjust this value as needed type { "Assignment" } comments_for_advertisement { "Sample comments for advertisement" } advertise_for_partner { true } submitted_hyperlinks { "Sample hyperlinks" } directory_num { 1 } grade_for_submission { 100 } comment_for_submission { "Sample comment for submission" } pair_programming_request { 1 } end
To run model tests:
1. git clone
2. cd reimplementation-back-end/
3. bundle install
4. bundle exec rspec spec/models/teams_assignment_spec.rb
To run controller tests:
1. git clone
2. cd reimplementation-back-end/
3. bundle install
4. 4. bundle exec rspec spec/requests/api/v1/teams_assignment_controller_spec.rb
References
Github repo link[1]
Rspec documentation[2]
Github pull request[3]
Team members
1. Reuben Vandezande
2. Nitin Joseph Madapally Abraham
Mentor
Renji Joseph Sabu