Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
1b22252
Added required files from expertiza repo, modified the participants c…
manavkamdar27 Mar 18, 2025
077912c
Changes to MentoredTeam and TeamParticipants
adii711 Mar 22, 2025
ad9ac18
Test cases added
adii711 Mar 23, 2025
d71f048
Create assignment_team_spec.rb
adii711 Mar 24, 2025
e7e8545
Create mentored_team_spec.rb
adii711 Mar 24, 2025
0f82e7b
Add files via upload
adii711 Mar 24, 2025
b09725b
Update team_participants_controller.rb
adii711 Mar 24, 2025
ba785e3
Update assignment_team_spec.rb
adii711 Mar 24, 2025
8f2144a
Update mentored_team_spec.rb
adii711 Mar 24, 2025
159590f
Update teams_participant_spec.rb
adii711 Mar 24, 2025
52b33eb
Update and rename teams_participants_controller_spec.rb to teams_part…
adii711 Mar 24, 2025
f9b8d0d
Update and rename teams_participants_request_spec.rb to teams_partici…
adii711 Mar 24, 2025
4028066
updated dockerfile
ShivangPatel2602 Mar 25, 2025
547ae3e
modified dockerfile
ShivangPatel2602 Mar 25, 2025
6376dd5
updated dockerfile
ShivangPatel2602 Mar 25, 2025
0ff6463
updated dockerfile
ShivangPatel2602 Mar 25, 2025
bced385
added production env variable in docker
ShivangPatel2602 Mar 25, 2025
1f5702b
updated application.rb file
ShivangPatel2602 Mar 25, 2025
bb02e03
modified gemfile
ShivangPatel2602 Mar 25, 2025
931e614
Made changes to swagger ui, testing of api pending
manavkamdar27 Apr 20, 2025
16babe3
Some changes on test files
manavkamdar27 Apr 20, 2025
ba3082d
Merge remote-tracking branch 'origin/aditya'
adii711 Apr 23, 2025
305feed
Update assignment_team_spec.rb
adii711 Apr 28, 2025
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
Empty file added 2
Empty file.
70 changes: 54 additions & 16 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,24 +1,62 @@
FROM ruby:3.2.7
# Make sure it matches the Ruby version in .ruby-version and Gemfile
ARG RUBY_VERSION=3.2.7
FROM ruby:$RUBY_VERSION

LABEL maintainer="Ankur Mundra <[email protected]>"
# Install dependencies
RUN apt-get update && \
apt-get install -y curl && \
curl -fsSL https://deb.nodesource.com/setup_18.x | bash - && \
apt-get install -y nodejs && \
apt-get install -y netcat-openbsd
# Install libvips for Active Storage preview support
RUN apt-get update -qq && \
apt-get install -y build-essential libvips && \
apt-get clean && \
rm -rf /var/lib/apt/lists/* /usr/share/doc /usr/share/man

# Set the working directory
# Rails app lives here
WORKDIR /app

# Copy your application files from current location to WORKDIR
COPY . .
# Set production environment
ENV RAILS_LOG_TO_STDOUT="1" \
RAILS_SERVE_STATIC_FILES="true" \
RAILS_ENV="production"

# Set DATABASE_URL here
ENV DATABASE_URL="mysql2://user:password@localhost:3306/app_name"

# Install Ruby dependencies
RUN gem update --system && gem install bundler:2.4.7
# Install application gems
COPY Gemfile Gemfile.lock ./
RUN bundle install

EXPOSE 3002
# Copy application code
COPY . .

# Precompile bootsnap code for faster boot times
RUN bundle exec bootsnap precompile --gemfile app/ lib/

# Install Yarn
RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
RUN echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
RUN apt-get update -qq && apt-get install -y yarn

# Create user and group for app
ARG UID=1000
ARG GID=1000

RUN bash -c "set -o pipefail && apt-get update \
&& apt-get install -y --no-install-recommends build-essential curl git libpq-dev \
&& curl -sSL https://deb.nodesource.com/setup_18.x | bash - \
&& curl -sSL https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
&& echo 'deb https://dl.yarnpkg.com/debian/ stable main' | tee /etc/apt/sources.list.d/yarn.list \
&& apt-get update && apt-get install -y --no-install-recommends nodejs yarn \
&& rm -rf /var/lib/apt/lists/* /usr/share/doc /usr/share/man \
&& apt-get clean \
&& groupadd -g \"${GID}\" ruby \
&& useradd --create-home --no-log-init -u \"${UID}\" -g \"${GID}\" ruby \
&& mkdir /node_modules && chown ruby:ruby -R /node_modules /app"

# Precompiling assets for production without requiring secret RAILS_MASTER_KEY
RUN bundle exec rails assets:precompile
RUN bundle exec rails db:migrate
RUN bundle exec rails db:seed

# Expose port 3000
EXPOSE 3000

# Set the entry point
ENTRYPOINT ["/app/setup.sh"]
# Start the server by default, this can be overwritten at runtime
CMD ["./bin/rails", "server", "-b", "0.0.0.0"]
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ gem 'puma', '~> 5.0'
gem 'rails', '~> 8.0', '>= 8.0.1'
gem 'rswag-api'
gem 'rswag-ui'
gem 'sprockets-rails'

# Build JSON APIs with ease [https://github.com/rails/jbuilder]
# gem "jbuilder"
Expand Down
114 changes: 114 additions & 0 deletions app/controllers/api/v1/team_participants_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
class TeamsParticipantsController < ApplicationController
include AuthorizationHelper

# Update duties only if student privileges are present
def action_allowed?
return current_user_has_student_privileges? if params[:action] == "update_duties"

current_user_has_ta_privileges?
end

# Auto completing username when adding a new member to a team
def auto_complete_for_user_name
team = Team.find(session[:team_id])

# Finds possible team members based on partial names
@users = team.possible_team_members(params[:user][:name])
render inline: "<%= auto_complete_result @users, 'name' %>", layout: false
end

# Updating the duties assigned to a team member
def update_duties
team_participant = TeamsParticipant.find(params[:teams_participant_id])
team_participant.update(duty_id: params[:teams_participant]['duty_id'])
redirect_to controller: 'student_teams', action: 'view', student_id: params[:participant_id]
end

# Listing all participants of a team
def list
@team = Team.find(params[:id])
@assignment = Assignment.find(@team.parent_id)
@teams_participants = TeamsParticipant.where(team_id: params[:id]).page(params[:page]).per(10)
end

# Finding a team by the team id
def new
@team = Team.find(params[:id])
end

# Adding a participant to a team
def create
user = User.find_by(name: params[:user][:name].strip)
# Throwing an error if no user is found
if user.nil?
flash[:error] = user_not_found_message(params[:user][:name])
redirect_back fallback_location: root_path
return
end

team = Team.find(params[:id])
assignment_or_course = team.parent

# Checking if a participant is valid for the assigned team or course
unless valid_participant?(user, assignment_or_course)
redirect_back fallback_location: root_path
return
end

# Adding a member to a team and then checking if there are any errors to that
begin
add_member_return = team.add_member(user, team.parent_id)
flash[:error] = "This team already has the maximum number of members." if add_member_return == false
undo_link("The participant \"#{user.name}\" has been successfully added to \"#{team.name}\".") if add_member_return
rescue
flash[:error] = "The user #{user.name} is already a member of the team #{team.name}"
end

redirect_to controller: 'teams', action: 'list', id: team.parent_id
end

# Removing a participant from a team
def delete
team_participant = TeamsParticipant.find(params[:id])
parent_id = Team.find(team_participant.team_id).parent_id
user = User.find(team_participant.participant.user_id)
team_participant.destroy

undo_link("The participant \"#{user.name}\" has been successfully removed.")
redirect_to controller: 'teams', action: 'list', id: parent_id
end

# Deleting multiple participants from a team
def delete_selected
TeamsParticipant.where(id: params[:item]).destroy_all
redirect_to action: 'list', id: params[:id]
end

private
def user_not_found_message(user_name)
urlCreate = url_for controller: 'users', action: 'new'
"\"#{user_name.strip}\" is not defined. Please <a href=\"#{urlCreate}\">create</a> this user before continuing."
end

# Checking if a participant is already assigned to a team
def valid_participant?(user, assignment_or_course)
if assignment_or_course.user_on_team?(user)
flash[:error] = "This user is already assigned to a team for this #{assignment_or_course.class.name.downcase}."
return false
end

participant = assignment_or_course.participants.find_by(user_id: user.id)
unless participant
flash[:error] = participant_not_found_message(user.name, assignment_or_course)
return false
end

true
end

# Returning an error when a participant is not found
def participant_not_found_message(user_name, assignment_or_course)
urlParticipantList = url_for controller: 'participants', action: 'list', id: assignment_or_course.id, model: assignment_or_course.class.name, authorization: 'participant'
"\"#{user_name}\" is not a participant of the current #{assignment_or_course.class.name.downcase}. Please <a href=\"#{urlParticipantList}\">add</a> this user before continuing."
end
end
118 changes: 118 additions & 0 deletions app/models/assignment_team.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
class AssignmentTeam < Team
# require File.dirname(__FILE__) + '/analytic/assignment_team_analytic'
# include AssignmentTeamAnalytic
# include Scoring

belongs_to :assignment, class_name: 'Assignment', foreign_key: 'parent_id'
has_many :review_mappings, class_name: 'ReviewResponseMap', foreign_key: 'reviewee_id'
has_many :review_response_maps, foreign_key: 'reviewee_id'
has_many :responses, through: :review_response_maps, foreign_key: 'map_id'

# E2516: Fetches the user_id of the first member of the team.
# If the current_user is part of the team, return their id.
# @param [User] current_user - The user currently logged in (optional)
# @return [Integer] user_id of the team member
def user_id(current_user = nil)
return current_user.id if current_user.present? && users.include?(current_user)
users.first.id
end

# E2516: Checks if a given participant is part of the team.
# @param [Participant] participant - The participant to check
# @return [Boolean] true if the participant is in the team, false otherwise
def includes?(participant)
participants.include?(participant)
end

# E2516: Returns the parent model name (Assignment) for the current team.
# @return [String] Parent model name
def parent_model
'Assignment'
end

# E2516: Assigns a reviewer to this team.
# Calls `create_review_map` to generate a ReviewResponseMap.
# @param [Reviewer] reviewer - The reviewer to be assigned
def assign_reviewer(reviewer)
assignment = Assignment.find_by(id: parent_id)
raise 'The assignment cannot be found.' if assignment.nil?

# E2516: Create a ReviewResponseMap for this reviewer
create_review_map(reviewer, assignment)
end

# E2516: Creates and returns a review map.
# Extracted to avoid duplicate code in `assign_reviewer`.
# @param [Reviewer] reviewer - The reviewer to assign
# @param [Assignment] assignment - The assignment being reviewed
# @return [ReviewResponseMap] The created review map
def create_review_map(reviewer, assignment)
ReviewResponseMap.create!(
reviewee_id: id,
reviewer_id: reviewer.get_reviewer.id,
reviewed_object_id: assignment.id,
team_reviewing_enabled: assignment.team_reviewing_enabled
)
end

# E2516: Checks if a review has been done by a given reviewer.
# @param [Reviewer] reviewer - The reviewer to check
# @return [Boolean] true if reviewed, false otherwise
def reviewed_by?(reviewer)
ReviewResponseMap.exists?(reviewee_id: id, reviewer_id: reviewer.get_reviewer.id, reviewed_object_id: assignment.id)
end

# E2516: Fetches the participants associated with the team.
# Delegates this to TeamsParticipant to promote DRY principles.
# @return [Array] Array of participants
def participants
TeamsParticipant.team_members(id)
end

# E2516: Adds a participant to the team via TeamsParticipant.
# @param [Integer] assignment_id - The assignment id
# @param [User] user - The user to be added as a participant
def add_participant(assignment_id, user)
return if TeamsParticipant.exists?(participant_id: user.id, team_id: id)

TeamsParticipant.create!(participant_id: user.id, team_id: id)
end

# E2516: Creates a new team and associates a user and topic.
# Extracted to simplify `create_new_team` and reduce nesting.
# @param [Integer] user_id - ID of the user
# @param [SignUpTopic] signuptopic - The selected topic
def create_new_team(user_id, signuptopic)
t_user = TeamsUser.create!(team_id: id, user_id: user_id)
SignedUpTeam.create!(topic_id: signuptopic.id, team_id: id, is_waitlisted: 0)
parent = TeamNode.create!(parent_id: signuptopic.assignment_id, node_object_id: id)
TeamUserNode.create!(parent_id: parent.id, node_object_id: t_user.id)
end

# E2516: Submits a hyperlink to the team's submission.
# Moved to TeamFileService for better separation of concerns.
# @param [String] hyperlink - The URL to submit
def submit_hyperlink(hyperlink)
TeamFileService.submit_hyperlink(self, hyperlink)
end

# E2516: Removes a hyperlink from the team's submission.
# Moved to TeamFileService for consistency.
# @param [String] hyperlink_to_delete - The URL to remove
def remove_hyperlink(hyperlink_to_delete)
TeamFileService.remove_hyperlink(self, hyperlink_to_delete)
end

# E2516: Checks if the team has submitted any files or hyperlinks.
# @return [Boolean] true if submissions exist, false otherwise
def has_submissions?
submitted_files.any? || submitted_hyperlinks.present?
end

# E2516: Returns the most recent submission by the team.
# Optimized query with order to fetch the latest submission.
# @return [SubmissionRecord] The most recent submission
def most_recent_submission
SubmissionRecord.where(team_id: id, assignment_id: parent_id).order(updated_at: :desc).first
end
end
Loading