CSC/ECE 517 Spring 2018- Project E1808: Refactor review mapping controller.rb: Difference between revisions

From Expertiza_Wiki
Jump to navigation Jump to search
No edit summary
No edit summary
Line 80: Line 80:
     # Create teams if its an individual assignment.
     # Create teams if its an individual assignment.
     helper.create_teams_if_individual_assignment
     helper.create_teams_if_individual_assignment
     if helper.check_if_all_artifacts_num_are_zero(flash,params)
     if helper.check_if_all_artifacts_num_are_zero(params)
       helper.assign_reviews_for_artifacts_num_zero(flash,params) {automatic_review_mapping_strategy(helper.assignment_id, helper.participants, helper.teams, helper.student_review_num, helper.submission_review_num)}
       helper.assign_reviews_for_artifacts_num_zero(params) do |error|
        if error
          flash[:error] = error.message
        else
        automatic_review_mapping_strategy(helper.assignment_id, helper.participants, helper.teams, helper.student_review_num, helper.submission_review_num)
        end
        end
     else
     else
       helper.assign_reviews_for_artifacts_num_not_zero(params) do
       helper.assign_reviews_for_artifacts_num_not_zero(params) do
Line 92: Line 98:
       end
       end
     end
     end
    redirect_to action: 'list_mappings', id: helper.assignment_id
  end
</pre>
</pre>


Line 107: Line 116:
<pre>
<pre>
def initialize(params)
def initialize(params)
    @assignment_id = params[:id].to_i
@assignment_id = params[:id].to_i
    @participants = AssignmentParticipant.where(parent_id: params[:id].to_i).to_a.reject {|p| p.can_review == false }.shuffle!
    @participants = AssignmentParticipant.where(parent_id: params[:id].to_i).to_a.reject {|p| p.can_review == false }.shuffle!
    @teams = AssignmentTeam.where(parent_id: params[:id].to_i).to_a.shuffle!
    @teams = AssignmentTeam.where(parent_id: params[:id].to_i).to_a.shuffle!
        @max_team_size = Integer(params[:max_team_size]) # Assignment.find(assignment_id).max_team_size
    @max_team_size = Integer(params[:max_team_size]) # Assignment.find(assignment_id).max_team_size
        @student_review_num = params[:num_reviews_per_student].to_i
    @student_review_num = params[:num_reviews_per_student].to_i
        @submission_review_num = params[:num_reviews_per_submission].to_i
    @submission_review_num = params[:num_reviews_per_submission].to_i
        @calibrated_artifacts_num = params[:num_calibrated_artifacts].to_i
    @calibrated_artifacts_num = params[:num_calibrated_artifacts].to_i
        @uncalibrated_artifacts_num = params[:num_uncalibrated_artifacts].to_i
    @uncalibrated_artifacts_num = params[:num_uncalibrated_artifacts].to_i
        @teams_with_calibrated_artifacts = []
    @teams_with_calibrated_artifacts = []
          @teams_with_uncalibrated_artifacts = []
      @teams_with_uncalibrated_artifacts = []
     end
     end
</pre>
</pre>
Line 123: Line 132:
<pre>  
<pre>  
   def create_teams_if_individual_assignment
   def create_teams_if_individual_assignment
        if @teams.empty? and @max_team_size == 1
    if @teams.empty? and @max_team_size == 1
    @participants.each do |participant|
    @participants.each do |participant|
        next if TeamsUser.team_id(@assignment_id, participant.user.id)
        next if TeamsUser.team_id(@assignment_id, participant.user.id)
        team = AssignmentTeam.create_team_and_node(@assignment_id)
        team = AssignmentTeam.create_team_and_node(@assignment_id)
        ApplicationController.helpers.create_team_users(participant.user, team.id)
        ApplicationController.helpers.create_team_users(participant.user, team.id)
        @teams << team
        @teams << team
                end
                end
        end
    end
     end
     end
</pre>
</pre>
Line 137: Line 146:
This method checks for the value of num_calibrated_artifacts and uncalibrated_artifacts. If both are zero, it returns true to the controller and the controller then calls the assign_reviews_for_artifacts_num_zero method. If both the values are not zero, 'false' s returned to the controller and controller calls assign_reviews_for_artifacts_num_not_zero method
This method checks for the value of num_calibrated_artifacts and uncalibrated_artifacts. If both are zero, it returns true to the controller and the controller then calls the assign_reviews_for_artifacts_num_zero method. If both the values are not zero, 'false' s returned to the controller and controller calls assign_reviews_for_artifacts_num_not_zero method
<pre>
<pre>
     def check_if_all_artifacts_num_are_zero(flash,params)
     def check_if_all_artifacts_num_are_zero(params)
    
    
      if @calibrated_artifacts_num == 0 and @uncalibrated_artifacts_num == 0
    if @calibrated_artifacts_num == 0 and @uncalibrated_artifacts_num == 0
    true
    true
     #check_review_num_before_assigning_review(flash,obj,params)
     else
      else
    false
    false
     end
     #artifacts_num_not_zero(obj,params)
      end


     end
     end
</pre>
</pre>


Line 156: Line 164:
<pre>
<pre>


     def assign_reviews_for_artifacts_num_zero(flash,params)
     def assign_reviews_for_artifacts_num_zero(params)
    
    
    if @student_review_num == 0 and @submission_review_num == 0
    if @student_review_num == 0 and @submission_review_num == 0
        flash[:error] = "Please choose either the number of reviews per student or the number of reviewers per team (student)."
    begin
      elsif (@student_review_num != 0 and @submission_review_num == 0) or (@tudent_review_num == 0 and @submission_review_num != 0)
        raise "Please choose either the number of reviews per student or the number of reviewers per team (student)."
        # REVIEW: mapping strategy
        rescue Exception => e
                    yield
        yield e
      else
            end
        flash[:error] = "Please choose either the number of reviews per student or the number of reviewers per team (student), not both."
        #flash[:error] = "Please choose either the number of reviews per student or the number of reviewers per team (student)."
            end
      elsif (@student_review_num != 0 and @submission_review_num == 0) or (@tudent_review_num == 0 and @submission_review_num != 0)
        # REVIEW: mapping strategy
            yield
      else
      begin
        raise "Please choose either the number of reviews per student or the number of reviewers per team (student), not both."
        rescue Exception => e
        yield e
        #flash[:error] = "Please choose either the number of reviews per student or the number of reviewers per team (student), not both."
            end
         
            end
     end
     end
</pre>
</pre>
Line 172: Line 191:
     def assign_reviews_for_artifacts_num_not_zero(params)
     def assign_reviews_for_artifacts_num_not_zero(params)
    
    
          @teams_with_calibrated_artifacts = []
    ReviewResponseMap.where(reviewed_object_id: @assignment_id, calibrate_to: 1).each do |response_map|
        @teams_with_uncalibrated_artifacts = []
        @teams_with_calibrated_artifacts << AssignmentTeam.find(response_map.reviewee_id)
      end
      @teams_with_uncalibrated_artifacts = @teams - @teams_with_calibrated_artifacts
      # REVIEW: mapping strategy
      yield
     
      # REVIEW: mapping strategy
      # since after first mapping, participants (delete_at) will be nil
      @participants = AssignmentParticipant.where(parent_id: params[:id].to_i).to_a.reject {|p| p.can_review == false }.shuffle!
      yield
     


        ReviewResponseMap.where(reviewed_object_id: @assignment_id, calibrate_to: 1).each do |response_map|
    end
          @teams_with_calibrated_artifacts << AssignmentTeam.find(response_map.reviewee_id)
        end
        @teams_with_uncalibrated_artifacts = @teams - @teams_with_calibrated_artifacts
        # REVIEW: mapping strategy
        yield
     
        # REVIEW: mapping strategy
          # since after first mapping, participants (delete_at) will be nil
        @participants = AssignmentParticipant.where(parent_id: params[:id].to_i).to_a.reject {|p| p.can_review == false }.shuffle!
        yield
        end
    end
end
</pre>
</pre>


Line 194: Line 210:


<pre>
<pre>
  if helper.check_if_all_artifacts_num_are_zero(flash,params)
  if helper.check_if_all_artifacts_num_are_zero(params)
       helper.assign_reviews_for_artifacts_num_zero(flash,params) {automatic_review_mapping_strategy(helper.assignment_id, helper.participants, elper.teams,  
       helper.assign_reviews_for_artifacts_num_zero(params) do |error|
      helper.student_review_num, helper.submission_review_num)}
        if error
else
          flash[:error] = error.message
        else
        automatic_review_mapping_strategy(helper.assignment_id, helper.participants, helper.teams, helper.student_review_num, helper.submission_review_num)
        end
        end
    else
       helper.assign_reviews_for_artifacts_num_not_zero(params) do
       helper.assign_reviews_for_artifacts_num_not_zero(params) do
      automatic_review_mapping_strategy(helper.assignment_id, helper.participants, helper.teams_with_calibrated_artifacts.shuffle!,
        automatic_review_mapping_strategy(helper.assignment_id, helper.participants, helper.teams_with_calibrated_artifacts.shuffle!, helper.calibrated_artifacts_num, 0) if count == 0
      helper.calibrated_artifacts_num, 0) if count == 0
        count = count+1
      count = count+1
        if count == 2  
      if count == 2  
          automatic_review_mapping_strategy(helper.assignment_id, helper.participants, helper.teams_with_uncalibrated_artifacts.shuffle!, helper.uncalibrated_artifacts_num, 0)
      automatic_review_mapping_strategy(helper.assignment_id, helper.participants, helper.teams_with_uncalibrated_artifacts.shuffle!,  
          count == 0
      helper.uncalibrated_artifacts_num, 0)
        end
      count == 0
      end
      end
end
</pre>
</pre>

Revision as of 00:26, 27 March 2018

Introduction

Expertiza

Expertiza is a platform through which students are able to view and manage their assignments, form teams for a group project and review other team's work for improvement. It uses Ruby on Rails Framework

Objectives of the Project

  • Refactor automatic_review_mapping method
    • Write failing tests first
    • Split into several simpler methods and assign reasonable names
    • Extract duplicated code into separate methods
  • Refactor response_report method
    • Write failing tests first
    • Use factory pattern to replace the long switch statements
    • Move switch conditions to corresponding subclasses with same method name response_report
    • Create corresponding helper classes for different report types if they do not exist
    • Replace the conditional with the relevant method calls

Implementation

About Review Mapping Controller

Review mapping controller handles the review responses that are done on every assignment. It assigns reviews to different teams or individual student depending on the type of assignment(team project or individual project). It keeps track that all the teams are reviewed and assigned teams to review too.

Refactoring

Refactoring is used to improve a design code by changing the structure of the code such that the functionality remains the same. The changes made are quite small, but the overall effect becomes significant.

Refactoring automatic_review_mapping method

As this method was long and complex it needed to be broken down into smaller methods with specific functionality.

Original:

def automatic_review_mapping
    assignment_id = params[:id].to_i
    participants = AssignmentParticipant.where(parent_id: params[:id].to_i).to_a.reject {|p| p.can_review == false }.shuffle!
    teams = AssignmentTeam.where(parent_id: params[:id].to_i).to_a.shuffle!
    max_team_size = Integer(params[:max_team_size]) # Assignment.find(assignment_id).max_team_size
    # Create teams if its an individual assignment.
    if teams.empty? and max_team_size == 1
      participants.each do |participant|
        user = participant.user
        next if TeamsUser.team_id(assignment_id, user.id)
        team = AssignmentTeam.create_team_and_node(assignment_id)
        ApplicationController.helpers.create_team_users(participant.user, team.id)
        teams << team
      end
    end
    student_review_num = params[:num_reviews_per_student].to_i
    submission_review_num = params[:num_reviews_per_submission].to_i
    calibrated_artifacts_num = params[:num_calibrated_artifacts].to_i
    uncalibrated_artifacts_num = params[:num_uncalibrated_artifacts].to_i

    if calibrated_artifacts_num == 0 and uncalibrated_artifacts_num == 0
      if student_review_num == 0 and submission_review_num == 0
        flash[:error] = "Please choose either the number of reviews per student or the number of reviewers per team (student)."
      elsif (student_review_num != 0 and submission_review_num == 0) or (student_review_num == 0 and submission_review_num != 0)
        # REVIEW: mapping strategy
        automatic_review_mapping_strategy(assignment_id, participants, teams, student_review_num, submission_review_num)
      else
        flash[:error] = "Please choose either the number of reviews per student or the number of reviewers per team (student), not both."
      end
    else
      teams_with_calibrated_artifacts = []
      teams_with_uncalibrated_artifacts = []
      ReviewResponseMap.where(reviewed_object_id: assignment_id, calibrate_to: 1).each do |response_map|
        teams_with_calibrated_artifacts << AssignmentTeam.find(response_map.reviewee_id)
      end
      teams_with_uncalibrated_artifacts = teams - teams_with_calibrated_artifacts
      # REVIEW: mapping strategy
      automatic_review_mapping_strategy(assignment_id, participants, teams_with_calibrated_artifacts.shuffle!, calibrated_artifacts_num, 0)
      # REVIEW: mapping strategy
      # since after first mapping, participants (delete_at) will be nil
      participants = AssignmentParticipant.where(parent_id: params[:id].to_i).to_a.reject {|p| p.can_review == false }.shuffle!
      automatic_review_mapping_strategy(assignment_id, participants, teams_with_uncalibrated_artifacts.shuffle!, uncalibrated_artifacts_num, 0)
    end
    redirect_to action: 'list_mappings', id: assignment_id
  end

After Refactoring:

def automatic_review_mapping
    count = 0 # this variable is used to control whether the calibrated or the uncalibrated automatic_review_map_controller_strategy will be called on yield.
    helper = AutomaticReviewMappingHelper::AutomaticReviewMappingHelper.new(params)
    # Create teams if its an individual assignment.
    helper.create_teams_if_individual_assignment
    if helper.check_if_all_artifacts_num_are_zero(params)
       helper.assign_reviews_for_artifacts_num_zero(params) do |error|
        if error
          flash[:error] = error.message
        else
        automatic_review_mapping_strategy(helper.assignment_id, helper.participants, helper.teams, helper.student_review_num, helper.submission_review_num)
        end
        end
    else
       helper.assign_reviews_for_artifacts_num_not_zero(params) do
        automatic_review_mapping_strategy(helper.assignment_id, helper.participants, helper.teams_with_calibrated_artifacts.shuffle!, helper.calibrated_artifacts_num, 0) if count == 0
        count = count+1
        if count == 2 
          automatic_review_mapping_strategy(helper.assignment_id, helper.participants, helper.teams_with_uncalibrated_artifacts.shuffle!, helper.uncalibrated_artifacts_num, 0)
          count == 0
        end
      end
    end

    redirect_to action: 'list_mappings', id: helper.assignment_id
  end

Approach

As seen, the method originally was very long and complex. So, the method was broken down into smaller methods. These smaller methods perform various tasks in the review mapping controller. As tey show similar behavior they were grouped in a helper class: AutomaticReviewMappingHelper.

Methods

  1. initialize
  2. create_teams_if_individual_assignment
  3. check_if_all_artifacts_num_are_zero
  4. assign_reviews_for_artifacts_num_zero
  5. assign_reviews_for_artifacts_num_not_zero

The common parameters that are used in the automatic review mapping method are defined here so that they can be used as instance variables throughout for better flow of the code.

	def initialize(params)
		@assignment_id = params[:id].to_i
    		@participants = AssignmentParticipant.where(parent_id: params[:id].to_i).to_a.reject {|p| p.can_review == false }.shuffle!
    		@teams = AssignmentTeam.where(parent_id: params[:id].to_i).to_a.shuffle!
    		@max_team_size = Integer(params[:max_team_size]) # Assignment.find(assignment_id).max_team_size
    		@student_review_num = params[:num_reviews_per_student].to_i
    		@submission_review_num = params[:num_reviews_per_submission].to_i
    		@calibrated_artifacts_num = params[:num_calibrated_artifacts].to_i
    		@uncalibrated_artifacts_num = params[:num_uncalibrated_artifacts].to_i
    		@teams_with_calibrated_artifacts = []
      		@teams_with_uncalibrated_artifacts = []
    	end


 
   	def create_teams_if_individual_assignment
    		if @teams.empty? and @max_team_size == 1
     			 @participants.each do |participant|
        		 next if TeamsUser.team_id(@assignment_id, participant.user.id)
        		 team = AssignmentTeam.create_team_and_node(@assignment_id)
        		 ApplicationController.helpers.create_team_users(participant.user, team.id)
        		 @teams << team
                 end
    		end
    	end


This method checks for the value of num_calibrated_artifacts and uncalibrated_artifacts. If both are zero, it returns true to the controller and the controller then calls the assign_reviews_for_artifacts_num_zero method. If both the values are not zero, 'false' s returned to the controller and controller calls assign_reviews_for_artifacts_num_not_zero method

    	def check_if_all_artifacts_num_are_zero(params)
    		
    		if @calibrated_artifacts_num == 0 and @uncalibrated_artifacts_num == 0
    			true
    		else
    			false
    		end

    	end


Use of 'Yield'

The keyword yield is used in the two helper class methods( assign_reviews_for_artifacts_num_zero and assign_reviews_for_artifacts_num_not_zero) as the functionality of these two methods require to call a controller method(automatic_review_mapping_strategy) inside them. As it is not a good practice to call controller methods in helper call, we use yield so that the method call still remains in the controller.


    	def assign_reviews_for_artifacts_num_zero(params)
    		
    			if @student_review_num == 0 and @submission_review_num == 0
    				begin
        			raise "Please choose either the number of reviews per student or the number of reviewers per team (student)."
        			rescue Exception => e
        			yield e
        		    end
        			#flash[:error] = "Please choose either the number of reviews per student or the number of reviewers per team (student)."
      			elsif (@student_review_num != 0 and @submission_review_num == 0) or (@tudent_review_num == 0 and @submission_review_num != 0)
        			# REVIEW: mapping strategy
             		yield
      			else
      				begin
        			raise "Please choose either the number of reviews per student or the number of reviewers per team (student), not both."
        			rescue Exception => e
        			yield e
        			#flash[:error] = "Please choose either the number of reviews per student or the number of reviewers per team (student), not both."
            		end
           
            	end
    	end
    	def assign_reviews_for_artifacts_num_not_zero(params)
    		
    		ReviewResponseMap.where(reviewed_object_id: @assignment_id, calibrate_to: 1).each do |response_map|
        	@teams_with_calibrated_artifacts << AssignmentTeam.find(response_map.reviewee_id)
      		end
      		@teams_with_uncalibrated_artifacts = @teams - @teams_with_calibrated_artifacts
      		# REVIEW: mapping strategy
      		yield
      		
      		# REVIEW: mapping strategy
      		# since after first mapping, participants (delete_at) will be nil
      		@participants = AssignmentParticipant.where(parent_id: params[:id].to_i).to_a.reject {|p| p.can_review == false }.shuffle!
      		yield
      		

    	end

In the Controller Method:

 if helper.check_if_all_artifacts_num_are_zero(params)
       helper.assign_reviews_for_artifacts_num_zero(params) do |error|
        if error
          flash[:error] = error.message
        else
        automatic_review_mapping_strategy(helper.assignment_id, helper.participants, helper.teams, helper.student_review_num, helper.submission_review_num)
        end
        end
    else
       helper.assign_reviews_for_artifacts_num_not_zero(params) do
        automatic_review_mapping_strategy(helper.assignment_id, helper.participants, helper.teams_with_calibrated_artifacts.shuffle!, helper.calibrated_artifacts_num, 0) if count == 0
        count = count+1
        if count == 2 
          automatic_review_mapping_strategy(helper.assignment_id, helper.participants, helper.teams_with_uncalibrated_artifacts.shuffle!, helper.uncalibrated_artifacts_num, 0)
          count == 0
        end
      end