CSC/ECE 517 Spring 2025 - E2511. Reimplement participants controller.rb: Difference between revisions
| Line 231: | Line 231: | ||
default_permissions.merge(permissions_map[authorization]) | default_permissions.merge(permissions_map[authorization]) | ||
end | end | ||
class Api::V1::ParticipantsController < ApplicationController | |||
include ParticipantsHelper | |||
# autocomplete :user, :name | |||
def index | |||
participants = Participant.all | |||
render json: participants | |||
end | |||
# ************************************************************ | |||
#keeping this commented as to test | |||
# def action_allowed? | |||
# if %w[change_handle update_duties].include? params[:action] | |||
# current_user_has_student_privileges? | |||
# else | |||
# current_user_has_ta_privileges? | |||
# end | |||
# end | |||
# ************************************************************ | |||
#redundant by may be required later | |||
# def action_allowed? | |||
# has_required_role?('Teaching Assistant') | |||
# end | |||
# ************************************************************ | |||
def create | |||
@participant = Participant.new(participant_params) | |||
if @participant.save | |||
render json: @participant, status: :created | |||
else | |||
render json: { errors: @participant.errors.full_messages }, status: :unprocessable_entity | |||
end | |||
end | |||
before_action :set_participant, only: [:show, :update, :destroy] | |||
# GET /api/v1/participants/:id | |||
def show | |||
render json: @participant | |||
end | |||
#getting the user by user_index and retrive and how on swagger ui | |||
def user_index | |||
participants = Participant.where(user_id: params[:user_id]) | |||
render json: participants, status: :ok | |||
end | |||
#updating the participant by request body of example { "can_submit": true, "can_review": true} | |||
def update | |||
if @participant.update(participant_params) | |||
render json: @participant, status: :ok | |||
else | |||
render json: { errors: @participant.errors.full_messages }, status: :unprocessable_entity | |||
end | |||
end | |||
#destroying the user by the id of the specific user | |||
def destroy | |||
participant = Participant.find(params[:id]) | |||
participant.destroy | |||
render json: { message: "Participant deleted successfully" }, status: :ok | |||
rescue ActiveRecord::RecordNotFound | |||
render json: { error: "Participant not found" }, status: :not_found | |||
end | |||
#finding partcipant by assignment id | |||
def assignment_index | |||
participants = Participant.where(assignment_id: params[:assignment_id]) | |||
render json: participants, status: :ok | |||
end | |||
# def add | |||
# handle = "#{user.name.parameterize}-#{SecureRandom.hex(2)}" | |||
# assignment_id = params[:id] | |||
# authorization = params[:authorization] | |||
# user_name = params[:user][:name] | |||
# user = User.find_by(name: user_name) | |||
# return render json: { error: "User not found" }, status: :not_found if user.nil? | |||
# assignment = Assignment.find(assignment_id) | |||
# permissions = participant_permissions(authorization) | |||
# participant = assignment.participants.create( | |||
# user: user, | |||
# handle: handle, | |||
# can_submit: permissions[:can_submit], | |||
# can_review: permissions[:can_review], | |||
# can_take_quiz: permissions[:can_take_quiz], | |||
# can_mentor: permissions[:can_mentor] | |||
# ) | |||
# if participant.persisted? | |||
# render json: participant, status: :created | |||
# else | |||
# render json: { errors: participant.errors.full_messages }, status: :unprocessable_entity | |||
# end | |||
# end | |||
#adding a participant with authorization | |||
def add | |||
assignment = Assignment.find(params[:id]) | |||
user = User.find_or_create_by(user_params) # 👈 You were probably missing this line | |||
# Now you can safely use `user` below | |||
handle = "#{user.name.parameterize}-#{SecureRandom.hex(2)}" | |||
permissions = participant_permissions(params[:authorization]) | |||
participant = assignment.participants.create!( | |||
user: user, | |||
handle: handle, | |||
can_submit: permissions[:can_submit], | |||
can_review: permissions[:can_review], | |||
can_take_quiz: permissions[:can_take_quiz], | |||
can_mentor: permissions[:can_mentor] | |||
) | |||
render json: participant, status: :created | |||
end | |||
#updating authorization of the participants | |||
def update_authorization | |||
participant = Participant.find(params[:id]) | |||
permissions = participant_permissions(params[:authorization]) | |||
participant.update!( | |||
can_submit: permissions[:can_submit], | |||
can_review: permissions[:can_review], | |||
can_take_quiz: permissions[:can_take_quiz], | |||
can_mentor: permissions[:can_mentor] | |||
) | |||
render json: participant, status: :ok | |||
end | |||
private | |||
def user_params | |||
params.require(:user).permit(:name) | |||
end | |||
def participant_permissions(role) | |||
case role | |||
when 'Student' | |||
{ can_submit: true, can_review: false, can_take_quiz: false, can_mentor: false } | |||
when 'Reviewer' | |||
{ can_submit: false, can_review: true, can_take_quiz: false, can_mentor: false } | |||
when 'Teaching Assistant' | |||
{ can_submit: false, can_review: true, can_take_quiz: false, can_mentor: true } | |||
when 'Mentor' | |||
{ can_submit: false, can_review: false, can_take_quiz: false, can_mentor: true } | |||
else | |||
{ can_submit: false, can_review: false, can_take_quiz: false, can_mentor: false } | |||
end | |||
end | |||
def set_participant | |||
@participant = Participant.find(params[:id]) | |||
rescue ActiveRecord::RecordNotFound | |||
render json: { error: "Participant not found" }, status: 404 | |||
end | |||
def participant_params | |||
params.require(:participant).permit(:user_id, :assignment_id) | |||
end | |||
# ***************************************************** | |||
def controller_locale | |||
locale_for_student | |||
end | |||
# def list | |||
# if Participant::PARTICIPANT_TYPES.include? params[:model] | |||
# @root_node = Object.const_get(params[:model] + 'Node').find_by(node_object_id: params[:id]) | |||
# @parent = Object.const_get(params[:model]).find(params[:id]) | |||
# end | |||
# begin | |||
# @participants = @parent.participants | |||
# @model = params[:model] | |||
# @authorization = params[:authorization] | |||
# rescue StandardError | |||
# flash[:error] = $ERROR_INFO | |||
# end | |||
# end | |||
# def add | |||
# curr_object = Object.const_get(params[:model]).find(params[:id]) if Participant::PARTICIPANT_TYPES.include? params[:model] | |||
# begin | |||
# permissions = participant_permissions(params[:authorization]) | |||
# can_submit = permissions[:can_submit] | |||
# can_review = permissions[:can_review] | |||
# can_take_quiz = permissions[:can_take_quiz] | |||
# #E2351 - add corresponding duty fill from permissions | |||
# can_mentor = permissions[:can_mentor] | |||
# if curr_object.is_a?(Assignment) | |||
# curr_object.add_participant(params[:user][:name], can_submit, can_review, can_take_quiz, can_mentor) | |||
# elsif curr_object.is_a?(Course) | |||
# curr_object.add_participant(params[:user][:name]) | |||
# end | |||
# user = User.find_by(name: params[:user][:name]) | |||
# @model = params[:model] | |||
# @participant = curr_object.participants.find_by(user_id: user.id) | |||
# flash.now[:note] = "The user <b>#{params[:user][:name]}</b> has successfully been added." | |||
# rescue StandardError | |||
# url_for controller: 'users', action: 'new' | |||
# flash.now[:error] = "The user <b>#{params[:user][:name]}</b> does not exist or has already been added." | |||
# end | |||
# render action: 'add.js.erb', layout: false | |||
# end | |||
# when you change the duties, changes the permissions based on the new duty you go to | |||
def update_authorizations | |||
participant = Participant.find(params[:id]) | |||
permissions = participant_permissions(params[:authorization]) | |||
can_submit = permissions[:can_submit] | |||
can_review = permissions[:can_review] | |||
can_take_quiz = permissions[:can_take_quiz] | |||
can_mentor = permissions[:can_mentor] | |||
parent_id = participant.parent_id | |||
# Upon successfully updating the attributes based on user role, a flash message is displayed to the user after the | |||
# change in the database. This also gives the user the error message if the update fails. | |||
begin | |||
participant.update_attributes(can_submit: can_submit, can_review: can_review, can_take_quiz: can_take_quiz, can_mentor: can_mentor) | |||
flash[:success] = 'The role of the selected participants has been successfully updated.' | |||
rescue StandardError | |||
flash[:error] = 'The update action failed.' | |||
end | |||
redirect_to action: 'list', id: parent_id, model: participant.class.to_s.gsub('Participant', '') | |||
end | |||
# def destroy | |||
# participant = Participant.find(params[:id]) | |||
# parent_id = participant.parent_id | |||
# begin | |||
# participant.destroy | |||
# flash[:note] = undo_link("The user \"#{participant.user.name}\" has been successfully removed as a participant.") | |||
# rescue StandardError | |||
# flash[:error] = 'This participant is on a team, or is assigned as a reviewer for someone’s work.' | |||
# end | |||
# redirect_to action: 'list', id: parent_id, model: participant.class.to_s.gsub('Participant', '') | |||
# end | |||
# Copies existing participants from a course down to an assignment | |||
def inherit | |||
assignment = Assignment.find(params[:id]) | |||
course = assignment.course | |||
@copied_participants = [] | |||
if course | |||
participants = course.participants | |||
if !participants.empty? | |||
participants.each do |participant| | |||
new_participant = participant.copy(params[:id]) | |||
@copied_participants.push new_participant if new_participant | |||
end | |||
# Only display undo link if copies of participants are created | |||
if !@copied_participants.empty? | |||
undo_link("The participants from \"#{course.name}\" have been successfully copied to this assignment. ") | |||
else | |||
flash[:note] = 'All course participants are already in this assignment' | |||
end | |||
else | |||
flash[:note] = 'No participants were found to inherit this assignment.' | |||
end | |||
else | |||
flash[:error] = 'No course was found for this assignment.' | |||
end | |||
redirect_to controller: 'participants', action: 'list', id: assignment.id, model: 'Assignment' | |||
end | |||
# Take all participants from an assignment and "bequeath" them to course as course_participants. | |||
def bequeath_all | |||
@copied_participants = [] | |||
assignment = Assignment.find(params[:id]) | |||
if assignment.course | |||
course = assignment.course | |||
assignment.participants.each do |participant| | |||
new_participant = participant.copy_to_course(course.id) | |||
@copied_participants.push new_participant if new_participant | |||
end | |||
# only display undo link if copies of participants are created | |||
if !@copied_participants.empty? | |||
undo_link("All participants were successfully copied to \"#{course.name}\". ") | |||
else | |||
flash[:note] = 'All assignment participants are already part of the course' | |||
end | |||
else | |||
flash[:error] = 'This assignment is not associated with a course.' | |||
end | |||
redirect_to controller: 'participants', action: 'list', id: assignment.id, model: 'Assignment' | |||
end | |||
# Allow participant to change handle for this assignment | |||
# If the participant parameters are available, update the participant | |||
# and redirect to the view_actions page | |||
def change_handle | |||
@participant = AssignmentParticipant.find(params[:id]) | |||
return unless current_user_id?(@participant.user_id) | |||
unless params[:participant].nil? | |||
if !AssignmentParticipant.where(parent_id: @participant.parent_id, handle: params[:participant][:handle]).empty? | |||
ExpertizaLogger.error LoggerMessage.new(controller_name, @participant.name, "Handle #{params[:participant][:handle]} already in use", request) | |||
flash[:error] = "<b>The handle #{params[:participant][:handle]}</b> is already in use for this assignment. Please select a different one." | |||
redirect_to controller: 'participants', action: 'change_handle', id: @participant | |||
else | |||
@participant.update_attributes(participant_params) | |||
ExpertizaLogger.info LoggerMessage.new(controller_name, @participant.name, 'The change handle is saved successfully', request) | |||
redirect_to controller: 'student_task', action: 'view', id: @participant | |||
end | |||
end | |||
end | |||
# Deletes participants from an assignment | |||
def delete | |||
contributor = AssignmentParticipant.find(params[:id]) | |||
name = contributor.name | |||
assignment_id = contributor.assignment | |||
begin | |||
contributor.destroy | |||
flash[:note] = "\"#{name}\" is no longer a participant in this assignment." | |||
rescue StandardError | |||
flash[:error] = "\"#{name}\" was not removed from this assignment. Please ensure that \"#{name}\" is not a reviewer or metareviewer and try again." | |||
end | |||
redirect_to controller: 'review_mapping', action: 'list_mappings', id: assignment_id | |||
end | |||
# A ‘copyright grant’ means the author has given permission to the instructor to use the work outside the course. | |||
# This is incompletely implemented, but the values in the last column in http://expertiza.ncsu.edu/student_task/list are sourced from here. | |||
def view_copyright_grants | |||
assignment_id = params[:id] | |||
assignment = Assignment.find(assignment_id) | |||
@assignment_name = assignment.name | |||
@has_topics = false | |||
@teams_info = [] | |||
teams = Team.where(parent_id: assignment_id) | |||
teams.each do |team| | |||
team_info = {} | |||
team_info[:name] = team.name(session[:ip]) | |||
users = [] | |||
team.users { |team_user| users.append(get_user_info(team_user, assignment)) } | |||
team_info[:users] = users | |||
@has_topics = get_signup_topics_for_assignment(assignment_id, team_info, team.id) | |||
team_without_topic = SignedUpTeam.where('team_id = ?', team.id).none? | |||
next if @has_topics && team_without_topic | |||
@teams_info.append(team_info) | |||
end | |||
@teams_info = @teams_info.sort_by { |hashmap| [hashmap[:topic_id] ? 0 : 1, hashmap[:topic_id] || 0] } | |||
end | |||
private | |||
def participant_params | |||
params.require(:participant).permit(:can_submit, :can_review, :user_id, :parent_id, :submitted_at, | |||
:permission_granted, :penalty_accumulated, :grade, :type, :handle, | |||
:time_stamp, :digital_signature, :can_mentor, :can_take_quiz) | |||
end | |||
# Get the user info from the team user | |||
def get_user_info(team_user, assignment) | |||
user = {} | |||
user[:name] = team_user.name | |||
user[:fullname] = team_user.fullname | |||
# set by default | |||
permission_granted = false | |||
assignment.participants.each do |participant| | |||
permission_granted = participant.permission_granted? if team_user.id == participant.user.id | |||
end | |||
# If permission is granted, set the publisting rights string | |||
user[:pub_rights] = permission_granted ? 'Granted' : 'Denied' | |||
user[:verified] = false | |||
user | |||
end | |||
# Get the signup topics for the assignment | |||
def get_signup_topics_for_assignment(assignment_id, team_info, team_id) | |||
signup_topics = SignUpTopic.where('assignment_id = ?', assignment_id) | |||
if signup_topics.any? | |||
has_topics = true | |||
signup_topics.each do |signup_topic| | |||
signup_topic.signed_up_teams.each do |signed_up_team| | |||
if signed_up_team.team_id == team_id | |||
team_info[:topic_name] = signup_topic.topic_name | |||
team_info[:topic_id] = signup_topic.topic_identifier.to_i | |||
end | |||
end | |||
end | |||
end | |||
has_topics | |||
end | |||
end | |||
# *************************************************************************************************** | |||
# class Api::V1::ParticipantsController < ApplicationController | |||
# before_action :set_participant, only: [:show, :update, :destroy, :update_authorization] | |||
# def index | |||
# participants = Participant.all | |||
# render json: participants, status: :ok | |||
# end | |||
# def show | |||
# render json: @participant, status: :ok | |||
# end | |||
# def create | |||
# participant = Participant.new(participant_params) | |||
# if participant.save | |||
# render json: participant, status: :created | |||
# else | |||
# render json: { errors: participant.errors.full_messages }, status: :unprocessable_entity | |||
# end | |||
# end | |||
# def update | |||
# if @participant.update(participant_params) | |||
# render json: @participant, status: :ok | |||
# else | |||
# render json: { errors: @participant.errors.full_messages }, status: :unprocessable_entity | |||
# end | |||
# end | |||
# def destroy | |||
# @participant.destroy | |||
# render json: { message: "Participant deleted successfully" }, status: :no_content | |||
# end | |||
# def user_index | |||
# participants = Participant.where(user_id: params[:user_id]) | |||
# render json: participants, status: :ok | |||
# end | |||
# def assignment_index | |||
# participants = Participant.where(assignment_id: params[:assignment_id]) | |||
# render json: participants, status: :ok | |||
# end | |||
# def add | |||
# user = User.find_by(name: params[:user][:name]) | |||
# if user.nil? | |||
# render json: { error: "User not found" }, status: :not_found | |||
# return | |||
# end | |||
# assignment = Assignment.find(params[:id]) | |||
# participant = assignment.participants.create(user: user) | |||
# if participant.persisted? | |||
# render json: participant, status: :created | |||
# else | |||
# render json: { errors: participant.errors.full_messages }, status: :unprocessable_entity | |||
# end | |||
# end | |||
# def update_authorization | |||
# permissions = participant_permissions(params[:authorization]) | |||
# if @participant.update(permissions) | |||
# render json: @participant, status: :ok | |||
# else | |||
# render json: { errors: @participant.errors.full_messages }, status: :unprocessable_entity | |||
# end | |||
# end | |||
# private | |||
# def set_participant | |||
# @participant = Participant.find_by(id: params[:id]) | |||
# render json: { error: "Participant not found" }, status: :not_found if @participant.nil? | |||
# end | |||
# def participant_params | |||
# params.require(:participant).permit(:user_id, :assignment_id, :can_submit, :can_review, :can_take_quiz) | |||
# end | |||
# end | |||
== Role-Based Strategy Pattern == | == Role-Based Strategy Pattern == | ||
Revision as of 00:04, 25 March 2025
About Expertiza
Expertiza is an open source project based on Ruby on Rails framework. Expertiza allows the instructor to create new assignments and customize new or existing assignments. It also allows the instructor to create a list of topics the students can sign up for. Students can form teams in Expertiza to work on various projects and assignments. Students can also peer review other students' submissions. Expertiza supports submission across various document types, including the URLs and wiki pages.
Introduction
This project aims to reimplement the participants_controller.rb and participants_helper.rb in the new Expertiza system. The participants_controller.rb manages participants within assignments, and this reimplementation focuses on streamlining its methods, eliminating redundancy, and enhancing code readability and maintainability.
Requirements
- Implement
participants_controller.rbMethods: Fully implement each controller method with the same functionality as the current controller. - Import from
participants_helper.rb: Integrate relevant helper methods to streamline functionality and remove redundancy. - API Creation: Expose API endpoints for all necessary methods to enable seamless access and integration from the frontend. Ensure that each endpoint follows best practices for RESTful API design, including proper error handling and secure data access.
- Testing with rswag: Create test cases for all methods, focusing on edge cases, and generate rswag documentation.
- Swagger UI Video Documentation: Record a video walkthrough of API endpoints in Swagger UI, showcasing each method’s functionality.
Tasks to be Completed
- Implement participants_controller.rb Methods: Develop each method with the same functionality as the existing controller.
- Refactor and Integrate Helper Methods: Import relevant methods from participants_helper.rb to improve efficiency and eliminate code duplication.
- API Development: Expose RESTful API endpoints for necessary operations, ensuring proper error handling and secure data access.
- Testing with Rswag: Implement comprehensive test cases, including edge cases, and generate Rswag documentation.
- Swagger UI Documentation: Create a video walkthrough demonstrating API endpoints and their functionalities in Swagger UI.
- Make sure to write intuitive test cases for this controller using RSpec
Reimplement participants_controller.rb
Previous participants_controller.rb Methods / API Calls
| # | Method | Endpoint | Description |
|---|---|---|---|
| 1 | user_index | GET /participants/user/:user_id
|
Return a list of participants for a given user |
| 2 | assignment_index | GET /participants/assignment/:assignment_id
|
Return a list of participants for a given assignment |
| 3 | show | GET /participants/:id
|
Return a specified participant |
| 4 | add | POST /participants/:authorization
|
Assign the specified authorization to the participant and add them to an assignment |
| 5 | update_authorization | PATCH /participants/:id/:authorization
|
Update the specified participant to the specified authorization |
| 6 | destroy | DELETE /participants/:id
|
Delete a participant |
| 7 | participant_params | - | Permitted parameters for creating a Participant object |
participants_controller.rb Methods / API Calls
| # | HTTP Method | Endpoint | Method Name | Description |
|---|---|---|---|---|
| 1 | GET | /api/v1/participants | index | List all participants |
| 2 | POST | /api/v1/participants | create | Create a new participant |
| 3 | GET | /api/v1/participants/:id | show | Retrieve a participant by ID |
| 4 | PUT | /api/v1/participants/:id | update | Update a participant |
| 5 | DELETE | /api/v1/participants/:id | destroy | Delete a participant |
| 6 | GET | /api/v1/participants/user/:user_id | user_index | Get participants by user ID |
| 7 | GET | /api/v1/participants/assignment/:assignment_id | assignment_index | Get participants by assignment ID |
| 8 | POST | /api/v1/participants/:id/:authorization | add | Add a participant with authorization |
| 9 | PATCH | /api/v1/participants/:id/:authorization | update_authorization | Update participant authorization |
Removed Methods from participants_controller.rb
The following methods were removed as part of the reimplementation:
action_allowed?– No impact on functionality.controller_locale– No impact on functionality.inherit– Redundant due to lack of direct relation between Course and Participant.bequeath_all– Same reason as above.change_handle– `User` model already has `handle`, so it's redundant.delete– Replaced by `remove_participant` inassignments_controller.rb.view_copyright_grants– Old implementation was incorrect.get_user_info– Covered by `belongs_to` relation in the `Participant` model.get_signup_topics_for_assignment– Now handled by `sign_up_topics_controller.rb`.
Implementation Summary
Api::V1::ParticipantsController was restructured to:
- Follow RESTful conventions (`index`, `show`, `create`, `update`, `destroy`)
- Add custom endpoints for `user_index`, `assignment_index`, and `update_authorization`
- Leverage strong parameters and `before_action` filters
- Integrate logic from `participants_helper.rb` (like `participant_permissions`)
- Improve error handling using status codes and JSON messages
participants_controller.rb Methods
The following custom methods were added to handle participant creation with role-based authorization and to update existing participant permissions based on a new authorization.
Method: add
```ruby
#adding a participant with authorization
def add
assignment = Assignment.find(params[:id])
user = User.find_or_create_by(user_params)
# Now you can safely use `user` below
handle = "#{user.name.parameterize}-#{SecureRandom.hex(2)}"
permissions = participant_permissions(params[:authorization])
participant = assignment.participants.create!(
user: user,
handle: handle,
can_submit: permissions[:can_submit],
can_review: permissions[:can_review],
can_take_quiz: permissions[:can_take_quiz],
can_mentor: permissions[:can_mentor]
)
render json: participant, status: :created
end
Method: update_authorization
def update_authorization
participant = Participant.find(params[:id])
permissions = participant_permissions(params[:authorization])
participant.update!(
can_submit: permissions[:can_submit],
can_review: permissions[:can_review],
can_take_quiz: permissions[:can_take_quiz],
can_mentor: permissions[:can_mentor]
)
render json: participant, status: :ok
end
participants_helper.rb Methods
The ParticipantsHelper module contains utility methods used to assign roles and permissions to a Participant. Compared to the original implementation, this version has been heavily simplified and cleaned up.
Only the following method is included in the reimplementation:
retrieve_participant_permissions– Returns a hash of permission flags based on the participant’s assigned role.
This method sets default permissions (submit, review, quiz, mentor) and overrides them based on the role passed in. The supported roles are: *reader*reviewer*submitter*mentor
```ruby
def retrieve_participant_permissions(authorization)
default_permissions = {
can_submit: true,
can_review: true,
can_take_quiz: true,
can_mentor: false
}
permissions_map = {
'reader' => { can_submit: false },
'reviewer' => { can_submit: false, can_take_quiz: false },
'submitter' => { can_review: false, can_take_quiz: false },
'mentor' => { can_mentor: true }
}
default_permissions.merge(permissions_map[authorization]) end
class Api::V1::ParticipantsController < ApplicationController
include ParticipantsHelper
# autocomplete :user, :name
def index
participants = Participant.all
render json: participants
end
# ************************************************************
#keeping this commented as to test
# def action_allowed?
# if %w[change_handle update_duties].include? params[:action]
# current_user_has_student_privileges?
# else
# current_user_has_ta_privileges?
# end
# end
# ************************************************************
#redundant by may be required later
# def action_allowed?
# has_required_role?('Teaching Assistant')
# end
# ************************************************************
def create
@participant = Participant.new(participant_params)
if @participant.save
render json: @participant, status: :created
else
render json: { errors: @participant.errors.full_messages }, status: :unprocessable_entity
end
end
before_action :set_participant, only: [:show, :update, :destroy]
# GET /api/v1/participants/:id def show render json: @participant end
#getting the user by user_index and retrive and how on swagger ui def user_index participants = Participant.where(user_id: params[:user_id]) render json: participants, status: :ok end
#updating the participant by request body of example { "can_submit": true, "can_review": true}
def update
if @participant.update(participant_params)
render json: @participant, status: :ok
else
render json: { errors: @participant.errors.full_messages }, status: :unprocessable_entity
end
end
#destroying the user by the id of the specific user
def destroy
participant = Participant.find(params[:id])
participant.destroy
render json: { message: "Participant deleted successfully" }, status: :ok
rescue ActiveRecord::RecordNotFound
render json: { error: "Participant not found" }, status: :not_found
end
#finding partcipant by assignment id def assignment_index participants = Participant.where(assignment_id: params[:assignment_id]) render json: participants, status: :ok end
# def add
# handle = "#{user.name.parameterize}-#{SecureRandom.hex(2)}"
# assignment_id = params[:id]
# authorization = params[:authorization]
# user_name = params[:user][:name]
# user = User.find_by(name: user_name)
# return render json: { error: "User not found" }, status: :not_found if user.nil?
# assignment = Assignment.find(assignment_id)
# permissions = participant_permissions(authorization)
# participant = assignment.participants.create(
# user: user,
# handle: handle,
# can_submit: permissions[:can_submit],
# can_review: permissions[:can_review],
# can_take_quiz: permissions[:can_take_quiz],
# can_mentor: permissions[:can_mentor]
# )
# if participant.persisted?
# render json: participant, status: :created
# else
# render json: { errors: participant.errors.full_messages }, status: :unprocessable_entity
# end
# end
#adding a participant with authorization
def add
assignment = Assignment.find(params[:id])
user = User.find_or_create_by(user_params) # 👈 You were probably missing this line
# Now you can safely use `user` below
handle = "#{user.name.parameterize}-#{SecureRandom.hex(2)}"
permissions = participant_permissions(params[:authorization])
participant = assignment.participants.create!(
user: user,
handle: handle,
can_submit: permissions[:can_submit],
can_review: permissions[:can_review],
can_take_quiz: permissions[:can_take_quiz],
can_mentor: permissions[:can_mentor]
)
render json: participant, status: :created
end
#updating authorization of the participants
def update_authorization
participant = Participant.find(params[:id])
permissions = participant_permissions(params[:authorization])
participant.update!(
can_submit: permissions[:can_submit],
can_review: permissions[:can_review],
can_take_quiz: permissions[:can_take_quiz],
can_mentor: permissions[:can_mentor]
)
render json: participant, status: :ok
end
private
def user_params params.require(:user).permit(:name) end
def participant_permissions(role)
case role
when 'Student'
{ can_submit: true, can_review: false, can_take_quiz: false, can_mentor: false }
when 'Reviewer'
{ can_submit: false, can_review: true, can_take_quiz: false, can_mentor: false }
when 'Teaching Assistant'
{ can_submit: false, can_review: true, can_take_quiz: false, can_mentor: true }
when 'Mentor'
{ can_submit: false, can_review: false, can_take_quiz: false, can_mentor: true }
else
{ can_submit: false, can_review: false, can_take_quiz: false, can_mentor: false }
end
end
def set_participant
@participant = Participant.find(params[:id])
rescue ActiveRecord::RecordNotFound
render json: { error: "Participant not found" }, status: 404
end
def participant_params params.require(:participant).permit(:user_id, :assignment_id) end
# *****************************************************
def controller_locale locale_for_student end
# def list # if Participant::PARTICIPANT_TYPES.include? params[:model] # @root_node = Object.const_get(params[:model] + 'Node').find_by(node_object_id: params[:id]) # @parent = Object.const_get(params[:model]).find(params[:id]) # end # begin # @participants = @parent.participants # @model = params[:model] # @authorization = params[:authorization] # rescue StandardError # flash[:error] = $ERROR_INFO # end # end
# def add
# curr_object = Object.const_get(params[:model]).find(params[:id]) if Participant::PARTICIPANT_TYPES.include? params[:model]
# begin
# permissions = participant_permissions(params[:authorization])
# can_submit = permissions[:can_submit]
# can_review = permissions[:can_review]
# can_take_quiz = permissions[:can_take_quiz]
# #E2351 - add corresponding duty fill from permissions
# can_mentor = permissions[:can_mentor]
# if curr_object.is_a?(Assignment)
# curr_object.add_participant(params[:user][:name], can_submit, can_review, can_take_quiz, can_mentor)
# elsif curr_object.is_a?(Course)
# curr_object.add_participant(params[:user][:name])
# end
# user = User.find_by(name: params[:user][:name])
# @model = params[:model]
# @participant = curr_object.participants.find_by(user_id: user.id)
# flash.now[:note] = "The user #{params[:user][:name]} has successfully been added."
# rescue StandardError
# url_for controller: 'users', action: 'new'
# flash.now[:error] = "The user #{params[:user][:name]} does not exist or has already been added."
# end
# render action: 'add.js.erb', layout: false
# end
# when you change the duties, changes the permissions based on the new duty you go to
def update_authorizations
participant = Participant.find(params[:id])
permissions = participant_permissions(params[:authorization])
can_submit = permissions[:can_submit]
can_review = permissions[:can_review]
can_take_quiz = permissions[:can_take_quiz]
can_mentor = permissions[:can_mentor]
parent_id = participant.parent_id
# Upon successfully updating the attributes based on user role, a flash message is displayed to the user after the
# change in the database. This also gives the user the error message if the update fails.
begin
participant.update_attributes(can_submit: can_submit, can_review: can_review, can_take_quiz: can_take_quiz, can_mentor: can_mentor)
flash[:success] = 'The role of the selected participants has been successfully updated.'
rescue StandardError
flash[:error] = 'The update action failed.'
end
redirect_to action: 'list', id: parent_id, model: participant.class.to_s.gsub('Participant', )
end
# def destroy
# participant = Participant.find(params[:id])
# parent_id = participant.parent_id
# begin
# participant.destroy
# flash[:note] = undo_link("The user \"#{participant.user.name}\" has been successfully removed as a participant.")
# rescue StandardError
# flash[:error] = 'This participant is on a team, or is assigned as a reviewer for someone’s work.'
# end
# redirect_to action: 'list', id: parent_id, model: participant.class.to_s.gsub('Participant', )
# end
# Copies existing participants from a course down to an assignment
def inherit
assignment = Assignment.find(params[:id])
course = assignment.course
@copied_participants = []
if course
participants = course.participants
if !participants.empty?
participants.each do |participant|
new_participant = participant.copy(params[:id])
@copied_participants.push new_participant if new_participant
end
# Only display undo link if copies of participants are created
if !@copied_participants.empty?
undo_link("The participants from \"#{course.name}\" have been successfully copied to this assignment. ")
else
flash[:note] = 'All course participants are already in this assignment'
end
else
flash[:note] = 'No participants were found to inherit this assignment.'
end
else
flash[:error] = 'No course was found for this assignment.'
end
redirect_to controller: 'participants', action: 'list', id: assignment.id, model: 'Assignment'
end
# Take all participants from an assignment and "bequeath" them to course as course_participants.
def bequeath_all
@copied_participants = []
assignment = Assignment.find(params[:id])
if assignment.course
course = assignment.course
assignment.participants.each do |participant|
new_participant = participant.copy_to_course(course.id)
@copied_participants.push new_participant if new_participant
end
# only display undo link if copies of participants are created
if !@copied_participants.empty?
undo_link("All participants were successfully copied to \"#{course.name}\". ")
else
flash[:note] = 'All assignment participants are already part of the course'
end
else
flash[:error] = 'This assignment is not associated with a course.'
end
redirect_to controller: 'participants', action: 'list', id: assignment.id, model: 'Assignment'
end
# Allow participant to change handle for this assignment # If the participant parameters are available, update the participant # and redirect to the view_actions page def change_handle @participant = AssignmentParticipant.find(params[:id]) return unless current_user_id?(@participant.user_id)
unless params[:participant].nil?
if !AssignmentParticipant.where(parent_id: @participant.parent_id, handle: params[:participant][:handle]).empty?
ExpertizaLogger.error LoggerMessage.new(controller_name, @participant.name, "Handle #{params[:participant][:handle]} already in use", request)
flash[:error] = "The handle #{params[:participant][:handle]} is already in use for this assignment. Please select a different one."
redirect_to controller: 'participants', action: 'change_handle', id: @participant
else
@participant.update_attributes(participant_params)
ExpertizaLogger.info LoggerMessage.new(controller_name, @participant.name, 'The change handle is saved successfully', request)
redirect_to controller: 'student_task', action: 'view', id: @participant
end
end
end
# Deletes participants from an assignment
def delete
contributor = AssignmentParticipant.find(params[:id])
name = contributor.name
assignment_id = contributor.assignment
begin
contributor.destroy
flash[:note] = "\"#{name}\" is no longer a participant in this assignment."
rescue StandardError
flash[:error] = "\"#{name}\" was not removed from this assignment. Please ensure that \"#{name}\" is not a reviewer or metareviewer and try again."
end
redirect_to controller: 'review_mapping', action: 'list_mappings', id: assignment_id
end
# A ‘copyright grant’ means the author has given permission to the instructor to use the work outside the course. # This is incompletely implemented, but the values in the last column in http://expertiza.ncsu.edu/student_task/list are sourced from here. def view_copyright_grants assignment_id = params[:id] assignment = Assignment.find(assignment_id) @assignment_name = assignment.name @has_topics = false @teams_info = [] teams = Team.where(parent_id: assignment_id) teams.each do |team| team_info = {} team_info[:name] = team.name(session[:ip]) users = [] team.users { |team_user| users.append(get_user_info(team_user, assignment)) } team_info[:users] = users @has_topics = get_signup_topics_for_assignment(assignment_id, team_info, team.id) team_without_topic = SignedUpTeam.where('team_id = ?', team.id).none? next if @has_topics && team_without_topic
@teams_info.append(team_info)
end
@teams_info = @teams_info.sort_by { |hashmap| [hashmap[:topic_id] ? 0 : 1, hashmap[:topic_id] || 0] }
end
private
def participant_params
params.require(:participant).permit(:can_submit, :can_review, :user_id, :parent_id, :submitted_at,
:permission_granted, :penalty_accumulated, :grade, :type, :handle,
:time_stamp, :digital_signature, :can_mentor, :can_take_quiz)
end
# Get the user info from the team user
def get_user_info(team_user, assignment)
user = {}
user[:name] = team_user.name
user[:fullname] = team_user.fullname
# set by default
permission_granted = false
assignment.participants.each do |participant|
permission_granted = participant.permission_granted? if team_user.id == participant.user.id
end
# If permission is granted, set the publisting rights string
user[:pub_rights] = permission_granted ? 'Granted' : 'Denied'
user[:verified] = false
user
end
# Get the signup topics for the assignment
def get_signup_topics_for_assignment(assignment_id, team_info, team_id)
signup_topics = SignUpTopic.where('assignment_id = ?', assignment_id)
if signup_topics.any?
has_topics = true
signup_topics.each do |signup_topic|
signup_topic.signed_up_teams.each do |signed_up_team|
if signed_up_team.team_id == team_id
team_info[:topic_name] = signup_topic.topic_name
team_info[:topic_id] = signup_topic.topic_identifier.to_i
end
end
end
end
has_topics
end
end
- ***************************************************************************************************
- class Api::V1::ParticipantsController < ApplicationController
- before_action :set_participant, only: [:show, :update, :destroy, :update_authorization]
- def index
- participants = Participant.all
- render json: participants, status: :ok
- end
- def show
- render json: @participant, status: :ok
- end
- def create
- participant = Participant.new(participant_params)
- if participant.save
- render json: participant, status: :created
- else
- render json: { errors: participant.errors.full_messages }, status: :unprocessable_entity
- end
- end
- def update
- if @participant.update(participant_params)
- render json: @participant, status: :ok
- else
- render json: { errors: @participant.errors.full_messages }, status: :unprocessable_entity
- end
- end
- def destroy
- @participant.destroy
- render json: { message: "Participant deleted successfully" }, status: :no_content
- end
- def user_index
- participants = Participant.where(user_id: params[:user_id])
- render json: participants, status: :ok
- end
- def assignment_index
- participants = Participant.where(assignment_id: params[:assignment_id])
- render json: participants, status: :ok
- end
- def add
- user = User.find_by(name: params[:user][:name])
- if user.nil?
- render json: { error: "User not found" }, status: :not_found
- return
- end
- assignment = Assignment.find(params[:id])
- participant = assignment.participants.create(user: user)
- if participant.persisted?
- render json: participant, status: :created
- else
- render json: { errors: participant.errors.full_messages }, status: :unprocessable_entity
- end
- end
- def update_authorization
- permissions = participant_permissions(params[:authorization])
- if @participant.update(permissions)
- render json: @participant, status: :ok
- else
- render json: { errors: @participant.errors.full_messages }, status: :unprocessable_entity
- end
- end
- private
- def set_participant
- @participant = Participant.find_by(id: params[:id])
- render json: { error: "Participant not found" }, status: :not_found if @participant.nil?
- end
- def participant_params
- params.require(:participant).permit(:user_id, :assignment_id, :can_submit, :can_review, :can_take_quiz)
- end
- end
Role-Based Strategy Pattern
As part of this reimplementation, a Strategy Design Pattern was introduced to streamline role-based permission handling, replacing the large conditional structure previously present in participants_helper.rb.
This pattern allows each role to encapsulate its own permission logic, enabling easier testing, extension, and adherence to SOLID principles.
Structure
The strategy pattern consists of the following components located under the Strategies/roles directory:
mentor_strategy.rbreviewer_strategy.rbstudent_strategy.rbteacher_assistant_strategy.rbrole_strategy.rb– Base module defining required methodsrole_context.rb– Context class that uses strategies dynamically
How it Works
Each role strategy implements the get_permissions method to return a role-specific permission hash, for example:
```ruby
- Example from MentorStrategy
def get_permissions
{
can_submit: false,
can_review: false,
can_take_quiz: false,
can_mentor: true
}
end
Sample of the Swagger UI API working
APIs of all the participants_controller.rb

Updating a user by specific id

Request successful with User updated

Resources
Team Members
- Vansh Dodiya (vkdodiya)
- Brian Huynh (bhhuynh)
- Akhil Adusumilli (aadusum)
Mentor
- Aniruddha Rajnekar