From 4f244e61d42220cdb2cddeca61a0bcc2dba4d556 Mon Sep 17 00:00:00 2001 From: Ajith Kanumuri <21294096+ajith05@users.noreply.github.com> Date: Tue, 22 Oct 2024 20:21:19 +0000 Subject: [PATCH 01/18] Rename sign_up_topic.rb to project_topic.rb (Issue #1) --- app/models/{sign_up_topic.rb => project_topic.rb} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename app/models/{sign_up_topic.rb => project_topic.rb} (89%) diff --git a/app/models/sign_up_topic.rb b/app/models/project_topic.rb similarity index 89% rename from app/models/sign_up_topic.rb rename to app/models/project_topic.rb index 366dc9f97..a0886ce9e 100644 --- a/app/models/sign_up_topic.rb +++ b/app/models/project_topic.rb @@ -1,4 +1,4 @@ -class SignUpTopic < ApplicationRecord +class ProjectTopic < ApplicationRecord has_many :signed_up_teams, foreign_key: 'topic_id', dependent: :destroy has_many :teams, through: :signed_up_teams # list all teams choose this topic, no matter in waitlist or not has_many :assignment_questionnaires, class_name: 'AssignmentQuestionnaire', foreign_key: 'topic_id', dependent: :destroy From 74a2206c55041cfc9b7ed8c15ff19f929cf9bf9e Mon Sep 17 00:00:00 2001 From: Ajith Kanumuri <21294096+ajith05@users.noreply.github.com> Date: Tue, 22 Oct 2024 20:49:46 +0000 Subject: [PATCH 02/18] Add migration file to create table for Project Topics (Issue #2) --- .../20241022204506_create_project_topics.rb | 15 +++++++++++++++ db/schema.rb | 14 +++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20241022204506_create_project_topics.rb diff --git a/db/migrate/20241022204506_create_project_topics.rb b/db/migrate/20241022204506_create_project_topics.rb new file mode 100644 index 000000000..d8ba37447 --- /dev/null +++ b/db/migrate/20241022204506_create_project_topics.rb @@ -0,0 +1,15 @@ +class CreateProjectTopics < ActiveRecord::Migration[7.0] + def change + create_table :project_topics do |t| + t.text :topic_title + t.integer :assignment_id, default: 0, null: false + t.integer :max_signups, default: 0, null: false + t.text :category + t.string :topic_code, limit: 10 + t.text :description + t.string :link + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 1770d8997..b1eeead75 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2024_04_15_192048) do +ActiveRecord::Schema[7.0].define(version: 2024_10_22_204506) do create_table "account_requests", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "username" t.string "full_name" @@ -194,6 +194,18 @@ t.index ["user_id"], name: "index_participants_on_user_id" end + create_table "project_topics", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + t.text "topic_title" + t.integer "assignment_id", default: 0, null: false + t.integer "max_signups", default: 0, null: false + t.text "category" + t.string "topic_code", limit: 10 + t.text "description" + t.string "link" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + create_table "questionnaires", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "name" t.integer "instructor_id" From 11aee0d68d60e732beecda0c3c6f1f5421a72fd8 Mon Sep 17 00:00:00 2001 From: Ajith Kanumuri <21294096+ajith05@users.noreply.github.com> Date: Sat, 26 Oct 2024 01:43:00 +0000 Subject: [PATCH 03/18] Revert "Add migration file to create table for Project Topics (Issue #2)" This reverts commit 74a2206c55041cfc9b7ed8c15ff19f929cf9bf9e. --- .../20241022204506_create_project_topics.rb | 15 --------------- db/schema.rb | 14 +------------- 2 files changed, 1 insertion(+), 28 deletions(-) delete mode 100644 db/migrate/20241022204506_create_project_topics.rb diff --git a/db/migrate/20241022204506_create_project_topics.rb b/db/migrate/20241022204506_create_project_topics.rb deleted file mode 100644 index d8ba37447..000000000 --- a/db/migrate/20241022204506_create_project_topics.rb +++ /dev/null @@ -1,15 +0,0 @@ -class CreateProjectTopics < ActiveRecord::Migration[7.0] - def change - create_table :project_topics do |t| - t.text :topic_title - t.integer :assignment_id, default: 0, null: false - t.integer :max_signups, default: 0, null: false - t.text :category - t.string :topic_code, limit: 10 - t.text :description - t.string :link - - t.timestamps - end - end -end diff --git a/db/schema.rb b/db/schema.rb index b1eeead75..1770d8997 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2024_10_22_204506) do +ActiveRecord::Schema[7.0].define(version: 2024_04_15_192048) do create_table "account_requests", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "username" t.string "full_name" @@ -194,18 +194,6 @@ t.index ["user_id"], name: "index_participants_on_user_id" end - create_table "project_topics", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.text "topic_title" - t.integer "assignment_id", default: 0, null: false - t.integer "max_signups", default: 0, null: false - t.text "category" - t.string "topic_code", limit: 10 - t.text "description" - t.string "link" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - end - create_table "questionnaires", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "name" t.integer "instructor_id" From db6dc3c140e8e74e733556f4a148958475e21e97 Mon Sep 17 00:00:00 2001 From: Ajith Kanumuri <21294096+ajith05@users.noreply.github.com> Date: Sat, 26 Oct 2024 03:11:33 +0000 Subject: [PATCH 04/18] Rename sign_up_topics table to project_topics --- .../api/v1/assignments_controller.rb | 2 +- .../api/v1/project_topics_controller.rb | 84 ++++++++++++ .../api/v1/sign_up_topics_controller.rb | 84 ------------ .../api/v1/signed_up_teams_controller.rb | 8 +- app/models/assignment.rb | 4 +- app/models/bookmark.rb | 2 +- app/models/signed_up_team.rb | 2 +- config/routes.rb | 4 +- ...rename_sign_up_topics_to_project_topics.rb | 5 + db/schema.rb | 38 +++--- ...ec.rb => project_topic_controller_spec.rb} | 122 +++++++++--------- spec/routing/project_topics_routing_spec.rb | 30 +++++ spec/routing/sign_up_topics_routing_spec.rb | 30 ----- swagger/v1/swagger.yaml | 16 +-- 14 files changed, 218 insertions(+), 213 deletions(-) create mode 100644 app/controllers/api/v1/project_topics_controller.rb delete mode 100644 app/controllers/api/v1/sign_up_topics_controller.rb create mode 100644 db/migrate/20241026015522_rename_sign_up_topics_to_project_topics.rb rename spec/requests/api/v1/{sign_up_topic_controller_spec.rb => project_topic_controller_spec.rb} (68%) create mode 100644 spec/routing/project_topics_routing_spec.rb delete mode 100644 spec/routing/sign_up_topics_routing_spec.rb diff --git a/app/controllers/api/v1/assignments_controller.rb b/app/controllers/api/v1/assignments_controller.rb index e28ad573f..dd2d98dc0 100644 --- a/app/controllers/api/v1/assignments_controller.rb +++ b/app/controllers/api/v1/assignments_controller.rb @@ -146,7 +146,7 @@ def show_assignment_details end # check if assignment has topics - # has_topics is set to true if there is SignUpTopic corresponding to the input assignment id + # has_topics is set to true if there is ProjectTopic corresponding to the input assignment id def has_topics assignment = Assignment.find_by(id: params[:assignment_id]) if assignment.nil? diff --git a/app/controllers/api/v1/project_topics_controller.rb b/app/controllers/api/v1/project_topics_controller.rb new file mode 100644 index 000000000..2ad4ff276 --- /dev/null +++ b/app/controllers/api/v1/project_topics_controller.rb @@ -0,0 +1,84 @@ +class Api::V1::ProjectTopicsController < ApplicationController + before_action :set_project_topic, only: %i[ show update ] + + # GET /api/v1/project_topics?assignment_id=&topic_ids[]= + # Retrieve ProjectTopics by two query parameters - assignment_id (compulsory) and an array of topic_ids (optional) + def index + if params[:assignment_id].nil? + render json: { message: 'Assignment ID is required!' }, status: :unprocessable_entity + elsif params[:topic_ids].nil? + @project_topics = ProjectTopic.where(assignment_id: params[:assignment_id]) + render json: @project_topics, status: :ok + else + @project_topics = ProjectTopic.where(assignment_id: params[:assignment_id], topic_identifier: params[:topic_ids]) + render json: @project_topics, status: :ok + end + # render json: {message: 'All selected topics have been loaded successfully.', project_topics: @stopics}, status: 200 + end + + # POST /project_topics + # The create method allows the instructor to create a new topic + # params[:project_topic][:topic_identifier] follows a json format + # The method takes inputs and outputs the if the topic creation was successful. + def create + @project_topic = ProjectTopic.new(project_topic_params) + @assignment = Assignment.find(params[:project_topic][:assignment_id]) + @project_topic.micropayment = params[:micropayment] if @assignment.microtask? + if @project_topic.save + # undo_link "The topic: \"#{@project_topic.topic_name}\" has been created successfully. " + render json: { message: "The topic: \"#{@project_topic.topic_name}\" has been created successfully. " }, status: :created + else + render json: { message: @project_topic.errors }, status: :unprocessable_entity + end + end + + # PATCH/PUT /project_topics/1 + # updates parameters present in project_topic_params. + def update + if @project_topic.update(project_topic_params) + render json: { message: "The topic: \"#{@project_topic.topic_name}\" has been updated successfully. " }, status: 200 + else + render json: @project_topic.errors, status: :unprocessable_entity + end + end + + # Show a ProjectTopic by ID + def show + render json: @project_topic, status: :ok + end + + # Similar to index method, this method destroys ProjectTopics by two query parameters + # assignment_id is compulsory. + # topic_ids[] is optional + def destroy + # render json: {message: @project_topic} + # filters topics based on assignment id (required) and topic identifiers (optional) + if params[:assignment_id].nil? + render json: { message: 'Assignment ID is required!' }, status: :unprocessable_entity + elsif params[:topic_ids].nil? + @project_topics = ProjectTopic.where(assignment_id: params[:assignment_id]) + # render json: @project_topics, status: :ok + else + @project_topics = ProjectTopic.where(assignment_id: params[:assignment_id], topic_identifier: params[:topic_ids]) + # render json: @project_topics, status: :ok + end + + if @project_topics.each(&:delete) + render json: { message: "The topic has been deleted successfully. " }, status: :no_content + else + render json: @project_topic.errors, status: :unprocessable_entity + end + end + + private + + # Use callbacks to share common setup or constraints between actions. + def set_project_topic + @project_topic = ProjectTopic.find(params[:id]) + end + + # Only allow a list of trusted parameters through. + def project_topic_params + params.require(:project_topic).permit(:topic_identifier, :category, :topic_name, :max_choosers, :assignment_id) + end +end diff --git a/app/controllers/api/v1/sign_up_topics_controller.rb b/app/controllers/api/v1/sign_up_topics_controller.rb deleted file mode 100644 index a736f3eaa..000000000 --- a/app/controllers/api/v1/sign_up_topics_controller.rb +++ /dev/null @@ -1,84 +0,0 @@ -class Api::V1::SignUpTopicsController < ApplicationController - before_action :set_sign_up_topic, only: %i[ show update ] - - # GET /api/v1/sign_up_topics?assignment_id=&topic_ids[]= - # Retrieve SignUpTopics by two query parameters - assignment_id (compulsory) and an array of topic_ids (optional) - def index - if params[:assignment_id].nil? - render json: { message: 'Assignment ID is required!' }, status: :unprocessable_entity - elsif params[:topic_ids].nil? - @sign_up_topics = SignUpTopic.where(assignment_id: params[:assignment_id]) - render json: @sign_up_topics, status: :ok - else - @sign_up_topics = SignUpTopic.where(assignment_id: params[:assignment_id], topic_identifier: params[:topic_ids]) - render json: @sign_up_topics, status: :ok - end - # render json: {message: 'All selected topics have been loaded successfully.', sign_up_topics: @stopics}, status: 200 - end - - # POST /sign_up_topics - # The create method allows the instructor to create a new topic - # params[:sign_up_topic][:topic_identifier] follows a json format - # The method takes inputs and outputs the if the topic creation was successful. - def create - @sign_up_topic = SignUpTopic.new(sign_up_topic_params) - @assignment = Assignment.find(params[:sign_up_topic][:assignment_id]) - @sign_up_topic.micropayment = params[:micropayment] if @assignment.microtask? - if @sign_up_topic.save - # undo_link "The topic: \"#{@sign_up_topic.topic_name}\" has been created successfully. " - render json: { message: "The topic: \"#{@sign_up_topic.topic_name}\" has been created successfully. " }, status: :created - else - render json: { message: @sign_up_topic.errors }, status: :unprocessable_entity - end - end - - # PATCH/PUT /sign_up_topics/1 - # updates parameters present in sign_up_topic_params. - def update - if @sign_up_topic.update(sign_up_topic_params) - render json: { message: "The topic: \"#{@sign_up_topic.topic_name}\" has been updated successfully. " }, status: 200 - else - render json: @sign_up_topic.errors, status: :unprocessable_entity - end - end - - # Show a SignUpTopic by ID - def show - render json: @sign_up_topic, status: :ok - end - - # Similar to index method, this method destroys SignUpTopics by two query parameters - # assignment_id is compulsory. - # topic_ids[] is optional - def destroy - # render json: {message: @sign_up_topic} - # filters topics based on assignment id (required) and topic identifiers (optional) - if params[:assignment_id].nil? - render json: { message: 'Assignment ID is required!' }, status: :unprocessable_entity - elsif params[:topic_ids].nil? - @sign_up_topics = SignUpTopic.where(assignment_id: params[:assignment_id]) - # render json: @sign_up_topics, status: :ok - else - @sign_up_topics = SignUpTopic.where(assignment_id: params[:assignment_id], topic_identifier: params[:topic_ids]) - # render json: @sign_up_topics, status: :ok - end - - if @sign_up_topics.each(&:delete) - render json: { message: "The topic has been deleted successfully. " }, status: :no_content - else - render json: @sign_up_topic.errors, status: :unprocessable_entity - end - end - - private - - # Use callbacks to share common setup or constraints between actions. - def set_sign_up_topic - @sign_up_topic = SignUpTopic.find(params[:id]) - end - - # Only allow a list of trusted parameters through. - def sign_up_topic_params - params.require(:sign_up_topic).permit(:topic_identifier, :category, :topic_name, :max_choosers, :assignment_id) - end -end diff --git a/app/controllers/api/v1/signed_up_teams_controller.rb b/app/controllers/api/v1/signed_up_teams_controller.rb index 97ada5a24..2740d49d5 100644 --- a/app/controllers/api/v1/signed_up_teams_controller.rb +++ b/app/controllers/api/v1/signed_up_teams_controller.rb @@ -1,11 +1,11 @@ class Api::V1::SignedUpTeamsController < ApplicationController - # Returns signed up topics using sign_up_topic assignment id - # Retrieves sign_up_topic using topic_id as a parameter + # Returns signed up topics using project_topic assignment id + # Retrieves project_topic using topic_id as a parameter def index # puts params[:topic_id] - @sign_up_topic = SignUpTopic.find(params[:topic_id]) - @signed_up_team = SignedUpTeam.find_team_participants(@sign_up_topic.assignment_id) + @project_topic = ProjectTopic.find(params[:topic_id]) + @signed_up_team = SignedUpTeam.find_team_participants(@project_topic.assignment_id) render json: @signed_up_team end diff --git a/app/models/assignment.rb b/app/models/assignment.rb index 45e8d2acf..7ee426157 100644 --- a/app/models/assignment.rb +++ b/app/models/assignment.rb @@ -8,7 +8,7 @@ class Assignment < ApplicationRecord has_many :questionnaires, through: :assignment_questionnaires has_many :response_maps, foreign_key: 'reviewed_object_id', dependent: :destroy, inverse_of: :assignment has_many :review_mappings, class_name: 'ReviewResponseMap', foreign_key: 'reviewed_object_id', dependent: :destroy, inverse_of: :assignment - has_many :sign_up_topics , class_name: 'SignUpTopic', foreign_key: 'assignment_id', dependent: :destroy + has_many :project_topics , class_name: 'ProjectTopic', foreign_key: 'assignment_id', dependent: :destroy belongs_to :course, optional: true belongs_to :instructor, class_name: 'User', inverse_of: :assignments @@ -137,7 +137,7 @@ def staggered_and_no_topic?(topic_id) #This method return the value of the has_topics field for the given assignment object. # has_topics is of boolean type and is set true if there is any topic associated with the assignment. def topics? - @has_topics ||= sign_up_topics.any? + @has_topics ||= project_topics.any? end #This method return if the given assignment is a team assignment. diff --git a/app/models/bookmark.rb b/app/models/bookmark.rb index 2f02eabb5..df2fddb39 100644 --- a/app/models/bookmark.rb +++ b/app/models/bookmark.rb @@ -1,6 +1,6 @@ class Bookmark < ApplicationRecord belongs_to :user - # belongs_to :topic, class_name: "SignUpTopic" + # belongs_to :topic, class_name: "ProjectTopic" has_many :bookmark_ratings validates :url, presence: true validates :title, presence: true diff --git a/app/models/signed_up_team.rb b/app/models/signed_up_team.rb index 39b1e2de4..2919b85ca 100644 --- a/app/models/signed_up_team.rb +++ b/app/models/signed_up_team.rb @@ -1,4 +1,4 @@ class SignedUpTeam < ApplicationRecord - belongs_to :sign_up_topic + belongs_to :project_topic belongs_to :team end diff --git a/config/routes.rb b/config/routes.rb index fc8a710a2..8fa5cb39c 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -92,10 +92,10 @@ - resources :sign_up_topics do + resources :project_topics do collection do get :filter - delete '/', to: 'sign_up_topics#destroy' + delete '/', to: 'project_topics#destroy' end end diff --git a/db/migrate/20241026015522_rename_sign_up_topics_to_project_topics.rb b/db/migrate/20241026015522_rename_sign_up_topics_to_project_topics.rb new file mode 100644 index 000000000..245e8a242 --- /dev/null +++ b/db/migrate/20241026015522_rename_sign_up_topics_to_project_topics.rb @@ -0,0 +1,5 @@ +class RenameSignUpTopicsToProjectTopics < ActiveRecord::Migration[7.0] + def change + rename_table :sign_up_topics, :project_topics + end +end diff --git a/db/schema.rb b/db/schema.rb index 1770d8997..3fa6347c8 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2024_04_15_192048) do +ActiveRecord::Schema[7.0].define(version: 2024_10_26_015522) do create_table "account_requests", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "username" t.string "full_name" @@ -194,6 +194,22 @@ t.index ["user_id"], name: "index_participants_on_user_id" end + create_table "project_topics", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + t.text "topic_name", null: false + t.bigint "assignment_id", null: false + t.integer "max_choosers", default: 0, null: false + t.text "category" + t.string "topic_identifier", limit: 10 + t.integer "micropayment", default: 0 + t.integer "private_to" + t.text "description" + t.string "link" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["assignment_id"], name: "fk_sign_up_categories_sign_up_topics" + t.index ["assignment_id"], name: "index_project_topics_on_assignment_id" + end + create_table "questionnaires", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "name" t.integer "instructor_id" @@ -251,22 +267,6 @@ t.index ["parent_id"], name: "fk_rails_4404228d2f" end - create_table "sign_up_topics", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| - t.text "topic_name", null: false - t.bigint "assignment_id", null: false - t.integer "max_choosers", default: 0, null: false - t.text "category" - t.string "topic_identifier", limit: 10 - t.integer "micropayment", default: 0 - t.integer "private_to" - t.text "description" - t.string "link" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.index ["assignment_id"], name: "fk_sign_up_categories_sign_up_topics" - t.index ["assignment_id"], name: "index_sign_up_topics_on_assignment_id" - end - create_table "signed_up_teams", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.bigint "sign_up_topic_id", null: false t.bigint "team_id", null: false @@ -341,10 +341,10 @@ add_foreign_key "participants", "join_team_requests" add_foreign_key "participants", "teams" add_foreign_key "participants", "users" + add_foreign_key "project_topics", "assignments" add_foreign_key "questions", "questionnaires" add_foreign_key "roles", "roles", column: "parent_id", on_delete: :cascade - add_foreign_key "sign_up_topics", "assignments" - add_foreign_key "signed_up_teams", "sign_up_topics" + add_foreign_key "signed_up_teams", "project_topics", column: "sign_up_topic_id" add_foreign_key "signed_up_teams", "teams" add_foreign_key "ta_mappings", "courses" add_foreign_key "ta_mappings", "users" diff --git a/spec/requests/api/v1/sign_up_topic_controller_spec.rb b/spec/requests/api/v1/project_topic_controller_spec.rb similarity index 68% rename from spec/requests/api/v1/sign_up_topic_controller_spec.rb rename to spec/requests/api/v1/project_topic_controller_spec.rb index dbbe78673..38c22bc48 100644 --- a/spec/requests/api/v1/sign_up_topic_controller_spec.rb +++ b/spec/requests/api/v1/project_topic_controller_spec.rb @@ -1,14 +1,14 @@ require 'swagger_helper' -RSpec.describe 'SignUpTopicController API', type: :request do +RSpec.describe 'ProjectTopicController API', type: :request do - # GET /sign_up_topics - path '/api/v1/sign_up_topics' do - get('Get sign-up topics') do + # GET /project_topics + path '/api/v1/project_topics' do + get('Get project topics') do parameter name: 'assignment_id', in: :query, type: :integer, description: 'Assignment ID', required: true parameter name: 'topic_ids', in: :query, type: :string, description: 'Topic Identifier', required: false - tags 'SignUpTopic' + tags 'ProjectTopic' produces 'application/json' response(200, 'successful') do after do |example| @@ -19,9 +19,9 @@ } end # context 'when assignment_id parameter is missing' do - # let(:assignment) { create(:sign_up_topic, assignment_id: create(:assignment)) } + # let(:assignment) { create(:project_topic, assignment_id: create(:assignment)) } # - # before { get '/api/v1/sign_up_topics', params: { assignment_id: assignment_id } } + # before { get '/api/v1/project_topics', params: { assignment_id: assignment_id } } # it 'returns an error message with status 422' do # expect(response).to have_http_status(422) # expect(response_body).to eq({ message: 'Assignment ID is required!' }) @@ -29,30 +29,30 @@ # end context 'when assignment_id parameter is present' do - let!(:sign_up_topics) { create_list(:sign_up_topic, 3, assignment_id: 1) } + let!(:project_topics) { create_list(:project_topic, 3, assignment_id: 1) } let(:assignment_id) { 1 } context 'when topic_identifier parameter is missing' do - before { get "/api/v1/sign_up_topics?assignment_id=#{assignment_id}" } + before { get "/api/v1/project_topics?assignment_id=#{assignment_id}" } - it 'returns a list of all sign-up topics with the given assignment_id' do + it 'returns a list of all project topics with the given assignment_id' do expect(response).to have_http_status(200) expect(response_body[:message]).to eq('All selected topics have been loaded successfully.') - expect(response_body[:sign_up_topics].count).to eq(3) + expect(response_body[:project_topics].count).to eq(3) end end context 'when topic_identifier parameter is present' do - let!(:sign_up_topic) { create(:sign_up_topic, assignment_id: 1, topic_identifier: 'abc') } + let!(:project_topic) { create(:project_topic, assignment_id: 1, topic_identifier: 'abc') } let(:topic_identifier) { 'abc' } - before { get "/api/v1/sign_up_topics?assignment_id=#{assignment_id}&topic_identifier=#{topic_identifier}" } + before { get "/api/v1/project_topics?assignment_id=#{assignment_id}&topic_identifier=#{topic_identifier}" } - it 'returns a list of sign-up topics with the given assignment_id and topic_identifier' do + it 'returns a list of project topics with the given assignment_id and topic_identifier' do expect(response).to have_http_status(200) expect(response_body[:message]).to eq('All selected topics have been loaded successfully.') - expect(response_body[:sign_up_topics].count).to eq(1) - expect(response_body[:sign_up_topics].first[:topic_identifier]).to eq('abc') + expect(response_body[:project_topics].count).to eq(1) + expect(response_body[:project_topics].first[:topic_identifier]).to eq('abc') end end end @@ -63,13 +63,13 @@ end - # DELETE /sign_up_topics - path '/api/v1/sign_up_topics' do - delete('Delete sign-up topics') do + # DELETE /project_topics + path '/api/v1/project_topics' do + delete('Delete project topics') do parameter name: 'assignment_id', in: :query, type: :integer, description: 'Assignment ID', required: true parameter name: 'topic_ids', in: :query, type: :array, items: { type: :string }, description: 'Topic Identifiers to delete', required: false - tags 'SignUpTopic' + tags 'ProjectTopic' produces 'application/json' response(200, 'successful') do after do |example| @@ -83,7 +83,7 @@ context 'when assignment_id parameter is missing' do let(:assignment_id) { nil } - before { delete '/api/v1/sign_up_topics', params: { assignment_id: assignment_id } } + before { delete '/api/v1/project_topics', params: { assignment_id: assignment_id } } it 'returns an error message with status 422' do expect(response).to have_http_status(422) @@ -95,26 +95,26 @@ context 'when topic_ids parameter is missing' do let(:assignment_id) { 1 } - before { delete "/api/v1/sign_up_topics?assignment_id=#{assignment_id}" } + before { delete "/api/v1/project_topics?assignment_id=#{assignment_id}" } - it 'deletes all sign-up topics with the given assignment_id' do + it 'deletes all project topics with the given assignment_id' do expect(response).to have_http_status(200) - expect(response_body).to eq({ message: 'All sign-up topics have been deleted successfully.' }) - expect(SignUpTopic.where(assignment_id: assignment_id)).to be_empty + expect(response_body).to eq({ message: 'All project topics have been deleted successfully.' }) + expect(ProjectTopic.where(assignment_id: assignment_id)).to be_empty end end context 'when topic_ids parameter is present' do - let!(:sign_up_topic) { create(:sign_up_topic, assignment_id: 1, topic_identifier: 'abc') } + let!(:project_topic) { create(:project_topic, assignment_id: 1, topic_identifier: 'abc') } let(:topic_ids) { ['abc'] } let(:assignment_id) { 1 } - before { delete "/api/v1/sign_up_topics?assignment_id=#{assignment_id}&topic_ids=#{topic_ids.join(',')}" } + before { delete "/api/v1/project_topics?assignment_id=#{assignment_id}&topic_ids=#{topic_ids.join(',')}" } it 'deletes sign-up topics with the given assignment_id and topic_identifier' do expect(response).to have_http_status(200) expect(response_body).to eq({ message: 'All selected topics have been deleted successfully.' }) - expect(SignUpTopic.where(assignment_id: assignment_id, topic_identifier: topic_ids)).to be_empty + expect(ProjectTopic.where(assignment_id: assignment_id, topic_identifier: topic_ids)).to be_empty end end end @@ -122,14 +122,14 @@ end end - # CREATE /sign_up_topics - path '/api/v1/sign_up_topics' do + # CREATE /project_topics + path '/api/v1/project_topics' do post('create a new topic in the sheet') do - tags 'SignUpTopic' + tags 'ProjectTopic' consumes 'application/json' - #inputs are from the sign up topic table with properties as ID, name, choosers + #inputs are from the project topic table with properties as ID, name, choosers # assignment ID and micropayment - parameter name: :sign_up_topic, in: :body, schema: { + parameter name: :project_topic, in: :body, schema: { type: :object, properties: { topic_identifier: { type: :integer }, @@ -158,20 +158,20 @@ let!(:assignment) { create(:assignment) } context 'when the request is valid' do - let(:valid_attributes) { { sign_up_topic: attributes_for(:sign_up_topic, assignment_id: assignment.id), micropayment: 0.1 } } + let(:valid_attributes) { { project_topic: attributes_for(:project_topic, assignment_id: assignment.id), micropayment: 0.1 } } - before { post '/api/v1/sign_up_topics', params: valid_attributes } + before { post '/api/v1/project_topics', params: valid_attributes } - it 'creates a sign-up topic' do + it 'creates a project topic' do expect(response).to have_http_status(:created) - expect(response_body[:message]).to eq("The topic: \"#{SignUpTopic.last.topic_name}\" has been created successfully.") + expect(response_body[:message]).to eq("The topic: \"#{ProjectTopic.last.topic_name}\" has been created successfully.") end end context 'when the request is invalid' do - let(:invalid_attributes) { { sign_up_topic: { topic_name: '' }, micropayment: 0.1, assignment_id: assignment.id } } + let(:invalid_attributes) { { project_topic: { topic_name: '' }, micropayment: 0.1, assignment_id: assignment.id } } - before { post '/api/v1/sign_up_topics', params: invalid_attributes } + before { post '/api/v1/project_topics', params: invalid_attributes } it 'returns an error message' do expect(response).to have_http_status(:unprocessable_entity) @@ -180,9 +180,9 @@ end context 'when the assignment does not exist' do - let(:invalid_attributes) { { sign_up_topic: attributes_for(:sign_up_topic), micropayment: 0.1, assignment_id: 999 } } + let(:invalid_attributes) { { project_topic: attributes_for(:project_topic), micropayment: 0.1, assignment_id: 999 } } - before { post '/api/v1/sign_up_topics', params: invalid_attributes } + before { post '/api/v1/project_topics', params: invalid_attributes } it 'returns an error message' do expect(response).to have_http_status(:unprocessable_entity) @@ -191,42 +191,42 @@ end context 'when the assignment is a microtask' do - let(:valid_attributes) { { sign_up_topic: attributes_for(:sign_up_topic, assignment_id: assignment.id), micropayment: 0.1 } } + let(:valid_attributes) { { project_topic: attributes_for(:project_topic, assignment_id: assignment.id), micropayment: 0.1 } } before do assignment.update(microtask: true) - post '/api/v1/sign_up_topics', params: valid_attributes + post '/api/v1/project_topics', params: valid_attributes end it 'sets the micropayment' do expect(response).to have_http_status(:created) - expect(SignUpTopic.last.micropayment).to eq(0.1) + expect(ProjectTopic.last.micropayment).to eq(0.1) end end context 'when the assignment is not a microtask' do - let(:valid_attributes) { { sign_up_topic: attributes_for(:sign_up_topic, assignment_id: assignment.id), micropayment: 0.1 } } + let(:valid_attributes) { { project_topic: attributes_for(:project_topic, assignment_id: assignment.id), micropayment: 0.1 } } before do assignment.update(microtask: false) - post '/api/v1/sign_up_topics', params: valid_attributes + post '/api/v1/project_topics', params: valid_attributes end it 'does not set the micropayment' do expect(response).to have_http_status(:created) - expect(SignUpTopic.last.micropayment).to be_nil + expect(ProjectTopic.last.micropayment).to be_nil end end end - # UPDATE /sign_up_topics - path '/api/v1/sign_up_topics/{id}' do - parameter name: 'id', in: :path, type: :integer, description: 'id of the sign up topic' + # UPDATE /project_topics + path '/api/v1/project_topics/{id}' do + parameter name: 'id', in: :path, type: :integer, description: 'id of the project topic' put('update a new topic in the sheet') do - tags 'SignUpTopic' + tags 'ProjectTopic' consumes 'application/json' - parameter name: :sign_up_topic, in: :body, schema: { + parameter name: :project_topic, in: :body, schema: { type: :object, properties: { topic_identifier: { type: :integer }, @@ -250,12 +250,12 @@ run_test! end - let(:sign_up_topic) { create(:sign_up_topic) } - let(:url) { "/api/v1/sign_up_topics/#{sign_up_topic.id}" } + let(:project_topic) { create(:project_topic) } + let(:url) { "/api/v1/project_topics/#{project_topic.id}" } context "when valid params are provided" do let(:new_topic_name) { "New Topic Name" } - let(:params) { { sign_up_topic: { topic_name: new_topic_name } } } + let(:params) { { project_topic: { topic_name: new_topic_name } } } before { put url, params: params } @@ -264,8 +264,8 @@ end it "updates the sign-up topic" do - sign_up_topic.reload - expect(sign_up_topic.topic_name).to eq new_topic_name + project_topic.reload + expect(project_topic.topic_name).to eq new_topic_name end it "returns a success message" do @@ -274,7 +274,7 @@ end context "when invalid params are provided" do - let(:params) { { sign_up_topic: { topic_name: "" } } } + let(:params) { { project_topic: { topic_name: "" } } } before { put url, params: params } @@ -282,9 +282,9 @@ expect(response).to have_http_status(422) end - it "does not update the sign-up topic" do - sign_up_topic.reload - expect(sign_up_topic.topic_name).not_to eq("") + it "does not update the project topic" do + project_topic.reload + expect(project_topic.topic_name).not_to eq("") end it "returns an error message" do diff --git a/spec/routing/project_topics_routing_spec.rb b/spec/routing/project_topics_routing_spec.rb new file mode 100644 index 000000000..e0509c82d --- /dev/null +++ b/spec/routing/project_topics_routing_spec.rb @@ -0,0 +1,30 @@ +require "rails_helper" + +RSpec.describe ProjectTopicsController, type: :routing do + describe "routing" do + it "routes to #index" do + expect(get: "/project_topics").to route_to("project_topics#index") + end + + it "routes to #show" do + expect(get: "/project_topics/1").to route_to("project_topics#show", id: "1") + end + + + it "routes to #create" do + expect(post: "/project_topics").to route_to("project_topics#create") + end + + it "routes to #update via PUT" do + expect(put: "/project_topics/1").to route_to("project_topics#update", id: "1") + end + + it "routes to #update via PATCH" do + expect(patch: "/project_topics/1").to route_to("project_topics#update", id: "1") + end + + it "routes to #destroy" do + expect(delete: "/project_topics/1").to route_to("project_topics#destroy", id: "1") + end + end +end diff --git a/spec/routing/sign_up_topics_routing_spec.rb b/spec/routing/sign_up_topics_routing_spec.rb deleted file mode 100644 index 5fbf3ed6b..000000000 --- a/spec/routing/sign_up_topics_routing_spec.rb +++ /dev/null @@ -1,30 +0,0 @@ -require "rails_helper" - -RSpec.describe SignUpTopicsController, type: :routing do - describe "routing" do - it "routes to #index" do - expect(get: "/sign_up_topics").to route_to("sign_up_topics#index") - end - - it "routes to #show" do - expect(get: "/sign_up_topics/1").to route_to("sign_up_topics#show", id: "1") - end - - - it "routes to #create" do - expect(post: "/sign_up_topics").to route_to("sign_up_topics#create") - end - - it "routes to #update via PUT" do - expect(put: "/sign_up_topics/1").to route_to("sign_up_topics#update", id: "1") - end - - it "routes to #update via PATCH" do - expect(patch: "/sign_up_topics/1").to route_to("sign_up_topics#update", id: "1") - end - - it "routes to #destroy" do - expect(delete: "/sign_up_topics/1").to route_to("sign_up_topics#destroy", id: "1") - end - end -end diff --git a/swagger/v1/swagger.yaml b/swagger/v1/swagger.yaml index f23cb7b20..2656a98dc 100644 --- a/swagger/v1/swagger.yaml +++ b/swagger/v1/swagger.yaml @@ -1038,9 +1038,9 @@ paths: responses: '204': description: successful - "/api/v1/sign_up_topics": + "/api/v1/project_topics": get: - summary: Get sign-up topics + summary: Get project topics parameters: - name: assignment_id in: query @@ -1055,12 +1055,12 @@ paths: schema: type: string tags: - - SignUpTopic + - ProjectTopic responses: '200': description: successful delete: - summary: Delete sign-up topics + summary: Delete project topics parameters: - name: assignment_id in: query @@ -1077,14 +1077,14 @@ paths: schema: type: array tags: - - SignUpTopic + - ProjectTopic responses: '200': description: successful post: summary: create a new topic in the sheet tags: - - SignUpTopic + - ProjectTopic parameters: [] responses: '201': @@ -1114,7 +1114,7 @@ paths: - category - assignment_id - micropayment - "/api/v1/sign_up_topics/{id}": + "/api/v1/project_topics/{id}": parameters: - name: id in: path @@ -1125,7 +1125,7 @@ paths: put: summary: update a new topic in the sheet tags: - - SignUpTopic + - ProjectTopic parameters: [] responses: '200': From fe1a6c8c938f724fe225d2f074fb9ca521f023f1 Mon Sep 17 00:00:00 2001 From: Ajith Kanumuri <21294096+ajith05@users.noreply.github.com> Date: Tue, 29 Oct 2024 20:19:20 +0000 Subject: [PATCH 05/18] Added methods to sign a team up (Issue #3) --- .../api/v1/signed_up_teams_controller.rb | 1 + app/models/project_topic.rb | 51 +++++++++++++++++++ app/models/signed_up_team.rb | 20 ++++++++ spec/models/project_topic_spec.rb | 0 4 files changed, 72 insertions(+) create mode 100644 spec/models/project_topic_spec.rb diff --git a/app/controllers/api/v1/signed_up_teams_controller.rb b/app/controllers/api/v1/signed_up_teams_controller.rb index 2740d49d5..cb53a7c95 100644 --- a/app/controllers/api/v1/signed_up_teams_controller.rb +++ b/app/controllers/api/v1/signed_up_teams_controller.rb @@ -28,6 +28,7 @@ def sign_up team_id = params[:team_id] topic_id = params[:topic_id] @signed_up_team = SignedUpTeam.create_signed_up_team(topic_id, team_id) + @signed_up_team.save if @signed_up_team render json: { message: "Signed up team successful!" }, status: :created else diff --git a/app/models/project_topic.rb b/app/models/project_topic.rb index a0886ce9e..b14d90665 100644 --- a/app/models/project_topic.rb +++ b/app/models/project_topic.rb @@ -3,4 +3,55 @@ class ProjectTopic < ApplicationRecord has_many :teams, through: :signed_up_teams # list all teams choose this topic, no matter in waitlist or not has_many :assignment_questionnaires, class_name: 'AssignmentQuestionnaire', foreign_key: 'topic_id', dependent: :destroy belongs_to :assignment + + def slot_available? + topic_id = self.id + + # Find the teams who have chosen the topic + teams_who_chose_the_topic = SignedUpTeam.where(sign_up_topic_id: topic_id, is_waitlisted: false) + + # Check if no teams have chosen the topic yet + if teams_who_chose_the_topic.nil? + return true + else + # Check if the number of teams who chose the topic is less than the max allowed and return + return teams_who_chose_the_topic.size < self.max_choosers + end + + def find_team_project_topics(assignment_id, team_id) + SignedUpTeam.joins('INNER JOIN project_topics ON signed_up_teams.topic_id = project_topics.id') + .select('project_topics.id as topic_id, project_topics.topic_name as topic_name, signed_up_teams.is_waitlisted as is_waitlisted, + signed_up_teams.preference_priority_number as preference_priority_number') + .where('project_topics.assignment_id = ? and signed_up_teams.team_id = ?', assignment_id, team_id) + end + + def assign_topic_to_team(new_sign_up) + new_sign_up.update(is_waitlisted: false, sign_up_topic_id: self.id) + end + + def save_waitlist_entry(new_sign_up) + new_sign_up.is_waitlisted = true + result = new_sign_up.save + result + end + + def sign_up_team(team_id) + topic_id = self.id + team = Team.find(team_id) + + existing_sign_up = SignedUpTeam.find_first_existing_sign_up(topic_id: topic_id, team_id: team_id) + + if !existing_sign_up.nil? && !existing_sign_up.is_waitlisted + return false + end + + new_sign_up = SignedUpTeam.new(sign_up_topic_id: topic_id, team_id: team_id) + if slot_available? + assign_topic_to_team(new_sign_up) + result = SignedUpTeam.drop_off_team_waitlists(team_id) + else + result = save_waitlist_entry(new_sign_up) + end + result + end end diff --git a/app/models/signed_up_team.rb b/app/models/signed_up_team.rb index 2919b85ca..1d6efbeb5 100644 --- a/app/models/signed_up_team.rb +++ b/app/models/signed_up_team.rb @@ -1,4 +1,24 @@ class SignedUpTeam < ApplicationRecord belongs_to :project_topic belongs_to :team + + def self.drop_off_team_waitlists(team_id) + SignedUpTeam.where(team_id: team_id, is_waitlisted: true).destroy_all + end + + def self.find_first_existing_sign_up(topic_id, team_id) + SignedUpTeam.find_by(sign_up_topic_id: topic_id, team_id: team_id) + end + + def self.create_signed_up_team(topic_id, team_id) + project_topic = ProjectTopic.find_by(topic_id) + project_topic.sign_up_team(team_id) + end + + def self.get_team_participants(user_id) + team_id + end + + def self.delete_signed_up_team(team_id) + end end diff --git a/spec/models/project_topic_spec.rb b/spec/models/project_topic_spec.rb new file mode 100644 index 000000000..e69de29bb From 1176cdb1641284f35c6689bdabee69e5b566c61b Mon Sep 17 00:00:00 2001 From: Ajith Kanumuri <21294096+ajith05@users.noreply.github.com> Date: Tue, 29 Oct 2024 23:02:16 +0000 Subject: [PATCH 06/18] Implemented remaining methods (Issue #3) --- app/controllers/api/v1/signed_up_teams_controller.rb | 5 +++-- app/models/project_topic.rb | 1 + app/models/signed_up_team.rb | 5 ++++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/app/controllers/api/v1/signed_up_teams_controller.rb b/app/controllers/api/v1/signed_up_teams_controller.rb index cb53a7c95..6c0ba1502 100644 --- a/app/controllers/api/v1/signed_up_teams_controller.rb +++ b/app/controllers/api/v1/signed_up_teams_controller.rb @@ -38,12 +38,13 @@ def sign_up # Method for signing up as student # Params : topic_id - # Get team_id using model method get_team_participants + # Get team_id using model method get_team_id # Call create_signed_up_team Model method def sign_up_student user_id = params[:user_id] topic_id = params[:topic_id] - team_id = SignedUpTeam.get_team_participants(user_id) + assignment_id = params[:assignment_id] + team_id = SignedUpTeam.get_team_id(user_id, assignment_id) # @teams_user = TeamsUser.where(user_id: user_id).first # team_id = @teams_user.team_id @signed_up_team = SignedUpTeam.create_signed_up_team(topic_id, team_id) diff --git a/app/models/project_topic.rb b/app/models/project_topic.rb index b14d90665..ee1b4a5b8 100644 --- a/app/models/project_topic.rb +++ b/app/models/project_topic.rb @@ -16,6 +16,7 @@ def slot_available? else # Check if the number of teams who chose the topic is less than the max allowed and return return teams_who_chose_the_topic.size < self.max_choosers + end end def find_team_project_topics(assignment_id, team_id) diff --git a/app/models/signed_up_team.rb b/app/models/signed_up_team.rb index 1d6efbeb5..acab54d90 100644 --- a/app/models/signed_up_team.rb +++ b/app/models/signed_up_team.rb @@ -15,10 +15,13 @@ def self.create_signed_up_team(topic_id, team_id) project_topic.sign_up_team(team_id) end - def self.get_team_participants(user_id) + def self.get_team_id(user_id, assignment_id) + team_ids = TeamsUser.select('team_id').where(user_id: user_id) + team_id = Team.where(team_id: team_ids, assignment_id: assignment_id).first.team_id team_id end def self.delete_signed_up_team(team_id) + SignedUpTeam.where(team_id: team_id).destroy_all end end From 260b64b347c7e704f9dfd56e03a7ba852d503bf4 Mon Sep 17 00:00:00 2001 From: rcb1409 Date: Wed, 30 Oct 2024 01:58:36 +0000 Subject: [PATCH 07/18] added test cases for Project_topic Model --- app/models/project_topic.rb | 3 +- spec/models/project_topic_spec.rb | 106 ++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 1 deletion(-) diff --git a/app/models/project_topic.rb b/app/models/project_topic.rb index ee1b4a5b8..c1561a667 100644 --- a/app/models/project_topic.rb +++ b/app/models/project_topic.rb @@ -20,7 +20,7 @@ def slot_available? end def find_team_project_topics(assignment_id, team_id) - SignedUpTeam.joins('INNER JOIN project_topics ON signed_up_teams.topic_id = project_topics.id') + SignedUpTeam.joins('INNER JOIN project_topics ON signed_up_teams.sign_up_topic_id = project_topics.id') .select('project_topics.id as topic_id, project_topics.topic_name as topic_name, signed_up_teams.is_waitlisted as is_waitlisted, signed_up_teams.preference_priority_number as preference_priority_number') .where('project_topics.assignment_id = ? and signed_up_teams.team_id = ?', assignment_id, team_id) @@ -32,6 +32,7 @@ def assign_topic_to_team(new_sign_up) def save_waitlist_entry(new_sign_up) new_sign_up.is_waitlisted = true + new_sign_up.sign_up_topic_id = self.id result = new_sign_up.save result end diff --git a/spec/models/project_topic_spec.rb b/spec/models/project_topic_spec.rb index e69de29bb..43d31d109 100644 --- a/spec/models/project_topic_spec.rb +++ b/spec/models/project_topic_spec.rb @@ -0,0 +1,106 @@ +require 'rails_helper' + +RSpec.describe ProjectTopic, type: :model do + # Set up initial data for tests + # let(:instructor) { Instructor.create(firstname: "John", lastname: "Doe") } + let!(:project_topic) { ProjectTopic.create(topic_name: "Dummy Topic", assignment_id: 1, max_choosers: 3) } + let!(:team) { Team.create(assignment_id: 1) } + let!(:assignment) { Assignment.create(name: "Assignment 1") } + + describe '#slot_available?' do + context 'when no teams have chosen the topic' do + it 'returns true' do + expect(project_topic.slot_available?).to be true + end + end + + context 'when the number of teams who chose the topic is less than max choosers' do + before do + # Create a signed up team directly without using a factory + SignedUpTeam.create(sign_up_topic_id: project_topic.id, team: team, is_waitlisted: false) + end + + it 'returns true' do + expect(project_topic.slot_available?).to be true + end + end + + context 'when the number of teams who chose the topic reaches max choosers' do + before do + # Create signed up teams directly + SignedUpTeam.create(sign_up_topic_id: project_topic.id, team: team, is_waitlisted: false) + SignedUpTeam.create(sign_up_topic_id: project_topic.id, team: Team.create(assignment_id: 1), is_waitlisted: false) + SignedUpTeam.create(sign_up_topic_id: project_topic.id, team: Team.create(assignment_id: 1), is_waitlisted: false) + end + + it 'returns false' do + expect(project_topic.slot_available?).to be false + end + end + end + + describe '#find_team_project_topics' do + it 'returns the topics signed up by a specific team' do + signed_up_team = SignedUpTeam.create(sign_up_topic_id: project_topic.id, team_id: team.id) + result = project_topic.find_team_project_topics(assignment.id, team.id) + expect(result.first.topic_id).to eq(project_topic.id) + end + end + + describe '#assign_topic_to_team' do + let(:new_sign_up) { SignedUpTeam.new(sign_up_topic_id: nil, team: team) } + + it 'assigns the topic to the team and updates waitlist status' do + project_topic.assign_topic_to_team(new_sign_up) + expect(new_sign_up.is_waitlisted).to be false + expect(new_sign_up.sign_up_topic_id).to eq(project_topic.id) + end + end + + describe '#save_waitlist_entry' do + let(:new_sign_up) { SignedUpTeam.new(sign_up_topic_id: nil, team_id: team.id) } + + it 'sets the is_waitlisted attribute to true and saves the entry' do + expect(project_topic.save_waitlist_entry(new_sign_up)).to be true + expect(new_sign_up.is_waitlisted).to be true + end + end + + describe '#sign_up_team' do + let(:new_team) { Team.create(assignment_id: 1) } + + context 'when the team has already signed up and is not waitlisted' do + before do + SignedUpTeam.create(sign_up_topic_id: project_topic.id, team: new_team, is_waitlisted: false) + end + + it 'returns false' do + expect(project_topic.sign_up_team(new_team.id)).to be false + end + end + + context 'when slot is available' do + it 'assigns the topic to the team and drops team waitlists' do + allow(SignedUpTeam).to receive(:drop_off_team_waitlists).with(new_team.id).and_return(true) + + expect(project_topic.sign_up_team(new_team.id)).to be true + expect(SignedUpTeam).to have_received(:drop_off_team_waitlists).with(new_team.id) + end + end + + context 'when no slot is available' do + before do + # Create signed up teams directly + SignedUpTeam.create(sign_up_topic: project_topic, team: Team.create(assignment_id: 1), is_waitlisted: false) + SignedUpTeam.create(sign_up_topic: project_topic, team: Team.create(assignment_id: 1), is_waitlisted: false) + end + + it 'saves a waitlist entry for the team' do + expect(project_topic.sign_up_team(new_team.id)).to be true + last_sign_up = SignedUpTeam.last + expect(last_sign_up.is_waitlisted).to be true + expect(last_sign_up.team_id).to eq(new_team.id) + end + end + end +end From ae4a67c39273adbc08765d39f72af7e185acfb09 Mon Sep 17 00:00:00 2001 From: Ajith Kanumuri <21294096+ajith05@users.noreply.github.com> Date: Wed, 30 Oct 2024 03:51:53 +0000 Subject: [PATCH 08/18] Fix test cases (Issue #4) --- app/models/project_topic.rb | 11 +- spec/models/project_topic_spec.rb | 79 ++---------- spec/requests/api/v1/project_topics_spec.rb | 131 ++++++++++++++++++++ 3 files changed, 141 insertions(+), 80 deletions(-) create mode 100644 spec/requests/api/v1/project_topics_spec.rb diff --git a/app/models/project_topic.rb b/app/models/project_topic.rb index c1561a667..a0edff047 100644 --- a/app/models/project_topic.rb +++ b/app/models/project_topic.rb @@ -10,13 +10,8 @@ def slot_available? # Find the teams who have chosen the topic teams_who_chose_the_topic = SignedUpTeam.where(sign_up_topic_id: topic_id, is_waitlisted: false) - # Check if no teams have chosen the topic yet - if teams_who_chose_the_topic.nil? - return true - else - # Check if the number of teams who chose the topic is less than the max allowed and return - return teams_who_chose_the_topic.size < self.max_choosers - end + # Check if the number of teams who chose the topic is less than the max allowed choosers + return teams_who_chose_the_topic.size < self.max_choosers end def find_team_project_topics(assignment_id, team_id) @@ -41,7 +36,7 @@ def sign_up_team(team_id) topic_id = self.id team = Team.find(team_id) - existing_sign_up = SignedUpTeam.find_first_existing_sign_up(topic_id: topic_id, team_id: team_id) + existing_sign_up = SignedUpTeam.find_first_existing_sign_up(topic_id, team_id) if !existing_sign_up.nil? && !existing_sign_up.is_waitlisted return false diff --git a/spec/models/project_topic_spec.rb b/spec/models/project_topic_spec.rb index 43d31d109..082be8cc8 100644 --- a/spec/models/project_topic_spec.rb +++ b/spec/models/project_topic_spec.rb @@ -2,10 +2,11 @@ RSpec.describe ProjectTopic, type: :model do # Set up initial data for tests - # let(:instructor) { Instructor.create(firstname: "John", lastname: "Doe") } - let!(:project_topic) { ProjectTopic.create(topic_name: "Dummy Topic", assignment_id: 1, max_choosers: 3) } - let!(:team) { Team.create(assignment_id: 1) } - let!(:assignment) { Assignment.create(name: "Assignment 1") } + let!(:role) { Role.create!(name: "Professor") } + let!(:instructor) { Instructor.create!(name: "Ed", password: "sec-Key1", full_name: "Ed Gehringer", email: "efg@ncsu.edu", role_id: role.id) } + let!(:assignment) { Assignment.create!(name: "Assignment 1", instructor_id: instructor.id) } + let!(:project_topic) { ProjectTopic.create!(topic_name: "Dummy Topic", assignment_id: assignment.id, max_choosers: 3) } + let!(:team) { Team.create!(assignment_id: assignment.id) } describe '#slot_available?' do context 'when no teams have chosen the topic' do @@ -13,42 +14,10 @@ expect(project_topic.slot_available?).to be true end end - - context 'when the number of teams who chose the topic is less than max choosers' do - before do - # Create a signed up team directly without using a factory - SignedUpTeam.create(sign_up_topic_id: project_topic.id, team: team, is_waitlisted: false) - end - - it 'returns true' do - expect(project_topic.slot_available?).to be true - end - end - - context 'when the number of teams who chose the topic reaches max choosers' do - before do - # Create signed up teams directly - SignedUpTeam.create(sign_up_topic_id: project_topic.id, team: team, is_waitlisted: false) - SignedUpTeam.create(sign_up_topic_id: project_topic.id, team: Team.create(assignment_id: 1), is_waitlisted: false) - SignedUpTeam.create(sign_up_topic_id: project_topic.id, team: Team.create(assignment_id: 1), is_waitlisted: false) - end - - it 'returns false' do - expect(project_topic.slot_available?).to be false - end - end - end - - describe '#find_team_project_topics' do - it 'returns the topics signed up by a specific team' do - signed_up_team = SignedUpTeam.create(sign_up_topic_id: project_topic.id, team_id: team.id) - result = project_topic.find_team_project_topics(assignment.id, team.id) - expect(result.first.topic_id).to eq(project_topic.id) - end end describe '#assign_topic_to_team' do - let(:new_sign_up) { SignedUpTeam.new(sign_up_topic_id: nil, team: team) } + let(:new_sign_up) { SignedUpTeam.new(sign_up_topic_id: nil, team_id: team.id) } it 'assigns the topic to the team and updates waitlist status' do project_topic.assign_topic_to_team(new_sign_up) @@ -57,27 +26,8 @@ end end - describe '#save_waitlist_entry' do - let(:new_sign_up) { SignedUpTeam.new(sign_up_topic_id: nil, team_id: team.id) } - - it 'sets the is_waitlisted attribute to true and saves the entry' do - expect(project_topic.save_waitlist_entry(new_sign_up)).to be true - expect(new_sign_up.is_waitlisted).to be true - end - end - describe '#sign_up_team' do - let(:new_team) { Team.create(assignment_id: 1) } - - context 'when the team has already signed up and is not waitlisted' do - before do - SignedUpTeam.create(sign_up_topic_id: project_topic.id, team: new_team, is_waitlisted: false) - end - - it 'returns false' do - expect(project_topic.sign_up_team(new_team.id)).to be false - end - end + let(:new_team) { Team.create!(assignment_id: assignment.id) } context 'when slot is available' do it 'assigns the topic to the team and drops team waitlists' do @@ -87,20 +37,5 @@ expect(SignedUpTeam).to have_received(:drop_off_team_waitlists).with(new_team.id) end end - - context 'when no slot is available' do - before do - # Create signed up teams directly - SignedUpTeam.create(sign_up_topic: project_topic, team: Team.create(assignment_id: 1), is_waitlisted: false) - SignedUpTeam.create(sign_up_topic: project_topic, team: Team.create(assignment_id: 1), is_waitlisted: false) - end - - it 'saves a waitlist entry for the team' do - expect(project_topic.sign_up_team(new_team.id)).to be true - last_sign_up = SignedUpTeam.last - expect(last_sign_up.is_waitlisted).to be true - expect(last_sign_up.team_id).to eq(new_team.id) - end - end end end diff --git a/spec/requests/api/v1/project_topics_spec.rb b/spec/requests/api/v1/project_topics_spec.rb new file mode 100644 index 000000000..1ec07c141 --- /dev/null +++ b/spec/requests/api/v1/project_topics_spec.rb @@ -0,0 +1,131 @@ +require 'swagger_helper' + +RSpec.describe 'api/v1/project_topics', type: :request do + + path '/api/v1/project_topics/filter' do + + get('filter project_topic') do + response(200, 'successful') do + + after do |example| + example.metadata[:response][:content] = { + 'application/json' => { + example: JSON.parse(response.body, symbolize_names: true) + } + } + end + run_test! + end + end + end + + path '/api/v1/project_topics' do + + delete('delete project_topic') do + response(200, 'successful') do + + after do |example| + example.metadata[:response][:content] = { + 'application/json' => { + example: JSON.parse(response.body, symbolize_names: true) + } + } + end + run_test! + end + end + + get('list project_topics') do + response(200, 'successful') do + + after do |example| + example.metadata[:response][:content] = { + 'application/json' => { + example: JSON.parse(response.body, symbolize_names: true) + } + } + end + run_test! + end + end + + post('create project_topic') do + response(200, 'successful') do + + after do |example| + example.metadata[:response][:content] = { + 'application/json' => { + example: JSON.parse(response.body, symbolize_names: true) + } + } + end + run_test! + end + end + end + + path '/api/v1/project_topics/{id}' do + # You'll want to customize the parameter types... + parameter name: 'id', in: :path, type: :string, description: 'id' + + get('show project_topic') do + response(200, 'successful') do + let(:id) { '123' } + + after do |example| + example.metadata[:response][:content] = { + 'application/json' => { + example: JSON.parse(response.body, symbolize_names: true) + } + } + end + run_test! + end + end + + patch('update project_topic') do + response(200, 'successful') do + let(:id) { '123' } + + after do |example| + example.metadata[:response][:content] = { + 'application/json' => { + example: JSON.parse(response.body, symbolize_names: true) + } + } + end + run_test! + end + end + + put('update project_topic') do + response(200, 'successful') do + let(:id) { '123' } + + after do |example| + example.metadata[:response][:content] = { + 'application/json' => { + example: JSON.parse(response.body, symbolize_names: true) + } + } + end + run_test! + end + end + + delete('delete project_topic') do + response(200, 'successful') do + let(:id) { '123' } + + after do |example| + example.metadata[:response][:content] = { + 'application/json' => { + example: JSON.parse(response.body, symbolize_names: true) + } + } + end + run_test! + end + end + end +end From 4f3e7a2e33621991855ffd38b30ef262ac4643ff Mon Sep 17 00:00:00 2001 From: Ajith Kanumuri <21294096+ajith05@users.noreply.github.com> Date: Sat, 30 Nov 2024 19:24:12 +0000 Subject: [PATCH 09/18] Added code for remaining requirements --- app/models/project_topic.rb | 46 ++++++++++++++++++++++++++---------- app/models/signed_up_team.rb | 9 +++++++ 2 files changed, 42 insertions(+), 13 deletions(-) diff --git a/app/models/project_topic.rb b/app/models/project_topic.rb index a0edff047..6064154d7 100644 --- a/app/models/project_topic.rb +++ b/app/models/project_topic.rb @@ -4,14 +4,10 @@ class ProjectTopic < ApplicationRecord has_many :assignment_questionnaires, class_name: 'AssignmentQuestionnaire', foreign_key: 'topic_id', dependent: :destroy belongs_to :assignment - def slot_available? - topic_id = self.id + validates :max_choosers, numericality: { only_integer: true, greater_than_or_equal_to: 0 } - # Find the teams who have chosen the topic - teams_who_chose_the_topic = SignedUpTeam.where(sign_up_topic_id: topic_id, is_waitlisted: false) - - # Check if the number of teams who chose the topic is less than the max allowed choosers - return teams_who_chose_the_topic.size < self.max_choosers + def slot_available? + current_available_slots > 0 end def find_team_project_topics(assignment_id, team_id) @@ -26,17 +22,13 @@ def assign_topic_to_team(new_sign_up) end def save_waitlist_entry(new_sign_up) - new_sign_up.is_waitlisted = true - new_sign_up.sign_up_topic_id = self.id - result = new_sign_up.save - result + new_sign_up.update(is_waitlisted: true, sign_up_topic_id: self.id) end def sign_up_team(team_id) topic_id = self.id - team = Team.find(team_id) - existing_sign_up = SignedUpTeam.find_first_existing_sign_up(topic_id, team_id) + existing_sign_up = SignedUpTeam.find_first_existing_sign_up(topic_id: topic_id, team_id: team_id) if !existing_sign_up.nil? && !existing_sign_up.is_waitlisted return false @@ -51,4 +43,32 @@ def sign_up_team(team_id) end result end + + def self.longest_waiting_team(topic_id) + SignedUpTeam.where(sign_up_topic_id: topic_id, is_waitlisted: true).order(:created_at).first + end + + def drop_team_from_topic(team_id) + signed_up_team = SignedUpTeam.find_by(team_id: team_id, sign_up_topic_id: self.id) + return nil unless signed_up_team + + unless signed_up_team.is_waitlisted + next_waitlisted_team = ProjectTopic.longest_waiting_team(self.id) + next_waitlisted_team&.reassign_topic(self.id) + end + + signed_up_team.destroy + end + + def self.signed_up_teams_for_topic(topic_id) + SignedUpTeam.where(sign_up_topic_id: topic_id) + end + + def current_available_slots + # Find the teams who have chosen the topic + teams_who_chose_the_topic = SignedUpTeam.where(sign_up_topic_id: self.id, is_waitlisted: false) + + # Compute the number of available slots and return + self.max_choosers.to_i - teams_who_chose_the_topic.size + end end diff --git a/app/models/signed_up_team.rb b/app/models/signed_up_team.rb index acab54d90..0670c573a 100644 --- a/app/models/signed_up_team.rb +++ b/app/models/signed_up_team.rb @@ -24,4 +24,13 @@ def self.get_team_id(user_id, assignment_id) def self.delete_signed_up_team(team_id) SignedUpTeam.where(team_id: team_id).destroy_all end + + def reassign_topic(topic_id) + assigned_team = SignedUpTeam.where(team_id: self.team_id, is_waitlisted: false) + unless assigned_team.nil? + project_topic = ProjectTopic.find(topic_id) + project_topic.drop_team_from_topic(team_id: self.team_id) + end + self.update(is_waitlisted: false) + end end From d5255595cd8b2318666c6951f9899203be9508b1 Mon Sep 17 00:00:00 2001 From: Ajith Kanumuri <21294096+ajith05@users.noreply.github.com> Date: Sat, 30 Nov 2024 20:32:46 +0000 Subject: [PATCH 10/18] Added comments --- .../api/v1/assignments_controller.rb | 5 +- .../api/v1/signed_up_teams_controller.rb | 2 + app/models/project_topic.rb | 30 +++- app/models/signed_up_team.rb | 25 +++- spec/requests/api/v1/project_topics_spec.rb | 131 ------------------ 5 files changed, 58 insertions(+), 135 deletions(-) delete mode 100644 spec/requests/api/v1/project_topics_spec.rb diff --git a/app/controllers/api/v1/assignments_controller.rb b/app/controllers/api/v1/assignments_controller.rb index dd2d98dc0..9d5d16081 100644 --- a/app/controllers/api/v1/assignments_controller.rb +++ b/app/controllers/api/v1/assignments_controller.rb @@ -145,8 +145,9 @@ def show_assignment_details end end - # check if assignment has topics - # has_topics is set to true if there is ProjectTopic corresponding to the input assignment id + # Checks if an assignment has associated topics. + # If the assignment is found, it returns a boolean indicating whether topics exist for it. + # If the assignment is not found, it returns an error message. def has_topics assignment = Assignment.find_by(id: params[:assignment_id]) if assignment.nil? diff --git a/app/controllers/api/v1/signed_up_teams_controller.rb b/app/controllers/api/v1/signed_up_teams_controller.rb index 6c0ba1502..762267303 100644 --- a/app/controllers/api/v1/signed_up_teams_controller.rb +++ b/app/controllers/api/v1/signed_up_teams_controller.rb @@ -68,6 +68,8 @@ def destroy private + # Strong parameters method for permitting attributes related to signed-up teams. + # Ensures only the allowed parameters are passed for creating or updating a signed-up team. def signed_up_teams_params params.require(:signed_up_team).permit(:topic_id, :team_id, :is_waitlisted, :preference_priority_number) end diff --git a/app/models/project_topic.rb b/app/models/project_topic.rb index 6064154d7..50f836612 100644 --- a/app/models/project_topic.rb +++ b/app/models/project_topic.rb @@ -4,8 +4,11 @@ class ProjectTopic < ApplicationRecord has_many :assignment_questionnaires, class_name: 'AssignmentQuestionnaire', foreign_key: 'topic_id', dependent: :destroy belongs_to :assignment + # max_choosers should be a non-negative integer validates :max_choosers, numericality: { only_integer: true, greater_than_or_equal_to: 0 } + # Checks if there are any slots currently available + # Returns true if the number of available slots is greater than 0, otherwise false def slot_available? current_available_slots > 0 end @@ -17,55 +20,80 @@ def find_team_project_topics(assignment_id, team_id) .where('project_topics.assignment_id = ? and signed_up_teams.team_id = ?', assignment_id, team_id) end + # Assigns the current topic to a team by updating the provided sign-up record. + # Marks the sign-up as not waitlisted and associates it with the current topic. def assign_topic_to_team(new_sign_up) new_sign_up.update(is_waitlisted: false, sign_up_topic_id: self.id) end + # Adds a new sign-up to the waitlist by updating the sign-up record. + # Marks the sign-up as waitlisted and associates it with the current topic. def save_waitlist_entry(new_sign_up) new_sign_up.update(is_waitlisted: true, sign_up_topic_id: self.id) end + # Signs up a team for the current topic. + # Checks if the team is already signed up, and if so, ensures they are not waitlisted. + # If a slot is available, assigns the topic to the team; otherwise, adds the team to the waitlist. def sign_up_team(team_id) topic_id = self.id + # Check if the team has already signed up for this topic existing_sign_up = SignedUpTeam.find_first_existing_sign_up(topic_id: topic_id, team_id: team_id) + # If the team is already signed up and not waitlisted, return false if !existing_sign_up.nil? && !existing_sign_up.is_waitlisted return false end + # Create a new sign-up entry for the team new_sign_up = SignedUpTeam.new(sign_up_topic_id: topic_id, team_id: team_id) + + # If there are available slots, assign the topic to the team and remove the team from the waitlist if slot_available? assign_topic_to_team(new_sign_up) result = SignedUpTeam.drop_off_team_waitlists(team_id) else + # If no slots are available, add the team to the waitlist result = save_waitlist_entry(new_sign_up) end + result end + # Retrieves the team that has been waitlisted the longest for a given topic. + # The team is selected based on the earliest created waitlist entry. def self.longest_waiting_team(topic_id) SignedUpTeam.where(sign_up_topic_id: topic_id, is_waitlisted: true).order(:created_at).first end + # Removes a team from the current topic. + # If the team is not waitlisted, the next waitlisted team is reassigned to the topic. + # The team is then destroyed (removed from the sign-up record). def drop_team_from_topic(team_id) + # Find the sign-up record for the team for this topic signed_up_team = SignedUpTeam.find_by(team_id: team_id, sign_up_topic_id: self.id) return nil unless signed_up_team + # If the team is not waitlisted, reassign the topic to the next waitlisted team unless signed_up_team.is_waitlisted next_waitlisted_team = ProjectTopic.longest_waiting_team(self.id) next_waitlisted_team&.reassign_topic(self.id) end + # Destroy the sign-up record for the team signed_up_team.destroy end + # Retrieves all teams that are signed up for a given topic. def self.signed_up_teams_for_topic(topic_id) SignedUpTeam.where(sign_up_topic_id: topic_id) end + # Calculates the number of available slots for a topic. + # It checks how many teams have already chosen the topic and subtracts that from the maximum allowed choosers. def current_available_slots - # Find the teams who have chosen the topic + # Find the teams who have already chosen the topic and are not waitlisted teams_who_chose_the_topic = SignedUpTeam.where(sign_up_topic_id: self.id, is_waitlisted: false) # Compute the number of available slots and return diff --git a/app/models/signed_up_team.rb b/app/models/signed_up_team.rb index 0670c573a..49aa741ed 100644 --- a/app/models/signed_up_team.rb +++ b/app/models/signed_up_team.rb @@ -2,35 +2,58 @@ class SignedUpTeam < ApplicationRecord belongs_to :project_topic belongs_to :team + # Removes all waitlist entries for a given team. + # This deletes all sign-up records where the team is waitlisted. def self.drop_off_team_waitlists(team_id) SignedUpTeam.where(team_id: team_id, is_waitlisted: true).destroy_all end + # Finds the first existing sign-up record for a given topic and team. + # This retrieves the sign-up record for the team and topic, if one exists. def self.find_first_existing_sign_up(topic_id, team_id) SignedUpTeam.find_by(sign_up_topic_id: topic_id, team_id: team_id) end + # Creates a sign-up record for a team by associating the team with the given topic. + # This finds the project topic by its ID and then calls the `sign_up_team` method to sign the team up. def self.create_signed_up_team(topic_id, team_id) - project_topic = ProjectTopic.find_by(topic_id) + # Find the project topic by its ID + project_topic = ProjectTopic.find(topic_id) + + # Sign up the team for the topic project_topic.sign_up_team(team_id) end + # Retrieves the team ID for a given user and assignment. + # This first finds the team(s) the user is associated with and then retrieves the team for the specified assignment. def self.get_team_id(user_id, assignment_id) + # Get the team IDs associated with the given user team_ids = TeamsUser.select('team_id').where(user_id: user_id) + # Find the team that matches the assignment ID and retrieve its team_id team_id = Team.where(team_id: team_ids, assignment_id: assignment_id).first.team_id + # Return the team ID team_id end + # Deletes all sign-up records for a given team. + # This removes all sign-up entries associated with the specified team. def self.delete_signed_up_team(team_id) SignedUpTeam.where(team_id: team_id).destroy_all end + # Reassigns the team to a new topic by removing them from their current topic + # and marking them as no longer waitlisted for the new topic. def reassign_topic(topic_id) + # Find the team's current sign-up record where they are not waitlisted assigned_team = SignedUpTeam.where(team_id: self.team_id, is_waitlisted: false) + + # If the team is already assigned to a topic, remove them from that topic unless assigned_team.nil? project_topic = ProjectTopic.find(topic_id) project_topic.drop_team_from_topic(team_id: self.team_id) end + + # Update the team's waitlist status to false (indicating they are no longer waitlisted) self.update(is_waitlisted: false) end end diff --git a/spec/requests/api/v1/project_topics_spec.rb b/spec/requests/api/v1/project_topics_spec.rb deleted file mode 100644 index 1ec07c141..000000000 --- a/spec/requests/api/v1/project_topics_spec.rb +++ /dev/null @@ -1,131 +0,0 @@ -require 'swagger_helper' - -RSpec.describe 'api/v1/project_topics', type: :request do - - path '/api/v1/project_topics/filter' do - - get('filter project_topic') do - response(200, 'successful') do - - after do |example| - example.metadata[:response][:content] = { - 'application/json' => { - example: JSON.parse(response.body, symbolize_names: true) - } - } - end - run_test! - end - end - end - - path '/api/v1/project_topics' do - - delete('delete project_topic') do - response(200, 'successful') do - - after do |example| - example.metadata[:response][:content] = { - 'application/json' => { - example: JSON.parse(response.body, symbolize_names: true) - } - } - end - run_test! - end - end - - get('list project_topics') do - response(200, 'successful') do - - after do |example| - example.metadata[:response][:content] = { - 'application/json' => { - example: JSON.parse(response.body, symbolize_names: true) - } - } - end - run_test! - end - end - - post('create project_topic') do - response(200, 'successful') do - - after do |example| - example.metadata[:response][:content] = { - 'application/json' => { - example: JSON.parse(response.body, symbolize_names: true) - } - } - end - run_test! - end - end - end - - path '/api/v1/project_topics/{id}' do - # You'll want to customize the parameter types... - parameter name: 'id', in: :path, type: :string, description: 'id' - - get('show project_topic') do - response(200, 'successful') do - let(:id) { '123' } - - after do |example| - example.metadata[:response][:content] = { - 'application/json' => { - example: JSON.parse(response.body, symbolize_names: true) - } - } - end - run_test! - end - end - - patch('update project_topic') do - response(200, 'successful') do - let(:id) { '123' } - - after do |example| - example.metadata[:response][:content] = { - 'application/json' => { - example: JSON.parse(response.body, symbolize_names: true) - } - } - end - run_test! - end - end - - put('update project_topic') do - response(200, 'successful') do - let(:id) { '123' } - - after do |example| - example.metadata[:response][:content] = { - 'application/json' => { - example: JSON.parse(response.body, symbolize_names: true) - } - } - end - run_test! - end - end - - delete('delete project_topic') do - response(200, 'successful') do - let(:id) { '123' } - - after do |example| - example.metadata[:response][:content] = { - 'application/json' => { - example: JSON.parse(response.body, symbolize_names: true) - } - } - end - run_test! - end - end - end -end From 2d1f3b6f7628f7ad37f64ac14cac034305b6c4b5 Mon Sep 17 00:00:00 2001 From: Suhas Adidela <101173171+suhasadidela@users.noreply.github.com> Date: Sun, 1 Dec 2024 17:43:30 +0000 Subject: [PATCH 11/18] Add test cases for project topic --- spec/models/project_topic_spec.rb | 98 +++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/spec/models/project_topic_spec.rb b/spec/models/project_topic_spec.rb index 082be8cc8..6d826398a 100644 --- a/spec/models/project_topic_spec.rb +++ b/spec/models/project_topic_spec.rb @@ -8,12 +8,32 @@ let!(:project_topic) { ProjectTopic.create!(topic_name: "Dummy Topic", assignment_id: assignment.id, max_choosers: 3) } let!(:team) { Team.create!(assignment_id: assignment.id) } + describe 'validations' do + it 'validates max_choosers is a non-negative integer' do + expect(project_topic).to be_valid + project_topic.max_choosers = -1 + expect(project_topic).not_to be_valid + end + end + describe '#slot_available?' do context 'when no teams have chosen the topic' do it 'returns true' do expect(project_topic.slot_available?).to be true end end + + context 'when no slots are available' do + before do + project_topic.max_choosers.times do + SignedUpTeam.create!(sign_up_topic_id: project_topic.id, is_waitlisted: false, team: Team.create!(assignment_id: assignment.id)) + end + end + + it 'returns false' do + expect(project_topic.slot_available?).to be false + end + end end describe '#assign_topic_to_team' do @@ -26,6 +46,16 @@ end end + describe '#save_waitlist_entry' do + let(:new_sign_up) { SignedUpTeam.new(sign_up_topic_id: nil, team_id: team.id) } + + it 'adds a new sign-up to the waitlist' do + project_topic.save_waitlist_entry(new_sign_up) + expect(new_sign_up.is_waitlisted).to be true + expect(new_sign_up.sign_up_topic_id).to eq(project_topic.id) + end + end + describe '#sign_up_team' do let(:new_team) { Team.create!(assignment_id: assignment.id) } @@ -37,5 +67,73 @@ expect(SignedUpTeam).to have_received(:drop_off_team_waitlists).with(new_team.id) end end + + context 'when no slots are available' do + before do + project_topic.max_choosers.times do + SignedUpTeam.create!(sign_up_topic_id: project_topic.id, is_waitlisted: false, team: Team.create!(assignment_id: assignment.id)) + end + end + + it 'adds the team to the waitlist' do + expect { project_topic.sign_up_team(new_team.id) }.to change { SignedUpTeam.count }.by(1) + expect(SignedUpTeam.last.is_waitlisted).to be true + end + end + + context 'when the team is already signed up and not waitlisted' do + before do + SignedUpTeam.create!(sign_up_topic_id: project_topic.id, team: team, is_waitlisted: false) + end + + it 'returns false' do + expect(project_topic.sign_up_team(team.id)).to be false + end + end + end + + describe '.longest_waiting_team' do + let!(:waitlisted_team) { SignedUpTeam.create!(sign_up_topic_id: project_topic.id, is_waitlisted: true, created_at: 1.day.ago, team: Team.create!(assignment_id: assignment.id)) } + + it 'returns the team that has been waitlisted the longest' do + expect(ProjectTopic.longest_waiting_team(project_topic.id)).to eq(waitlisted_team) + end + end + + describe '#drop_team_from_topic' do + let!(:signed_up_team) { SignedUpTeam.create!(sign_up_topic_id: project_topic.id, team: team) } + + it 'removes the team from the topic' do + expect { project_topic.drop_team_from_topic(team.id) }.to change { SignedUpTeam.count }.by(-1) + end + + context 'when the team is not waitlisted' do + let!(:waitlisted_team) { SignedUpTeam.create!(sign_up_topic_id: project_topic.id, is_waitlisted: true, created_at: 1.day.ago, team: Team.create!(assignment_id: assignment.id)) } + + it 'assigns the topic to the next waitlisted team' do + project_topic.drop_team_from_topic(team.id) + expect(waitlisted_team.reload.is_waitlisted).to be false + end + end + end + + describe '#current_available_slots' do + context 'when no teams have signed up' do + it 'returns max_choosers as available slots' do + expect(project_topic.current_available_slots).to eq(project_topic.max_choosers) + end + end + + context 'when some teams have signed up' do + before do + 2.times do + SignedUpTeam.create!(sign_up_topic_id: project_topic.id, is_waitlisted: false, team: Team.create!(assignment_id: assignment.id)) + end + end + + it 'returns the remaining available slots' do + expect(project_topic.current_available_slots).to eq(1) + end + end end end From 7a2969b3e7c5998ebe1a0dd6894f9d343b6a8668 Mon Sep 17 00:00:00 2001 From: Ajith Kanumuri <21294096+ajith05@users.noreply.github.com> Date: Tue, 3 Dec 2024 17:52:28 +0000 Subject: [PATCH 12/18] Addressed PR comments --- .../api/v1/project_topics_controller.rb | 23 +++++++++++-------- .../api/v1/signed_up_teams_controller.rb | 4 ---- app/models/project_topic.rb | 19 +++++---------- app/models/signed_up_team.rb | 8 +------ .../api/v1/project_topic_controller_spec.rb | 9 -------- 5 files changed, 20 insertions(+), 43 deletions(-) diff --git a/app/controllers/api/v1/project_topics_controller.rb b/app/controllers/api/v1/project_topics_controller.rb index 2ad4ff276..23cfc3c23 100644 --- a/app/controllers/api/v1/project_topics_controller.rb +++ b/app/controllers/api/v1/project_topics_controller.rb @@ -13,7 +13,6 @@ def index @project_topics = ProjectTopic.where(assignment_id: params[:assignment_id], topic_identifier: params[:topic_ids]) render json: @project_topics, status: :ok end - # render json: {message: 'All selected topics have been loaded successfully.', project_topics: @stopics}, status: 200 end # POST /project_topics @@ -25,7 +24,6 @@ def create @assignment = Assignment.find(params[:project_topic][:assignment_id]) @project_topic.micropayment = params[:micropayment] if @assignment.microtask? if @project_topic.save - # undo_link "The topic: \"#{@project_topic.topic_name}\" has been created successfully. " render json: { message: "The topic: \"#{@project_topic.topic_name}\" has been created successfully. " }, status: :created else render json: { message: @project_topic.errors }, status: :unprocessable_entity @@ -42,27 +40,30 @@ def update end end + # GET /project_topics/:id # Show a ProjectTopic by ID def show render json: @project_topic, status: :ok end - # Similar to index method, this method destroys ProjectTopics by two query parameters - # assignment_id is compulsory. - # topic_ids[] is optional +# DELETE /project_topics +# Deletes one or more project topics associated with an assignment. +# If `assignment_id` or `topic_ids` are missing, appropriate validations are handled. +# Deletes the topics and returns a success message if successful or error messages otherwise. def destroy - # render json: {message: @project_topic} - # filters topics based on assignment id (required) and topic identifiers (optional) + # Check if the assignment ID is provided if params[:assignment_id].nil? render json: { message: 'Assignment ID is required!' }, status: :unprocessable_entity + # Determine which topics to delete based on the provided parameters elsif params[:topic_ids].nil? + # If no specific topic IDs are provided, fetch all topics for the assignment @project_topics = ProjectTopic.where(assignment_id: params[:assignment_id]) - # render json: @project_topics, status: :ok else + # Fetch the specified topics for the assignment @project_topics = ProjectTopic.where(assignment_id: params[:assignment_id], topic_identifier: params[:topic_ids]) - # render json: @project_topics, status: :ok end + # Attempt to delete the topics and return the appropriate response if @project_topics.each(&:delete) render json: { message: "The topic has been deleted successfully. " }, status: :no_content else @@ -72,7 +73,9 @@ def destroy private - # Use callbacks to share common setup or constraints between actions. + # Callback to set the @project_topic instance variable. + # This method is executed before certain actions (via `before_action`) to load the project topic. + # If the topic is not found, it raises an ActiveRecord::RecordNotFound exception. def set_project_topic @project_topic = ProjectTopic.find(params[:id]) end diff --git a/app/controllers/api/v1/signed_up_teams_controller.rb b/app/controllers/api/v1/signed_up_teams_controller.rb index 762267303..dfd3a2bcc 100644 --- a/app/controllers/api/v1/signed_up_teams_controller.rb +++ b/app/controllers/api/v1/signed_up_teams_controller.rb @@ -3,7 +3,6 @@ class Api::V1::SignedUpTeamsController < ApplicationController # Returns signed up topics using project_topic assignment id # Retrieves project_topic using topic_id as a parameter def index - # puts params[:topic_id] @project_topic = ProjectTopic.find(params[:topic_id]) @signed_up_team = SignedUpTeam.find_team_participants(@project_topic.assignment_id) render json: @signed_up_team @@ -45,10 +44,7 @@ def sign_up_student topic_id = params[:topic_id] assignment_id = params[:assignment_id] team_id = SignedUpTeam.get_team_id(user_id, assignment_id) - # @teams_user = TeamsUser.where(user_id: user_id).first - # team_id = @teams_user.team_id @signed_up_team = SignedUpTeam.create_signed_up_team(topic_id, team_id) - # create(topic_id, team_id) if @signed_up_team render json: { message: "Signed up team successful!" }, status: :created else diff --git a/app/models/project_topic.rb b/app/models/project_topic.rb index 50f836612..c6e9c583e 100644 --- a/app/models/project_topic.rb +++ b/app/models/project_topic.rb @@ -13,13 +13,6 @@ def slot_available? current_available_slots > 0 end - def find_team_project_topics(assignment_id, team_id) - SignedUpTeam.joins('INNER JOIN project_topics ON signed_up_teams.sign_up_topic_id = project_topics.id') - .select('project_topics.id as topic_id, project_topics.topic_name as topic_name, signed_up_teams.is_waitlisted as is_waitlisted, - signed_up_teams.preference_priority_number as preference_priority_number') - .where('project_topics.assignment_id = ? and signed_up_teams.team_id = ?', assignment_id, team_id) - end - # Assigns the current topic to a team by updating the provided sign-up record. # Marks the sign-up as not waitlisted and associates it with the current topic. def assign_topic_to_team(new_sign_up) @@ -39,7 +32,7 @@ def sign_up_team(team_id) topic_id = self.id # Check if the team has already signed up for this topic - existing_sign_up = SignedUpTeam.find_first_existing_sign_up(topic_id: topic_id, team_id: team_id) + existing_sign_up = SignedUpTeam.find_by(sign_up_topic_id: topic_id, team_id: team_id) # If the team is already signed up and not waitlisted, return false if !existing_sign_up.nil? && !existing_sign_up.is_waitlisted @@ -63,8 +56,8 @@ def sign_up_team(team_id) # Retrieves the team that has been waitlisted the longest for a given topic. # The team is selected based on the earliest created waitlist entry. - def self.longest_waiting_team(topic_id) - SignedUpTeam.where(sign_up_topic_id: topic_id, is_waitlisted: true).order(:created_at).first + def longest_waiting_team + SignedUpTeam.where(sign_up_topic_id: self.id, is_waitlisted: true).order(:created_at).first end # Removes a team from the current topic. @@ -77,7 +70,7 @@ def drop_team_from_topic(team_id) # If the team is not waitlisted, reassign the topic to the next waitlisted team unless signed_up_team.is_waitlisted - next_waitlisted_team = ProjectTopic.longest_waiting_team(self.id) + next_waitlisted_team = longest_waiting_team(self.id) next_waitlisted_team&.reassign_topic(self.id) end @@ -86,8 +79,8 @@ def drop_team_from_topic(team_id) end # Retrieves all teams that are signed up for a given topic. - def self.signed_up_teams_for_topic(topic_id) - SignedUpTeam.where(sign_up_topic_id: topic_id) + def signed_up_teams_for_topic + SignedUpTeam.where(sign_up_topic_id: self.id) end # Calculates the number of available slots for a topic. diff --git a/app/models/signed_up_team.rb b/app/models/signed_up_team.rb index 49aa741ed..eb4f5fbad 100644 --- a/app/models/signed_up_team.rb +++ b/app/models/signed_up_team.rb @@ -8,12 +8,6 @@ def self.drop_off_team_waitlists(team_id) SignedUpTeam.where(team_id: team_id, is_waitlisted: true).destroy_all end - # Finds the first existing sign-up record for a given topic and team. - # This retrieves the sign-up record for the team and topic, if one exists. - def self.find_first_existing_sign_up(topic_id, team_id) - SignedUpTeam.find_by(sign_up_topic_id: topic_id, team_id: team_id) - end - # Creates a sign-up record for a team by associating the team with the given topic. # This finds the project topic by its ID and then calls the `sign_up_team` method to sign the team up. def self.create_signed_up_team(topic_id, team_id) @@ -37,7 +31,7 @@ def self.get_team_id(user_id, assignment_id) # Deletes all sign-up records for a given team. # This removes all sign-up entries associated with the specified team. - def self.delete_signed_up_team(team_id) + def self.drop_off_team_signup_records(team_id) SignedUpTeam.where(team_id: team_id).destroy_all end diff --git a/spec/requests/api/v1/project_topic_controller_spec.rb b/spec/requests/api/v1/project_topic_controller_spec.rb index 38c22bc48..5909fb449 100644 --- a/spec/requests/api/v1/project_topic_controller_spec.rb +++ b/spec/requests/api/v1/project_topic_controller_spec.rb @@ -18,15 +18,6 @@ } } end - # context 'when assignment_id parameter is missing' do - # let(:assignment) { create(:project_topic, assignment_id: create(:assignment)) } - # - # before { get '/api/v1/project_topics', params: { assignment_id: assignment_id } } - # it 'returns an error message with status 422' do - # expect(response).to have_http_status(422) - # expect(response_body).to eq({ message: 'Assignment ID is required!' }) - # end - # end context 'when assignment_id parameter is present' do let!(:project_topics) { create_list(:project_topic, 3, assignment_id: 1) } From 7b7d9c32f12cd790b19511b0d3a0631c3c19c2ee Mon Sep 17 00:00:00 2001 From: Ajith Kanumuri <21294096+ajith05@users.noreply.github.com> Date: Tue, 3 Dec 2024 17:57:19 +0000 Subject: [PATCH 13/18] Updated tests based on updated code --- spec/models/project_topic_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/models/project_topic_spec.rb b/spec/models/project_topic_spec.rb index 6d826398a..a799b1bda 100644 --- a/spec/models/project_topic_spec.rb +++ b/spec/models/project_topic_spec.rb @@ -96,7 +96,7 @@ let!(:waitlisted_team) { SignedUpTeam.create!(sign_up_topic_id: project_topic.id, is_waitlisted: true, created_at: 1.day.ago, team: Team.create!(assignment_id: assignment.id)) } it 'returns the team that has been waitlisted the longest' do - expect(ProjectTopic.longest_waiting_team(project_topic.id)).to eq(waitlisted_team) + expect(project_topic.longest_waiting_team).to eq(waitlisted_team) end end From 5a63dbcc075131b36a90eb403ed2327c804b9a10 Mon Sep 17 00:00:00 2001 From: Ajith Kanumuri <21294096+ajith05@users.noreply.github.com> Date: Wed, 4 Dec 2024 03:43:26 +0000 Subject: [PATCH 14/18] Added test cases for signed up team class --- app/models/project_topic.rb | 4 +-- app/models/signed_up_team.rb | 8 +++-- spec/models/project_topic_spec.rb | 2 +- spec/models/signed_up_team_spec.rb | 49 ++++++++++++++++++++++++++++++ 4 files changed, 58 insertions(+), 5 deletions(-) create mode 100644 spec/models/signed_up_team_spec.rb diff --git a/app/models/project_topic.rb b/app/models/project_topic.rb index c6e9c583e..6f1628548 100644 --- a/app/models/project_topic.rb +++ b/app/models/project_topic.rb @@ -1,5 +1,5 @@ class ProjectTopic < ApplicationRecord - has_many :signed_up_teams, foreign_key: 'topic_id', dependent: :destroy + has_many :signed_up_teams, foreign_key: :sign_up_topic_id, dependent: :destroy has_many :teams, through: :signed_up_teams # list all teams choose this topic, no matter in waitlist or not has_many :assignment_questionnaires, class_name: 'AssignmentQuestionnaire', foreign_key: 'topic_id', dependent: :destroy belongs_to :assignment @@ -70,7 +70,7 @@ def drop_team_from_topic(team_id) # If the team is not waitlisted, reassign the topic to the next waitlisted team unless signed_up_team.is_waitlisted - next_waitlisted_team = longest_waiting_team(self.id) + next_waitlisted_team = longest_waiting_team next_waitlisted_team&.reassign_topic(self.id) end diff --git a/app/models/signed_up_team.rb b/app/models/signed_up_team.rb index eb4f5fbad..97ffc7acf 100644 --- a/app/models/signed_up_team.rb +++ b/app/models/signed_up_team.rb @@ -1,5 +1,5 @@ class SignedUpTeam < ApplicationRecord - belongs_to :project_topic + belongs_to :project_topic, foreign_key: :sign_up_topic_id belongs_to :team # Removes all waitlist entries for a given team. @@ -23,8 +23,12 @@ def self.create_signed_up_team(topic_id, team_id) def self.get_team_id(user_id, assignment_id) # Get the team IDs associated with the given user team_ids = TeamsUser.select('team_id').where(user_id: user_id) + + if team_ids.empty? + return nil + end # Find the team that matches the assignment ID and retrieve its team_id - team_id = Team.where(team_id: team_ids, assignment_id: assignment_id).first.team_id + team_id = Team.where(id: team_ids, assignment_id: assignment_id).first.id # Return the team ID team_id end diff --git a/spec/models/project_topic_spec.rb b/spec/models/project_topic_spec.rb index a799b1bda..cbcdd43f6 100644 --- a/spec/models/project_topic_spec.rb +++ b/spec/models/project_topic_spec.rb @@ -92,7 +92,7 @@ end end - describe '.longest_waiting_team' do + describe '#longest_waiting_team' do let!(:waitlisted_team) { SignedUpTeam.create!(sign_up_topic_id: project_topic.id, is_waitlisted: true, created_at: 1.day.ago, team: Team.create!(assignment_id: assignment.id)) } it 'returns the team that has been waitlisted the longest' do diff --git a/spec/models/signed_up_team_spec.rb b/spec/models/signed_up_team_spec.rb new file mode 100644 index 000000000..bc70fa41a --- /dev/null +++ b/spec/models/signed_up_team_spec.rb @@ -0,0 +1,49 @@ +require 'rails_helper' + +RSpec.describe SignedUpTeam, type: :model do + let!(:role) { Role.create!(name: "Professor") } + let!(:instructor) { Instructor.create!(name: "Ed", password: "sec-Key1", full_name: "Ed Gehringer", email: "efg@ncsu.edu", role_id: role.id) } + let!(:assignment) { Assignment.create!(name: "Assignment 1", instructor_id: instructor.id) } + let!(:project_topic) { ProjectTopic.create!(topic_name: "Dummy Topic", assignment_id: assignment.id, max_choosers: 3) } + let!(:team) { Team.create!(assignment_id: assignment.id) } + + describe '.get_team_id' do + let!(:user) { User.create!(name: "Name", password: "password", full_name: "Full Name", email: "email@example.com", mru_directory_path: "/dummy/path", role_id: role.id) } + let!(:teams_user) { TeamsUser.create!(team_id: team.id, user_id: user.id) } + + it 'returns the correct team ID for the given user and assignment' do + expect(SignedUpTeam.get_team_id(user.id, assignment.id)).to eq(team.id) + end + + it 'returns nil if the user is not associated with any team for the assignment' do + other_user = User.create!(name: "Name 2", password: "password", full_name: "Full Name", email: "email@example.com", mru_directory_path: "/dummy/path", role_id: role.id) + expect(SignedUpTeam.get_team_id(other_user.id, assignment.id)).to be_nil + end + end + + describe '.drop_off_team_signup_records' do + let!(:signup1) { SignedUpTeam.create!(sign_up_topic_id: project_topic.id, is_waitlisted: false, team_id: team.id) } + let!(:project_topic_2) { ProjectTopic.create!(topic_name: "Dummy Topic 2", assignment_id: assignment.id, max_choosers: 3) } + let!(:signup2) { SignedUpTeam.create!(sign_up_topic_id: project_topic_2.id, is_waitlisted: true, team_id: team.id) } + + it 'removes all sign-up records for the given team' do + expect { SignedUpTeam.drop_off_team_signup_records(team.id) } + .to change { SignedUpTeam.where(team_id: team.id).count }.by(-2) + end + end + + describe '#reassign_topic' do + let!(:signed_up_team) { SignedUpTeam.create!(sign_up_topic_id: project_topic.id, is_waitlisted: true, team_id: team.id) } + + before do + allow(ProjectTopic).to receive(:find).with(project_topic.id).and_return(project_topic) + allow(project_topic).to receive(:drop_team_from_topic) + end + + it 'reassigns the team to a new topic and marks them as not waitlisted' do + signed_up_team.reassign_topic(project_topic.id) + expect(signed_up_team.reload.is_waitlisted).to be_falsey + expect(project_topic).to have_received(:drop_team_from_topic).with(team_id: team.id) + end + end +end From f8ccef4d41482249adbc1603b85d9189252cdca6 Mon Sep 17 00:00:00 2001 From: Ajith Kanumuri <21294096+ajith05@users.noreply.github.com> Date: Thu, 12 Dec 2024 04:37:57 +0000 Subject: [PATCH 15/18] Addressed Professor's comments --- .../api/v1/signed_up_teams_controller.rb | 14 +++---- app/models/project_topic.rb | 39 +++++++------------ app/models/signed_up_team.rb | 21 +++++----- spec/models/project_topic_spec.rb | 24 +----------- spec/models/signed_up_team_spec.rb | 20 +++++----- 5 files changed, 45 insertions(+), 73 deletions(-) diff --git a/app/controllers/api/v1/signed_up_teams_controller.rb b/app/controllers/api/v1/signed_up_teams_controller.rb index dfd3a2bcc..a0d55234e 100644 --- a/app/controllers/api/v1/signed_up_teams_controller.rb +++ b/app/controllers/api/v1/signed_up_teams_controller.rb @@ -8,7 +8,7 @@ def index render json: @signed_up_team end - # Implemented by signed_up_team.rb (Model) --> create_signed_up_team + # Implemented by signed_up_team.rb (Model) --> sign_up_team_for_topic def create; end # Update signed_up_team using parameters. @@ -22,11 +22,11 @@ def update end # Sign up using parameters: team_id and topic_id - # Calls model method create_signed_up_team + # Calls model method sign_up_team_for_topic def sign_up team_id = params[:team_id] topic_id = params[:topic_id] - @signed_up_team = SignedUpTeam.create_signed_up_team(topic_id, team_id) + @signed_up_team = SignedUpTeam.sign_up_team_for_topic(topic_id, team_id) @signed_up_team.save if @signed_up_team render json: { message: "Signed up team successful!" }, status: :created @@ -37,14 +37,14 @@ def sign_up # Method for signing up as student # Params : topic_id - # Get team_id using model method get_team_id - # Call create_signed_up_team Model method + # Get team_id using model method get_team_id_for_user + # Call sign_up_team_for_topic Model method def sign_up_student user_id = params[:user_id] topic_id = params[:topic_id] assignment_id = params[:assignment_id] - team_id = SignedUpTeam.get_team_id(user_id, assignment_id) - @signed_up_team = SignedUpTeam.create_signed_up_team(topic_id, team_id) + team_id = SignedUpTeam.get_team_id_for_user(user_id, assignment_id) + @signed_up_team = SignedUpTeam.sign_up_team_for_topic(topic_id, team_id) if @signed_up_team render json: { message: "Signed up team successful!" }, status: :created else diff --git a/app/models/project_topic.rb b/app/models/project_topic.rb index 6f1628548..128a13bc4 100644 --- a/app/models/project_topic.rb +++ b/app/models/project_topic.rb @@ -13,49 +13,35 @@ def slot_available? current_available_slots > 0 end - # Assigns the current topic to a team by updating the provided sign-up record. - # Marks the sign-up as not waitlisted and associates it with the current topic. - def assign_topic_to_team(new_sign_up) - new_sign_up.update(is_waitlisted: false, sign_up_topic_id: self.id) - end - - # Adds a new sign-up to the waitlist by updating the sign-up record. - # Marks the sign-up as waitlisted and associates it with the current topic. - def save_waitlist_entry(new_sign_up) - new_sign_up.update(is_waitlisted: true, sign_up_topic_id: self.id) - end - # Signs up a team for the current topic. # Checks if the team is already signed up, and if so, ensures they are not waitlisted. # If a slot is available, assigns the topic to the team; otherwise, adds the team to the waitlist. def sign_up_team(team_id) - topic_id = self.id - # Check if the team has already signed up for this topic - existing_sign_up = SignedUpTeam.find_by(sign_up_topic_id: topic_id, team_id: team_id) + team_signup_record = SignedUpTeam.find_by(sign_up_topic_id: self.id, team_id: team_id, is_waitlisted: false) - # If the team is already signed up and not waitlisted, return false - if !existing_sign_up.nil? && !existing_sign_up.is_waitlisted + # If the team is already signed up, return false + if !team_signup_record.nil? return false end # Create a new sign-up entry for the team - new_sign_up = SignedUpTeam.new(sign_up_topic_id: topic_id, team_id: team_id) + new_signup_record = SignedUpTeam.new(sign_up_topic_id: self.id, team_id: team_id) # If there are available slots, assign the topic to the team and remove the team from the waitlist if slot_available? - assign_topic_to_team(new_sign_up) - result = SignedUpTeam.drop_off_team_waitlists(team_id) + new_signup_record.update(is_waitlisted: false, sign_up_topic_id: self.id) + result = SignedUpTeam.drop_off_topic_waitlists(team_id) else # If no slots are available, add the team to the waitlist - result = save_waitlist_entry(new_sign_up) + new_signup_record.update(is_waitlisted: true, sign_up_topic_id: self.id) end result end - # Retrieves the team that has been waitlisted the longest for a given topic. - # The team is selected based on the earliest created waitlist entry. + # Retrieves the team with the earliest waitlisted record for a given topic. + # The team is determined based on the creation time of the waitlisted record. def longest_waiting_team SignedUpTeam.where(sign_up_topic_id: self.id, is_waitlisted: true).order(:created_at).first end @@ -86,10 +72,11 @@ def signed_up_teams_for_topic # Calculates the number of available slots for a topic. # It checks how many teams have already chosen the topic and subtracts that from the maximum allowed choosers. def current_available_slots - # Find the teams who have already chosen the topic and are not waitlisted - teams_who_chose_the_topic = SignedUpTeam.where(sign_up_topic_id: self.id, is_waitlisted: false) + # Find the number of teams who have already chosen the topic and are not waitlisted + # This would give us the number of teams who have been assigned the topic + num_teams_registered = SignedUpTeam.where(sign_up_topic_id: self.id, is_waitlisted: false).size # Compute the number of available slots and return - self.max_choosers.to_i - teams_who_chose_the_topic.size + self.max_choosers.to_i - num_teams_registered end end diff --git a/app/models/signed_up_team.rb b/app/models/signed_up_team.rb index 97ffc7acf..ce1843b36 100644 --- a/app/models/signed_up_team.rb +++ b/app/models/signed_up_team.rb @@ -4,13 +4,13 @@ class SignedUpTeam < ApplicationRecord # Removes all waitlist entries for a given team. # This deletes all sign-up records where the team is waitlisted. - def self.drop_off_team_waitlists(team_id) + def self.drop_off_topic_waitlists(team_id) SignedUpTeam.where(team_id: team_id, is_waitlisted: true).destroy_all end - # Creates a sign-up record for a team by associating the team with the given topic. - # This finds the project topic by its ID and then calls the `sign_up_team` method to sign the team up. - def self.create_signed_up_team(topic_id, team_id) + # Signs up a team for a specific project topic. + # Finds the project topic by its ID and associates the given team with the topic by calling `sign_up_team`. + def self.sign_up_team_for_topic(topic_id, team_id) # Find the project topic by its ID project_topic = ProjectTopic.find(topic_id) @@ -20,7 +20,8 @@ def self.create_signed_up_team(topic_id, team_id) # Retrieves the team ID for a given user and assignment. # This first finds the team(s) the user is associated with and then retrieves the team for the specified assignment. - def self.get_team_id(user_id, assignment_id) + # NOTE: This method is called in signed_up_teams_controller + def self.get_team_id_for_user(user_id, assignment_id) # Get the team IDs associated with the given user team_ids = TeamsUser.select('team_id').where(user_id: user_id) @@ -35,23 +36,25 @@ def self.get_team_id(user_id, assignment_id) # Deletes all sign-up records for a given team. # This removes all sign-up entries associated with the specified team. - def self.drop_off_team_signup_records(team_id) + def self.delete_team_signup_records(team_id) SignedUpTeam.where(team_id: team_id).destroy_all end # Reassigns the team to a new topic by removing them from their current topic # and marking them as no longer waitlisted for the new topic. + # NOTE: This method gets called only on a waitlisted team def reassign_topic(topic_id) # Find the team's current sign-up record where they are not waitlisted - assigned_team = SignedUpTeam.where(team_id: self.team_id, is_waitlisted: false) + # As this method gets called only on a waitlisted team, we need to check if the team has been assigned another topic + assigned_team = SignedUpTeam.where(team_id: self.team_id, is_waitlisted: false).first # If the team is already assigned to a topic, remove them from that topic unless assigned_team.nil? - project_topic = ProjectTopic.find(topic_id) + project_topic = ProjectTopic.find(assigned_team.sign_up_topic_id) project_topic.drop_team_from_topic(team_id: self.team_id) end # Update the team's waitlist status to false (indicating they are no longer waitlisted) - self.update(is_waitlisted: false) + self.update(sign_up_topic_id: topic_id, is_waitlisted: false) end end diff --git a/spec/models/project_topic_spec.rb b/spec/models/project_topic_spec.rb index cbcdd43f6..fab1703fc 100644 --- a/spec/models/project_topic_spec.rb +++ b/spec/models/project_topic_spec.rb @@ -36,35 +36,15 @@ end end - describe '#assign_topic_to_team' do - let(:new_sign_up) { SignedUpTeam.new(sign_up_topic_id: nil, team_id: team.id) } - - it 'assigns the topic to the team and updates waitlist status' do - project_topic.assign_topic_to_team(new_sign_up) - expect(new_sign_up.is_waitlisted).to be false - expect(new_sign_up.sign_up_topic_id).to eq(project_topic.id) - end - end - - describe '#save_waitlist_entry' do - let(:new_sign_up) { SignedUpTeam.new(sign_up_topic_id: nil, team_id: team.id) } - - it 'adds a new sign-up to the waitlist' do - project_topic.save_waitlist_entry(new_sign_up) - expect(new_sign_up.is_waitlisted).to be true - expect(new_sign_up.sign_up_topic_id).to eq(project_topic.id) - end - end - describe '#sign_up_team' do let(:new_team) { Team.create!(assignment_id: assignment.id) } context 'when slot is available' do it 'assigns the topic to the team and drops team waitlists' do - allow(SignedUpTeam).to receive(:drop_off_team_waitlists).with(new_team.id).and_return(true) + allow(SignedUpTeam).to receive(:drop_off_topic_waitlists).with(new_team.id).and_return(true) expect(project_topic.sign_up_team(new_team.id)).to be true - expect(SignedUpTeam).to have_received(:drop_off_team_waitlists).with(new_team.id) + expect(SignedUpTeam).to have_received(:drop_off_topic_waitlists).with(new_team.id) end end diff --git a/spec/models/signed_up_team_spec.rb b/spec/models/signed_up_team_spec.rb index bc70fa41a..8c3f32d68 100644 --- a/spec/models/signed_up_team_spec.rb +++ b/spec/models/signed_up_team_spec.rb @@ -7,43 +7,45 @@ let!(:project_topic) { ProjectTopic.create!(topic_name: "Dummy Topic", assignment_id: assignment.id, max_choosers: 3) } let!(:team) { Team.create!(assignment_id: assignment.id) } - describe '.get_team_id' do + describe '.get_team_id_for_user' do let!(:user) { User.create!(name: "Name", password: "password", full_name: "Full Name", email: "email@example.com", mru_directory_path: "/dummy/path", role_id: role.id) } let!(:teams_user) { TeamsUser.create!(team_id: team.id, user_id: user.id) } it 'returns the correct team ID for the given user and assignment' do - expect(SignedUpTeam.get_team_id(user.id, assignment.id)).to eq(team.id) + expect(SignedUpTeam.get_team_id_for_user(user.id, assignment.id)).to eq(team.id) end it 'returns nil if the user is not associated with any team for the assignment' do other_user = User.create!(name: "Name 2", password: "password", full_name: "Full Name", email: "email@example.com", mru_directory_path: "/dummy/path", role_id: role.id) - expect(SignedUpTeam.get_team_id(other_user.id, assignment.id)).to be_nil + expect(SignedUpTeam.get_team_id_for_user(other_user.id, assignment.id)).to be_nil end end - describe '.drop_off_team_signup_records' do + describe '.delete_team_signup_records' do let!(:signup1) { SignedUpTeam.create!(sign_up_topic_id: project_topic.id, is_waitlisted: false, team_id: team.id) } let!(:project_topic_2) { ProjectTopic.create!(topic_name: "Dummy Topic 2", assignment_id: assignment.id, max_choosers: 3) } let!(:signup2) { SignedUpTeam.create!(sign_up_topic_id: project_topic_2.id, is_waitlisted: true, team_id: team.id) } it 'removes all sign-up records for the given team' do - expect { SignedUpTeam.drop_off_team_signup_records(team.id) } + expect { SignedUpTeam.delete_team_signup_records(team.id) } .to change { SignedUpTeam.where(team_id: team.id).count }.by(-2) end end describe '#reassign_topic' do + let!(:project_topic_2) { ProjectTopic.create!(topic_name: "Dummy Topic 2", assignment_id: assignment.id, max_choosers: 3) } let!(:signed_up_team) { SignedUpTeam.create!(sign_up_topic_id: project_topic.id, is_waitlisted: true, team_id: team.id) } + let!(:signed_up_team_2) { SignedUpTeam.create!(sign_up_topic_id: project_topic_2.id, is_waitlisted: false, team_id: team.id) } before do - allow(ProjectTopic).to receive(:find).with(project_topic.id).and_return(project_topic) - allow(project_topic).to receive(:drop_team_from_topic) + allow(ProjectTopic).to receive(:find).with(project_topic_2.id).and_return(project_topic_2) + allow(project_topic_2).to receive(:drop_team_from_topic) end it 'reassigns the team to a new topic and marks them as not waitlisted' do signed_up_team.reassign_topic(project_topic.id) - expect(signed_up_team.reload.is_waitlisted).to be_falsey - expect(project_topic).to have_received(:drop_team_from_topic).with(team_id: team.id) + expect(signed_up_team.is_waitlisted).to be_falsey + expect(project_topic_2).to have_received(:drop_team_from_topic).with(team_id: team.id) end end end From aa34b5394feef4428295c3c0e9056b1e369446ba Mon Sep 17 00:00:00 2001 From: Ajith Kanumuri <21294096+ajith05@users.noreply.github.com> Date: Thu, 12 Dec 2024 18:04:17 +0000 Subject: [PATCH 16/18] Addressed PR comments --- .../api/v1/signed_up_teams_controller.rb | 14 +- app/models/project_topic.rb | 4 +- app/models/signed_up_team.rb | 10 +- config/routes.rb | 4 +- spec/models/project_topic_spec.rb | 8 +- spec/models/signed_up_team_spec.rb | 4 +- .../api/v1/signed_up_team_controller_spec.rb | 2 +- swagger/v1/swagger.yaml | 906 +++++++++++++----- 8 files changed, 710 insertions(+), 242 deletions(-) diff --git a/app/controllers/api/v1/signed_up_teams_controller.rb b/app/controllers/api/v1/signed_up_teams_controller.rb index a0d55234e..801fc6fc0 100644 --- a/app/controllers/api/v1/signed_up_teams_controller.rb +++ b/app/controllers/api/v1/signed_up_teams_controller.rb @@ -8,7 +8,7 @@ def index render json: @signed_up_team end - # Implemented by signed_up_team.rb (Model) --> sign_up_team_for_topic + # Implemented by signed_up_team.rb (Model) --> signup_team_for_topic def create; end # Update signed_up_team using parameters. @@ -22,11 +22,11 @@ def update end # Sign up using parameters: team_id and topic_id - # Calls model method sign_up_team_for_topic - def sign_up + # Calls model method signup_team_for_topic + def signup team_id = params[:team_id] topic_id = params[:topic_id] - @signed_up_team = SignedUpTeam.sign_up_team_for_topic(topic_id, team_id) + @signed_up_team = SignedUpTeam.signup_team_for_topic(topic_id, team_id) @signed_up_team.save if @signed_up_team render json: { message: "Signed up team successful!" }, status: :created @@ -38,13 +38,13 @@ def sign_up # Method for signing up as student # Params : topic_id # Get team_id using model method get_team_id_for_user - # Call sign_up_team_for_topic Model method - def sign_up_student + # Call signup_team_for_topic Model method + def signup_user user_id = params[:user_id] topic_id = params[:topic_id] assignment_id = params[:assignment_id] team_id = SignedUpTeam.get_team_id_for_user(user_id, assignment_id) - @signed_up_team = SignedUpTeam.sign_up_team_for_topic(topic_id, team_id) + @signed_up_team = SignedUpTeam.signup_team_for_topic(topic_id, team_id) if @signed_up_team render json: { message: "Signed up team successful!" }, status: :created else diff --git a/app/models/project_topic.rb b/app/models/project_topic.rb index 128a13bc4..5d0c002bd 100644 --- a/app/models/project_topic.rb +++ b/app/models/project_topic.rb @@ -16,7 +16,7 @@ def slot_available? # Signs up a team for the current topic. # Checks if the team is already signed up, and if so, ensures they are not waitlisted. # If a slot is available, assigns the topic to the team; otherwise, adds the team to the waitlist. - def sign_up_team(team_id) + def signup_team(team_id) # Check if the team has already signed up for this topic team_signup_record = SignedUpTeam.find_by(sign_up_topic_id: self.id, team_id: team_id, is_waitlisted: false) @@ -57,7 +57,7 @@ def drop_team_from_topic(team_id) # If the team is not waitlisted, reassign the topic to the next waitlisted team unless signed_up_team.is_waitlisted next_waitlisted_team = longest_waiting_team - next_waitlisted_team&.reassign_topic(self.id) + next_waitlisted_team&.reassign_team_to_new_topic(self.id) end # Destroy the sign-up record for the team diff --git a/app/models/signed_up_team.rb b/app/models/signed_up_team.rb index ce1843b36..8c23a359b 100644 --- a/app/models/signed_up_team.rb +++ b/app/models/signed_up_team.rb @@ -9,13 +9,13 @@ def self.drop_off_topic_waitlists(team_id) end # Signs up a team for a specific project topic. - # Finds the project topic by its ID and associates the given team with the topic by calling `sign_up_team`. - def self.sign_up_team_for_topic(topic_id, team_id) + # Finds the project topic by its ID and associates the given team with the topic by calling `signup_team`. + def self.signup_team_for_topic(topic_id, team_id) # Find the project topic by its ID project_topic = ProjectTopic.find(topic_id) # Sign up the team for the topic - project_topic.sign_up_team(team_id) + project_topic.signup_team(team_id) end # Retrieves the team ID for a given user and assignment. @@ -42,8 +42,8 @@ def self.delete_team_signup_records(team_id) # Reassigns the team to a new topic by removing them from their current topic # and marking them as no longer waitlisted for the new topic. - # NOTE: This method gets called only on a waitlisted team - def reassign_topic(topic_id) + # NOTE: This method gets called only on a waitlisted team (See project_topic.rb -> drop_team_from_topic) + def reassign_team_to_new_topic(topic_id) # Find the team's current sign-up record where they are not waitlisted # As this method gets called only on a waitlisted team, we need to check if the team has been assigned another topic assigned_team = SignedUpTeam.where(team_id: self.team_id, is_waitlisted: false).first diff --git a/config/routes.rb b/config/routes.rb index 8fa5cb39c..7a54c2aea 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -79,8 +79,8 @@ resources :signed_up_teams do collection do - post '/sign_up', to: 'signed_up_teams#sign_up' - post '/sign_up_student', to: 'signed_up_teams#sign_up_student' + post '/signup', to: 'signed_up_teams#signup' + post '/signup_user', to: 'signed_up_teams#signup_user' end end diff --git a/spec/models/project_topic_spec.rb b/spec/models/project_topic_spec.rb index fab1703fc..792334568 100644 --- a/spec/models/project_topic_spec.rb +++ b/spec/models/project_topic_spec.rb @@ -36,14 +36,14 @@ end end - describe '#sign_up_team' do + describe '#signup_team' do let(:new_team) { Team.create!(assignment_id: assignment.id) } context 'when slot is available' do it 'assigns the topic to the team and drops team waitlists' do allow(SignedUpTeam).to receive(:drop_off_topic_waitlists).with(new_team.id).and_return(true) - expect(project_topic.sign_up_team(new_team.id)).to be true + expect(project_topic.signup_team(new_team.id)).to be true expect(SignedUpTeam).to have_received(:drop_off_topic_waitlists).with(new_team.id) end end @@ -56,7 +56,7 @@ end it 'adds the team to the waitlist' do - expect { project_topic.sign_up_team(new_team.id) }.to change { SignedUpTeam.count }.by(1) + expect { project_topic.signup_team(new_team.id) }.to change { SignedUpTeam.count }.by(1) expect(SignedUpTeam.last.is_waitlisted).to be true end end @@ -67,7 +67,7 @@ end it 'returns false' do - expect(project_topic.sign_up_team(team.id)).to be false + expect(project_topic.signup_team(team.id)).to be false end end end diff --git a/spec/models/signed_up_team_spec.rb b/spec/models/signed_up_team_spec.rb index 8c3f32d68..96125becd 100644 --- a/spec/models/signed_up_team_spec.rb +++ b/spec/models/signed_up_team_spec.rb @@ -32,7 +32,7 @@ end end - describe '#reassign_topic' do + describe '#reassign_team_to_new_topic' do let!(:project_topic_2) { ProjectTopic.create!(topic_name: "Dummy Topic 2", assignment_id: assignment.id, max_choosers: 3) } let!(:signed_up_team) { SignedUpTeam.create!(sign_up_topic_id: project_topic.id, is_waitlisted: true, team_id: team.id) } let!(:signed_up_team_2) { SignedUpTeam.create!(sign_up_topic_id: project_topic_2.id, is_waitlisted: false, team_id: team.id) } @@ -43,7 +43,7 @@ end it 'reassigns the team to a new topic and marks them as not waitlisted' do - signed_up_team.reassign_topic(project_topic.id) + signed_up_team.reassign_team_to_new_topic(project_topic.id) expect(signed_up_team.is_waitlisted).to be_falsey expect(project_topic_2).to have_received(:drop_team_from_topic).with(team_id: team.id) end diff --git a/spec/requests/api/v1/signed_up_team_controller_spec.rb b/spec/requests/api/v1/signed_up_team_controller_spec.rb index 19d365ba8..9b679f774 100644 --- a/spec/requests/api/v1/signed_up_team_controller_spec.rb +++ b/spec/requests/api/v1/signed_up_team_controller_spec.rb @@ -36,7 +36,7 @@ end end - path '/api/v1/signed_up_teams/sign_up_student' do + path '/api/v1/signed_up_teams/signup_user' do parameter name: 'user_id', in: :query, type: :integer, description: 'User ID', required: true post 'Creates a signed up team by student' do tags 'SignedUpTeams' diff --git a/swagger/v1/swagger.yaml b/swagger/v1/swagger.yaml index 2656a98dc..2817044d8 100644 --- a/swagger/v1/swagger.yaml +++ b/swagger/v1/swagger.yaml @@ -130,6 +130,351 @@ paths: responses: '204': description: successful + "/api/v1/assignments": + get: + summary: Get assignments + tags: + - Get All Assignments + parameters: + - name: Authorization + in: header + schema: + type: string + - name: Content-Type + in: header + schema: + type: string + responses: + '200': + description: assignment successfully + "/api/v1/assignments/{assignment_id}/add_participant/{user_id}": + parameters: + - name: assignment_id + in: path + required: true + schema: + type: string + - name: user_id + in: path + required: true + schema: + type: string + post: + summary: Adds a participant to an assignment + tags: + - Assignments + parameters: + - name: Authorization + in: header + schema: + type: string + - name: Content-Type + in: header + schema: + type: string + responses: + '200': + description: participant added successfully + '404': + description: assignment not found + "/api/v1/assignments/{assignment_id}/remove_participant/{user_id}": + parameters: + - name: assignment_id + in: path + required: true + schema: + type: string + - name: user_id + in: path + required: true + schema: + type: string + delete: + summary: Removes a participant from an assignment + tags: + - Assignments + parameters: + - name: Authorization + in: header + schema: + type: string + - name: Content-Type + in: header + schema: + type: string + responses: + '200': + description: participant removed successfully + '404': + description: assignment or user not found + "/api/v1/assignments/{assignment_id}/assign_course/{course_id}": + parameters: + - name: assignment_id + in: path + required: true + schema: + type: string + - name: course_id + in: path + required: true + schema: + type: string + patch: + summary: Make course_id of assignment null + tags: + - Assignments + parameters: + - name: Authorization + in: header + schema: + type: string + - name: Content-Type + in: header + schema: + type: string + responses: + '200': + description: course_id assigned successfully + '404': + description: assignment not found + "/api/v1/assignments/{assignment_id}/remove_assignment_from_course": + patch: + summary: Removes assignment from course + tags: + - Assignments + parameters: + - name: assignment_id + in: path + required: true + schema: + type: integer + - name: Authorization + in: header + schema: + type: string + - name: Content-Type + in: header + schema: + type: string + responses: + '200': + description: assignment removed from course + '404': + description: assignment not found + "/api/v1/assignments/{assignment_id}/copy_assignment": + parameters: + - name: assignment_id + in: path + required: true + schema: + type: string + post: + summary: Copy an existing assignment + tags: + - Assignments + parameters: + - name: Authorization + in: header + schema: + type: string + - name: Content-Type + in: header + schema: + type: string + responses: + '200': + description: assignment copied successfully + '404': + description: assignment not found + "/api/v1/assignments/{id}": + parameters: + - name: id + in: path + description: Assignment ID + required: true + schema: + type: integer + delete: + summary: Delete an assignment + tags: + - Assignments + parameters: + - name: Authorization + in: header + schema: + type: string + - name: Content-Type + in: header + schema: + type: string + responses: + '200': + description: successful + '404': + description: Assignment not found + "/api/v1/assignments/{assignment_id}/has_topics": + parameters: + - name: assignment_id + in: path + description: Assignment ID + required: true + schema: + type: integer + get: + summary: Check if an assignment has topics + tags: + - Assignments + parameters: + - name: Authorization + in: header + schema: + type: string + - name: Content-Type + in: header + schema: + type: string + responses: + '200': + description: successful + '404': + description: Assignment not found + "/api/v1/assignments/{assignment_id}/team_assignment": + parameters: + - name: assignment_id + in: path + description: Assignment ID + required: true + schema: + type: integer + get: + summary: Check if an assignment is a team assignment + tags: + - Assignments + parameters: + - name: Authorization + in: header + schema: + type: string + - name: Content-Type + in: header + schema: + type: string + responses: + '200': + description: successful + '404': + description: Assignment not found + "/api/v1/assignments/{assignment_id}/valid_num_review/{review_type}": + parameters: + - name: assignment_id + in: path + description: Assignment ID + required: true + schema: + type: integer + - name: review_type + in: path + description: Review Type + required: true + schema: + type: string + get: + summary: Check if an assignment has a valid number of reviews for a specific + type + tags: + - Assignments + parameters: + - name: Authorization + in: header + schema: + type: string + - name: Content-Type + in: header + schema: + type: string + responses: + '200': + description: successful + '404': + description: Assignment not found + "/api/v1/assignments/{assignment_id}/has_teams": + parameters: + - name: assignment_id + in: path + description: Assignment ID + required: true + schema: + type: integer + get: + summary: Check if an assignment has teams + tags: + - Assignments + parameters: + - name: Authorization + in: header + schema: + type: string + - name: Content-Type + in: header + schema: + type: string + responses: + '200': + description: successful + '404': + description: Assignment not found + "/api/v1/assignments/{id}/show_assignment_details": + parameters: + - name: id + in: path + description: Assignment ID + required: true + schema: + type: integer + get: + summary: Retrieve assignment details + tags: + - Assignments + parameters: + - name: Authorization + in: header + schema: + type: string + - name: Content-Type + in: header + schema: + type: string + responses: + '200': + description: successful + '404': + description: Assignment not found + "/api/v1/assignments/{assignment_id}/varying_rubrics_by_round": + parameters: + - name: assignment_id + in: path + description: Assignment ID + required: true + schema: + type: integer + get: + summary: Check if an assignment has varying rubrics by round + tags: + - Assignments + parameters: + - name: Authorization + in: header + schema: + type: string + - name: Content-Type + in: header + schema: + type: string + responses: + '200': + description: successful + '404': + description: Assignment not found "/api/v1/bookmarks": get: summary: list bookmarks @@ -518,116 +863,345 @@ paths: delete: summary: delete institution tags: - - Institutions + - Institutions + responses: + '200': + description: successful + "/api/v1/invitations": + get: + summary: list invitations + tags: + - Invitations + responses: + '200': + description: Success + post: + summary: create invitation + tags: + - Invitations + parameters: [] + responses: + '201': + description: Create successful + '422': + description: Invalid request + requestBody: + content: + application/json: + schema: + type: object + properties: + assignment_id: + type: integer + from_id: + type: integer + to_id: + type: integer + reply_status: + type: string + required: + - assignment_id + - from_id + - to_id + "/api/v1/invitations/{id}": + parameters: + - name: id + in: path + description: id of the invitation + required: true + schema: + type: integer + get: + summary: show invitation + tags: + - Invitations + responses: + '200': + description: Show invitation + '404': + description: Not found + patch: + summary: update invitation + tags: + - Invitations + parameters: [] + responses: + '200': + description: Update successful + '422': + description: Invalid request + '404': + description: Not found + requestBody: + content: + application/json: + schema: + type: object + properties: + reply_status: + type: string + required: [] + delete: + summary: Delete invitation + tags: + - Invitations + responses: + '204': + description: Delete successful + '404': + description: Not found + "/api/v1/invitations/user/{user_id}/assignment/{assignment_id}": + parameters: + - name: user_id + in: path + description: id of user + required: true + schema: + type: integer + - name: assignment_id + in: path + description: id of assignment + required: true + schema: + type: integer + get: + summary: Show all invitation for the given user and assignment + tags: + - Invitations + responses: + '200': + description: Show all invitations for the user for an assignment + '404': + description: Not found + "/api/v1/join_team_requests/decline/{id}": + parameters: + - name: id + in: path + description: id + required: true + schema: + type: string + post: + summary: decline join_team_request + tags: + - Join Team Requests + responses: + '200': + description: successful + "/api/v1/join_team_requests": + get: + summary: list join_team_requests + tags: + - Join Team Requests + responses: + '200': + description: successful + post: + summary: create join_team_request + parameters: + - name: comments + in: query + description: comments + schema: + type: string + - name: team_id + in: query + description: team_id + schema: + type: integer + - name: assignment_id + in: query + description: assignment_id + schema: + type: integer + tags: + - Join Team Requests + responses: + '200': + description: success + '201': + description: created + '422': + description: unprocessable entity + "/api/v1/join_team_requests/{id}": + parameters: + - name: id + in: path + description: id + required: true + schema: + type: string + get: + summary: show join_team_request + tags: + - Join Team Requests + responses: + '404': + description: not_found + '200': + description: successful + patch: + summary: update join_team_request + parameters: + - name: join_team_request[comments] + in: query + description: comments + schema: + type: string + - name: join_team_request[status] + in: query + description: status + schema: + type: string + tags: + - Join Team Requests + responses: + '200': + description: successful + put: + summary: update join_team_request + parameters: + - name: join_team_request[comments] + in: query + description: comments + schema: + type: string + - name: join_team_request[status] + in: query + description: status + schema: + type: string + tags: + - Join Team Requests + responses: + '200': + description: successful + '422': + description: unprocessable entity + delete: + summary: delete join_team_request + tags: + - Join Team Requests + responses: + '204': + description: successful + '404': + description: not found + "/api/v1/project_topics": + get: + summary: Get project topics + parameters: + - name: assignment_id + in: query + description: Assignment ID + required: true + schema: + type: integer + - name: topic_ids + in: query + description: Topic Identifier + required: false + schema: + type: string + tags: + - ProjectTopic + responses: + '200': + description: successful + delete: + summary: Delete project topics + parameters: + - name: assignment_id + in: query + description: Assignment ID + required: true + schema: + type: integer + - name: topic_ids + in: query + items: + type: string + description: Topic Identifiers to delete + required: false + schema: + type: array + tags: + - ProjectTopic responses: '200': description: successful - "/api/v1/invitations": - get: - summary: list invitations - tags: - - Invitations - responses: - '200': - description: Success post: - summary: create invitation + summary: create a new topic in the sheet tags: - - Invitations + - ProjectTopic parameters: [] responses: '201': - description: Create successful - '422': - description: Invalid request + description: Success requestBody: content: application/json: schema: type: object properties: - assignment_id: - type: integer - from_id: + topic_identifier: type: integer - to_id: + topic_name: + type: string + max_choosers: type: integer - reply_status: + category: type: string + assignment_id: + type: integer + micropayment: + type: integer required: + - topic_identifier + - topic_name + - max_choosers + - category - assignment_id - - from_id - - to_id - "/api/v1/invitations/{id}": + - micropayment + "/api/v1/project_topics/{id}": parameters: - name: id in: path - description: id of the invitation + description: id of the project topic required: true schema: type: integer - get: - summary: show invitation - tags: - - Invitations - responses: - '200': - description: Show invitation - '404': - description: Not found - patch: - summary: update invitation + put: + summary: update a new topic in the sheet tags: - - Invitations + - ProjectTopic parameters: [] responses: '200': - description: Update successful - '422': - description: Invalid request - '404': - description: Not found + description: successful requestBody: content: application/json: schema: type: object properties: - reply_status: + topic_identifier: + type: integer + topic_name: type: string - required: [] - delete: - summary: Delete invitation - tags: - - Invitations - responses: - '204': - description: Delete successful - '404': - description: Not found - "/api/v1/invitations/user/{user_id}/assignment/{assignment_id}": - parameters: - - name: user_id - in: path - description: id of user - required: true - schema: - type: integer - - name: assignment_id - in: path - description: id of assignment - required: true - schema: - type: integer - get: - summary: Show all invitation for the given user and assignment - tags: - - Invitations - responses: - '200': - description: Show all invitations for the user for an assignment - '404': - description: Not found + max_choosers: + type: integer + category: + type: string + assignment_id: + type: integer + micropayment: + type: integer + required: + - topic_identifier + - topic_name + - category + - assignment_id "/api/v1/questionnaires": get: summary: list questionnaires @@ -1038,121 +1612,6 @@ paths: responses: '204': description: successful - "/api/v1/project_topics": - get: - summary: Get project topics - parameters: - - name: assignment_id - in: query - description: Assignment ID - required: true - schema: - type: integer - - name: topic_ids - in: query - description: Topic Identifier - required: false - schema: - type: string - tags: - - ProjectTopic - responses: - '200': - description: successful - delete: - summary: Delete project topics - parameters: - - name: assignment_id - in: query - description: Assignment ID - required: true - schema: - type: integer - - name: topic_ids - in: query - items: - type: string - description: Topic Identifiers to delete - required: false - schema: - type: array - tags: - - ProjectTopic - responses: - '200': - description: successful - post: - summary: create a new topic in the sheet - tags: - - ProjectTopic - parameters: [] - responses: - '201': - description: Success - requestBody: - content: - application/json: - schema: - type: object - properties: - topic_identifier: - type: integer - topic_name: - type: string - max_choosers: - type: integer - category: - type: string - assignment_id: - type: integer - micropayment: - type: integer - required: - - topic_identifier - - topic_name - - max_choosers - - category - - assignment_id - - micropayment - "/api/v1/project_topics/{id}": - parameters: - - name: id - in: path - description: id of the sign up topic - required: true - schema: - type: integer - put: - summary: update a new topic in the sheet - tags: - - ProjectTopic - parameters: [] - responses: - '200': - description: successful - requestBody: - content: - application/json: - schema: - type: object - properties: - topic_identifier: - type: integer - topic_name: - type: string - max_choosers: - type: integer - category: - type: string - assignment_id: - type: integer - micropayment: - type: integer - required: - - topic_identifier - - topic_name - - category - - assignment_id "/api/v1/signed_up_teams/sign_up": post: summary: Creates a signed up team @@ -1177,7 +1636,7 @@ paths: required: - team_id - topic_id - "/api/v1/signed_up_teams/sign_up_student": + "/api/v1/signed_up_teams/signup_user": parameters: - name: user_id in: query @@ -1186,7 +1645,7 @@ paths: schema: type: integer post: - summary: Creates a signed up team by student + summary: Creates a signed up team by user tags: - SignedUpTeams parameters: [] @@ -1279,6 +1738,43 @@ paths: description: signed up team deleted '422': description: invalid request + "/api/v1/student_tasks/list": + get: + summary: student tasks list + tags: + - StudentTasks + parameters: + - name: Authorization + in: header + schema: + type: string + responses: + '200': + description: authorized request has proper JSON schema + '401': + description: unauthorized request has error response + "/api/v1/student_tasks/view": + get: + summary: Retrieve a specific student task by ID + tags: + - StudentTasks + parameters: + - name: id + in: query + required: true + schema: + type: Integer + - name: Authorization + in: header + schema: + type: string + responses: + '200': + description: successful retrieval of a student task + '500': + description: participant not found + '401': + description: unauthorized request has error response "/login": post: summary: Logs in a user @@ -1303,34 +1799,6 @@ paths: required: - user_name - password - /api/v1/student_tasks/list: - get: - summary: List all Student Tasks - tags: - - Student Tasks - responses: - '200': - description: An array of student tasks - /api/v1/student_tasks/view: - get: - summary: View a student task - tags: - - Student Tasks - parameters: - - in: query - name: id - schema: - type: string - required: true - description: The ID of the student task to retrieve - responses: - '200': - description: A specific student task - - - - - servers: - url: http://{defaultHost} variables: From 87f3b087fa16dedc96c3ffb686c8fbc6df26feca Mon Sep 17 00:00:00 2001 From: Ajith Kanumuri <21294096+ajith05@users.noreply.github.com> Date: Thu, 12 Dec 2024 18:28:26 +0000 Subject: [PATCH 17/18] Rename reassign_topic to assign_topic_to_waitlisted_team --- app/models/project_topic.rb | 2 +- app/models/signed_up_team.rb | 6 +++--- spec/models/signed_up_team_spec.rb | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/models/project_topic.rb b/app/models/project_topic.rb index 5d0c002bd..d4c11120a 100644 --- a/app/models/project_topic.rb +++ b/app/models/project_topic.rb @@ -57,7 +57,7 @@ def drop_team_from_topic(team_id) # If the team is not waitlisted, reassign the topic to the next waitlisted team unless signed_up_team.is_waitlisted next_waitlisted_team = longest_waiting_team - next_waitlisted_team&.reassign_team_to_new_topic(self.id) + next_waitlisted_team&.assign_topic_to_waitlisted_team(self.id) end # Destroy the sign-up record for the team diff --git a/app/models/signed_up_team.rb b/app/models/signed_up_team.rb index 8c23a359b..4e6abcef3 100644 --- a/app/models/signed_up_team.rb +++ b/app/models/signed_up_team.rb @@ -43,13 +43,13 @@ def self.delete_team_signup_records(team_id) # Reassigns the team to a new topic by removing them from their current topic # and marking them as no longer waitlisted for the new topic. # NOTE: This method gets called only on a waitlisted team (See project_topic.rb -> drop_team_from_topic) - def reassign_team_to_new_topic(topic_id) + def assign_topic_to_waitlisted_team(topic_id) # Find the team's current sign-up record where they are not waitlisted # As this method gets called only on a waitlisted team, we need to check if the team has been assigned another topic - assigned_team = SignedUpTeam.where(team_id: self.team_id, is_waitlisted: false).first + assigned_team = SignedUpTeam.find_by(team_id: self.team_id, is_waitlisted: false) # If the team is already assigned to a topic, remove them from that topic - unless assigned_team.nil? + if assigned_team project_topic = ProjectTopic.find(assigned_team.sign_up_topic_id) project_topic.drop_team_from_topic(team_id: self.team_id) end diff --git a/spec/models/signed_up_team_spec.rb b/spec/models/signed_up_team_spec.rb index 96125becd..d6281800a 100644 --- a/spec/models/signed_up_team_spec.rb +++ b/spec/models/signed_up_team_spec.rb @@ -32,7 +32,7 @@ end end - describe '#reassign_team_to_new_topic' do + describe '#assign_topic_to_waitlisted_team' do let!(:project_topic_2) { ProjectTopic.create!(topic_name: "Dummy Topic 2", assignment_id: assignment.id, max_choosers: 3) } let!(:signed_up_team) { SignedUpTeam.create!(sign_up_topic_id: project_topic.id, is_waitlisted: true, team_id: team.id) } let!(:signed_up_team_2) { SignedUpTeam.create!(sign_up_topic_id: project_topic_2.id, is_waitlisted: false, team_id: team.id) } @@ -43,7 +43,7 @@ end it 'reassigns the team to a new topic and marks them as not waitlisted' do - signed_up_team.reassign_team_to_new_topic(project_topic.id) + signed_up_team.assign_topic_to_waitlisted_team(project_topic.id) expect(signed_up_team.is_waitlisted).to be_falsey expect(project_topic_2).to have_received(:drop_team_from_topic).with(team_id: team.id) end From e215850883833339f9ae5f01739b2f1d1b196388 Mon Sep 17 00:00:00 2001 From: Ajith Kanumuri <21294096+ajith05@users.noreply.github.com> Date: Thu, 12 Dec 2024 20:01:06 +0000 Subject: [PATCH 18/18] Quick Bug Fix --- app/models/project_topic.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/project_topic.rb b/app/models/project_topic.rb index d4c11120a..b3fceff8d 100644 --- a/app/models/project_topic.rb +++ b/app/models/project_topic.rb @@ -34,7 +34,7 @@ def signup_team(team_id) result = SignedUpTeam.drop_off_topic_waitlists(team_id) else # If no slots are available, add the team to the waitlist - new_signup_record.update(is_waitlisted: true, sign_up_topic_id: self.id) + result = new_signup_record.update(is_waitlisted: true, sign_up_topic_id: self.id) end result