CSC/ECE 517 Fall 2016/E1640. Refactor response.rb and response helper.rb
Introduction to Expertiza
Expertiza is a web application developed using Ruby on Rails for online assignment assessment. It is an open source project and its code base is maintained in the Git repository. Expertiza authorizes the instructor to create new assignments as well as modify the existing ones. Students can team up with other students in Expertiza to work on various projects and assignments. Students can also review other students' work, thus enabling peer learning. After a particular response is recorded, the rubric gets displayed in graphical form under Your Scores tab. The submitted work can be seen in Your Work tab and the feedback given can be seen in Others' Work. The teammates for a particular project can be seen in Your team tab.
Overview
Response Model
Response.rb is a class that manages the response to all rubrics in Expertiza. The review form, quiz attempt, teammate review, review for others' work, filling a survey are all treated as responses. Method display_as_html handles the front-end for displaying a rubric.
Helper module
Response_helper.rb
It contains two kinds of methods:
1) Methods related to the display of questionnaires.
2) Methods to open a rubric on selecting a particular section name.
This contains a method that rearranges the questions based on the frequency of answers to the questions.
Project Requirements
In response.rb:
1) The class contains code that checks the rubric type (ReviewResponseMap, MetareviewResponseMap, FeedbackResponseMap, TeammateReviewResponseMap). These classes are sub-classes of ResponseMap. Rather than checking the map type, the code should send a message to the ResponseMap, and the code for specific types of ResponseMaps should be implemented in the subclasses of ResponseMap.
2) Code for calculating scores seems to be incorrect, as it should multiply per-question scores by the weight for each question. It seems to ignore weighting.
3) Most of the code for sending e-mails should be moved to app/mailers.
4) Remove the comment in lines 25–27 about TeamResponseMap; it is no longer relevant to the current version.
In response_helper.rb:
1)The rearrange_questions method was written before we added sequence numbers to questions. At the very least, it needs to be modified so that questions above the threshold are kept in sequence. Ideally, it would be extended to allow the person who creates the rubric to specify whether each question could be moved or not, and then rearrange only the questions that are allowed to be moved, and add a column of check boxes to the Rubrics tab for assignment creation, saying whether questions on a particular rubric can be rearranged or not.
Issues addressed
In response.rb
1) The functionality for email notification was initially cluttered in a single class Response. In the current version, this functionality is been divided into sub-classes depending on the response type. This has facilitated improved modularity and segregation of functionality into different classes.
2) In the current version of the code, the total score is calculated by considering the weights for each question.
3) The code for email notification is in the mailer module in the current version and the syn_message function is used for sending synchronous email messages after a response is entered.
4) The irrelevant lines from TeamResponseMap are removed.
In response_helper.rb
1) The response_helper.rb is removed in the current version since it is dead code and not referenced anywhere in the code.
Code Modifications
The email notification functionality was initially in Response.rb and was shifted to the sub-classes as below:
Before Refactoring :
Model: Response.rb
def email() if response_map.type == "ReviewResponseMap" defn[:body][:type] = "Author Feedback" AssignmentTeam.find(response_map.reviewee_id).users.each do |user| defn[:body][:obj_name] = if assignment.has_topics? SignUpTopic.find(SignedUpTeam.topic_id(assignment.id, user.id)).topic_name else assignment.name end defn[:body][:first_name] = User.find(user.id).fullname defn[:to] = User.find(user.id).email Mailer.sync_message(defn).deliver_now end if response_map.type == "MetareviewResponseMap" defn[:body][:type] = "Metareview" reviewee_user = Participant.find(response_map.reviewee_id) signup_topic_id = SignedUpTeam.topic_id(assignment.id, response_map.contributor.teams_users.first.user_id) defn[:body][:obj_name] = SignUpTopic.find(signup_topic_id).topic_name defn[:body][:first_name] = User.find(reviewee_user.user_id).fullname defn[:to] = User.find(reviewee_user.user_id).email Mailer.sync_message(defn).deliver end if response_map.type == "FeedbackResponseMap" # This is authors' feedback from UI defn[:body][:type] = "Review Feedback" # reviewee is a response, reviewer is a participant # we need to track back to find the original reviewer on whose work the author comments response_id_for_original_feedback = response_map.reviewed_object_id response_for_original_feedback = Response.find response_id_for_original_feedback response_map_for_original_feedback = ResponseMap.find response_for_original_feedback.map_id original_reviewer_participant_id = response_map_for_original_feedback.reviewer_id participant = AssignmentParticipant.find(original_reviewer_participant_id) topic_id = SignedUpTeam.topic_id(participant.parent_id, participant.user_id) defn[:body][:obj_name] = if topic_id.nil? assignment.name else SignUpTopic.find(topic_id).topic_name end user = User.find(participant.user_id) defn[:to] = user.email defn[:body][:first_name] = user.fullname Mailer.sync_message(defn).deliver end if response_map.type == "TeammateReviewResponseMap" defn[:body][:type] = "Teammate Review" participant = AssignmentParticipant.find(response_map.reviewee_id) topic_id = SignedUpTeam.topic_id(participant.parent_id, participant.user_id) defn[:body][:obj_name] = SignUpTopic.find(topic_id).topic_name rescue nil user = User.find(participant.user_id) defn[:body][:first_name] = user.fullname defn[:to] = user.email Mailer.sync_message(defn).deliver end end
After Refactoring :
Model changed: Response.rb, ReviewResponseMap.rb, MetareviewResponseMap.rb, FeedbackResponseMap.rb, TeammateReviewResponseMap.rb
Function added: email
In ReviewResponseMap
def email(defn,assignment,participant) defn[:body][:type] = "Author Feedback" AssignmentTeam.find(reviewee_id).users.each do |user| defn[:body][:obj_name] = if assignment.has_topics? SignUpTopic.find(SignedUpTeam.topic_id(assignment.id, user.id)).topic_name else assignment.name end defn[:body][:first_name] = User.find(user.id).fullname defn[:to] = User.find(user.id).email Mailer.sync_message(defn).deliver_now end end
In MetareviewResponseMap
def email(defn,participant,assignment) defn[:body][:type] = "Metareview" reviewee_user = Participant.find(reviewee_id) signup_topic_id = SignedUpTeam.topic_id(assignment.id, contributor.teams_users.first.user_id) defn[:body][:obj_name] = SignUpTopic.find(signup_topic_id).topic_name defn[:body][:first_name] = User.find(reviewee_user.user_id).fullname defn[:to] = User.find(reviewee_user.user_id).email Mailer.sync_message(defn).deliver end
In FeedbackResponseMap
def email(defn,assignment,participant) defn[:body][:type] = "Review Feedback" # reviewee is a response, reviewer is a participant # we need to track back to find the original reviewer on whose work the author comments response_id_for_original_feedback = reviewed_object_id response_for_original_feedback = Response.find response_id_for_original_feedback response_map_for_original_feedback = ResponseMap.find response_for_original_feedback.map_id original_reviewer_participant_id = response_map_for_original_feedback.reviewer_id participant = AssignmentParticipant.find(original_reviewer_participant_id) topic_id = SignedUpTeam.topic_id(participant.parent_id, participant.user_id) defn[:body][:obj_name] = if topic_id.nil? assignment.name else SignUpTopic.find(topic_id).topic_name end user = User.find(participant.user_id) defn[:to] = user.email defn[:body][:first_name] = user.fullname Mailer.sync_message(defn).deliver end end
In TeammateReviewResponseMap
def email(defn,assignment,participant) defn[:body][:type] = "Teammate Review" participant = AssignmentParticipant.find(reviewee_id) topic_id = SignedUpTeam.topic_id(participant.parent_id, participant.user_id) defn[:body][:obj_name] = SignUpTopic.find(topic_id).topic_name rescue nil user = User.find(participant.user_id) defn[:body][:first_name] = user.fullname defn[:to] = user.email Mailer.sync_message(defn).deliver end
Testing
New refactoring has been tested and verified using existing Rspec tests. Manual testing has been done for the functionalities which does not have any existing Rspec tests.