CSC/ECE 517 Spring 2022 - E2215: Refactor student quizzes controller: Difference between revisions

From Expertiza_Wiki
Jump to navigation Jump to search
(Adding Code Snippets)
Line 13: Line 13:
'''1. Modification - ''' Added comments for custom methods in this controller explaining the purpose. <br>
'''1. Modification - ''' Added comments for custom methods in this controller explaining the purpose. <br>
'''2. Modification - ''' Line 29  - .first has been changed to .last to count the score of the most recent attempt of the quiz. <br>
'''2. Modification - ''' Line 29  - .first has been changed to .last to count the score of the most recent attempt of the quiz. <br>
student_quizzes_controller.rb:
<pre>
@response = Response.where(map_id: params[:map_id]).last
</pre>
'''3. Modification - ''' Line 33  - @map has been renamed to @quiz_response_map to more accurately describe its function. <br>
'''3. Modification - ''' Line 33  - @map has been renamed to @quiz_response_map to more accurately describe its function. <br>
student_quizzes_controller.rb:
<pre>
@quiz_response_map = ResponseMap.find(params[:map_id])
@quiz_taker = AssignmentTeam.find(@quiz_response_map.reviewee_id).participants.first
</pre>
'''4. Modification - ''' Line 34  - @participant has been changed to @quiz_taker to more accurately describe its function. <br>
'''4. Modification - ''' Line 34  - @participant has been changed to @quiz_taker to more accurately describe its function. <br>
student_quizzes_controller.rb:
<pre>
quiz_taker = AssignmentTeam.find(@quiz_response_map.reviewee_id).participants.first
</pre>
finished_quiz.html.erb:
<pre>
<%  @assignment = Assignment.find(@quiz_taker.parent_id)%>
<%= render :partial => 'submitted_content/main', :locals => {:participant => @quiz_taker, :stage => ""} %>
</pre>
'''5. Modification - ''' Line 39  - The method comment of self.take_quiz has been updated to describe its function <br>
'''5. Modification - ''' Line 39  - The method comment of self.take_quiz has been updated to describe its function <br>
student_quizzes_controller.rb:
<pre>
# Lists all the available quizzes created by the other teams in the current project which can be attempted.
</pre>
'''6. Modification - ''' Controller Method calculate_score has been split into three separate functions and has been moved to student_quizzes_helper.rb. <br>
'''6. Modification - ''' Controller Method calculate_score has been split into three separate functions and has been moved to student_quizzes_helper.rb. <br>
student_quizzes_helper.rb:
<pre>
module StudentQuizzesHelper
  # This method as whole fetches the answers provided and calculates the final scores for the quiz.
  # Also calls seperate methods for handling single answer/ true or false evaluations and mulitple answer evaluations for calculating score.
  def calculate_score(map, response)
    questionnaire = Questionnaire.find(map.reviewed_object_id)
    answers = []
    has_response = true
    questions = Question.where(questionnaire_id: questionnaire.id)
    questions.each do |question|
      correct_answers = QuizQuestionChoice.where(question_id: question.id, iscorrect: true)
      ques_type = question.type
      if ques_type.eql? 'MultipleChoiceCheckbox'
        has_response = multiple_answer_evaluation(answers, params, question, correct_answers, has_response, response)
      # TrueFalse and MultipleChoiceRadio
      else
        has_response = single_answer_evaluation(answers, params, question, correct_answers, has_response, response)
      end
    end
    if has_response
      answers.each(&:save)
      redirect_to controller: 'student_quizzes', action: 'finished_quiz', map_id: map.id
    else
      response.destroy
      flash[:error] = 'Please answer every question.'
      redirect_to action: :take_quiz, assignment_id: params[:assignment_id], questionnaire_id: questionnaire.id, map_id: map.id
    end
  end
  # Evaluates scores for questions that contains multiple answers
  def multiple_answer_evaluation(answers, params, question, correct_answers, has_response, response)
    score = 0
    if params[question.id.to_s].nil?
        has_response = false
      else
        params[question.id.to_s].each do |choice|
          # loop the quiz taker's choices and see if 1)all the correct choice are checked and 2) # of quiz taker's choice matches the # of the correct choices
          correct_answers.each do |correct|
            score += 1 if choice.eql? correct.txt
          end
        end
        score = score == correct_answers.count && score == params[question.id.to_s].count ? 1 : 0
        # for MultipleChoiceCheckbox, score =1 means the quiz taker have done this question correctly, not just make select this choice correctly.
        params[question.id.to_s].each do |choice|
          new_answer = Answer.new comments: choice, question_id: question.id, response_id: response.id, answer: score
          has_response = false unless new_answer.valid?
          answers.push(new_answer)
        end
    end
    return has_response
  end
  # Evaluates scores for questions that contains only single/ true or false answers
  def single_answer_evaluation(answers, params, question, correct_answers, has_response, response)
    correct_answer = correct_answers.first
    score = correct_answer.txt == params[question.id.to_s] ? 1 : 0
    new_score = Answer.new comments: params[question.id.to_s], question_id: question.id, response_id: response.id, answer: score
    has_response = false if new_score.nil? || new_score.comments.nil? || new_score.comments.empty?
    answers.push(new_score)
    return has_response
  end
end
</pre>
'''7. Modification - ''' Line 60  - A method comment has been added for record_response to describe its function <br>
'''7. Modification - ''' Line 60  - A method comment has been added for record_response to describe its function <br>
student_quizzes_controller.rb:
<pre>
# Stores the answers entered by the quiz taker and calculates the score based on the answers entered.
</pre>
'''8. Modification - ''' -graded? has been removed as it is no longer necessary.<br>
'''8. Modification - ''' -graded? has been removed as it is no longer necessary.<br>
'''9. Modification - ''' Line 72  - The Flash message has been updated. <br>
'''9. Modification - ''' Line 72  - The Flash message has been updated. <br>
student_quizzes_controller.rb:
<pre>
flash[:error] = 'You have already taken this quiz. Below are the responses of your previous attempt.'
<pre/>
'''10. Modification - ''' Line 77  - The method comment for review_questions has been updated. <br>
'''10. Modification - ''' Line 77  - The method comment for review_questions has been updated. <br>
student_quizzes_controller.rb:
<pre>
# This method is only for quiz questionnaires, it is called when instructors click "view quiz questions" on the pop-up panel.
# Using the current assignment id parameter, fetches all the questions for each quiz and finally lists all the answers and scores for each submission.
</pre>


==Tests on the Controller Methods==
==Tests on the Controller Methods==


==Video on changes made==
==Video on changes made==

Revision as of 23:56, 21 March 2022

About Expertiza

Expertiza is a multi-purpose web application built using Ruby on Rails for Students and Instructors. Instructors enrolled in Expertiza can create and customize classes, teams, assignments, quizzes, and many more. On the other hand, Students are also allowed to form teams, attempt quizzes, and complete assignments. Apart from that, Expertiza also allows students to provide peer reviews enabling them to work together to improve others' learning experiences. It is an open-source application and its Github repository is Expertiza.

E2215. Refactoring student_quizzes_controller.rb in Expertiza

This page is a description of Expertiza OSS project E2215, which is refactoring the student_quizzes_controller.rb file.

Controller Description

The student_quizzes_controller consists of methods involved in creating, scoring & recording responses of the quizzes taken by reviewers or students of the other teams with the same assignment. This controller has some issues that violate essential Rails design principles such as DRY principle. There are few methods in this controller that should have been in model classes. Some methods share code, which creates code repetition. Some method comments needs to be rewritten.

Files Modified

1. student_quizzes_controller.rb
2. finished_quiz.html
3. student_quizzes_helper.rb

Modifications made to the existing code

1. Modification - Added comments for custom methods in this controller explaining the purpose.
2. Modification - Line 29 - .first has been changed to .last to count the score of the most recent attempt of the quiz.
student_quizzes_controller.rb:

@response = Response.where(map_id: params[:map_id]).last

3. Modification - Line 33 - @map has been renamed to @quiz_response_map to more accurately describe its function.
student_quizzes_controller.rb:

@quiz_response_map = ResponseMap.find(params[:map_id])
@quiz_taker = AssignmentTeam.find(@quiz_response_map.reviewee_id).participants.first

4. Modification - Line 34 - @participant has been changed to @quiz_taker to more accurately describe its function.
student_quizzes_controller.rb:

quiz_taker = AssignmentTeam.find(@quiz_response_map.reviewee_id).participants.first

finished_quiz.html.erb:

<%  @assignment = Assignment.find(@quiz_taker.parent_id)%>
<%= render :partial => 'submitted_content/main', :locals => {:participant => @quiz_taker, :stage => ""} %>

5. Modification - Line 39 - The method comment of self.take_quiz has been updated to describe its function
student_quizzes_controller.rb:

# Lists all the available quizzes created by the other teams in the current project which can be attempted.

6. Modification - Controller Method calculate_score has been split into three separate functions and has been moved to student_quizzes_helper.rb.
student_quizzes_helper.rb:

module StudentQuizzesHelper

  # This method as whole fetches the answers provided and calculates the final scores for the quiz.
  # Also calls seperate methods for handling single answer/ true or false evaluations and mulitple answer evaluations for calculating score.
  def calculate_score(map, response)
    questionnaire = Questionnaire.find(map.reviewed_object_id)
    answers = []
    has_response = true
    questions = Question.where(questionnaire_id: questionnaire.id)
    questions.each do |question|
      correct_answers = QuizQuestionChoice.where(question_id: question.id, iscorrect: true)
      ques_type = question.type
      if ques_type.eql? 'MultipleChoiceCheckbox'
        has_response = multiple_answer_evaluation(answers, params, question, correct_answers, has_response, response)
      # TrueFalse and MultipleChoiceRadio
      else
        has_response = single_answer_evaluation(answers, params, question, correct_answers, has_response, response)
      end
    end
    if has_response
      answers.each(&:save)
      redirect_to controller: 'student_quizzes', action: 'finished_quiz', map_id: map.id
    else
      response.destroy
      flash[:error] = 'Please answer every question.'
      redirect_to action: :take_quiz, assignment_id: params[:assignment_id], questionnaire_id: questionnaire.id, map_id: map.id
    end
  end

  # Evaluates scores for questions that contains multiple answers
  def multiple_answer_evaluation(answers, params, question, correct_answers, has_response, response)
    score = 0
    if params[question.id.to_s].nil?
        has_response = false
      else
        params[question.id.to_s].each do |choice|
          # loop the quiz taker's choices and see if 1)all the correct choice are checked and 2) # of quiz taker's choice matches the # of the correct choices
          correct_answers.each do |correct|
            score += 1 if choice.eql? correct.txt
          end
        end
        score = score == correct_answers.count && score == params[question.id.to_s].count ? 1 : 0
        # for MultipleChoiceCheckbox, score =1 means the quiz taker have done this question correctly, not just make select this choice correctly.
        params[question.id.to_s].each do |choice|
          new_answer = Answer.new comments: choice, question_id: question.id, response_id: response.id, answer: score

          has_response = false unless new_answer.valid?
          answers.push(new_answer)
        end
    end
    return has_response
  end

  # Evaluates scores for questions that contains only single/ true or false answers
  def single_answer_evaluation(answers, params, question, correct_answers, has_response, response)
    correct_answer = correct_answers.first
    score = correct_answer.txt == params[question.id.to_s] ? 1 : 0
    new_score = Answer.new comments: params[question.id.to_s], question_id: question.id, response_id: response.id, answer: score
    has_response = false if new_score.nil? || new_score.comments.nil? || new_score.comments.empty?
    answers.push(new_score)
    return has_response
  end

end

7. Modification - Line 60 - A method comment has been added for record_response to describe its function
student_quizzes_controller.rb:

# Stores the answers entered by the quiz taker and calculates the score based on the answers entered.

8. Modification - -graded? has been removed as it is no longer necessary.
9. Modification - Line 72 - The Flash message has been updated.
student_quizzes_controller.rb:

flash[:error] = 'You have already taken this quiz. Below are the responses of your previous attempt.'
<pre/>
'''10. Modification - ''' Line 77  - The method comment for review_questions has been updated. <br>
student_quizzes_controller.rb:
<pre>
# This method is only for quiz questionnaires, it is called when instructors click "view quiz questions" on the pop-up panel.
# Using the current assignment id parameter, fetches all the questions for each quiz and finally lists all the answers and scores for each submission.

Tests on the Controller Methods

Video on changes made