CSC/ECE 517 Fall 2013/oss E810 aas
E810 Regularize staggered-deadline assignments
This page provides a description of the OSS project conducted on Expertiza which was done as the part of the Object Oriented Languages and Systems coursework.
Introduction to Expertiza
Expertiza is a project developed using Ruby on Rails platform. It provides features like peer review, team assignments and submission of projects. This can be achieved by submitting code base, URL of hosted code on Remote Servers and Wiki submissions. It is open source application and the code can be cloned from github. This application provides an efficient way to manage assignments, grades and reviews. This makes the process easier and faster when the class strength is large.
Expertiza is supported by National Science Foundation under Grant No. 0536558. Additional funding from the NCSU Learning in a Technology-Rich Environment (LITRE) program, the NCSU Faculty Center for Teaching and Learning, the NCSU STEM Initiative, and the Center for Advanced Computing and Communication.
Problem Statement
According to the existing code flow, Assignment deadlines belonged to two categories i.e. Staggered Deadlines and Deadlines (Normal Due Date). If an Assignment has different topics which have dependencies i.e. if Assignment A has two topics Topic A and Topic B , where Topic A can have deadline before Topic B, and therefore the Due Date of Assignment A is dependent on deadlines of Topics designed in that particular assignment. Hence these deadlines are called Staggered Deadlines. On the other hand, If Assignment A has Topic A and Topic B, but they all have the same deadlines i.e. there is no dependency on each other then that's Normal Due Date scenario. In this case an Assignment may or may not have separate Topics.
Original code had various check statements for staggered and normal deadlines, which had to eliminated and structured in a way that with minimum checks we could distinguish between Staggered and Normal Due Dates. This required refactoring and restructuring of the whole functionality where we reduce these checks by distributing code into separate actions.
Re-factored Code Cases
Case 1 : Refactoring in sign_up_sheet_controller.rb
Current Scenario
Different method definitions for setting up staggered and non-staggered assignment sign up sheet.
method:add_signup_topics_staggered
This method lists all the available topics for an assignment and allows admin to set due dates for assignment.Staggered means that different topics can have different deadlines.
def add_signup_topics_staggered load_add_signup_topics(params[:id]) @review_rounds = Assignment.find(params[:id]).get_review_rounds @topics = SignUpTopic.find_all_by_assignment_id(params[:id]) #below functionality finds allows to set due dates for topics of assignment @duedates = SignUpTopic.find_by_sql("SELECT s.id as topic_id FROM sign_up_topics s WHERE s.assignment_id = " + params[:id].to_s) if !@topics.nil? i=0 @topics.each { |topic| ...... i = i + 1 } end end
method:add_signup_topics
It is similar to the above function except that all the topics and review/submission rounds have the similar deadlines.
def add_signup_topics load_add_signup_topics(params[:id]) end
Conditional call
def redirect_to_sign_up(assignment_id) assignment = Assignment.find(assignment_id) if assignment.staggered_deadline == true redirect_to :action => 'add_signup_topics_staggered', :id => assignment_id else redirect_to :action => 'add_signup_topics', :id => assignment_id end end
After Changes
We have redundant code in two methods so we merged add_signup_topics_staggered and add_signup_topics into add_signup_topics. The action calls single method for both type of assignments and renders views based on condition of staggered or non-staggered assignment.
def add_signup_topics load_add_signup_topics(params[:id]) if @assignment.staggered_deadline ==true @review_rounds = Assignment.find(params[:id]).get_review_rounds @topics = SignUpTopic.find_all_by_assignment_id(params[:id]) #below functionality finds allows to set due dates for topics of assignment @duedates = SignUpTopic.find_by_sql("SELECT s.id as topic_id FROM sign_up_topics s WHERE s.assignment_id = " + params[:id].to_s) if !@topics.nil? i=0 @topics.each { |topic| ...... i = i + 1 end #Render a view different for staggered deadlines assignment from the add_sign_up.html.erb render "add_signup_topics_staggered" end end
Single method call
At several places in the project, there was separate method call based on staggered and non-staggered assignments. Now there is a single method call and condition check in various files for type of assignment has been removed as part of re-factoring.
Case 2 : Method Extraction
Improved Readability by extracting two separate functionalities from the same method into separate methods with appropriate naming convention and responsibilities.
[b]Before Changes to code The add_signup_topics , made a call to load_add_signup_topics method followed by a check for staggered assignments and calculation of due dates for separate topics in this Staggered assignment.
def add_signup_topics load_add_signup_topics(params[:id]) if @assignment.staggered_deadline ==true @review_rounds = Assignment.find(params[:id]).get_review_rounds @topics = SignUpTopic.find_all_by_assignment_id(params[:id]) #below functionality finds allows to set due dates for topics of assignment @duedates = SignUpTopic.find_by_sql("SELECT s.id as topic_id FROM sign_up_topics s WHERE s.assignment_id = " + params[:id].to_s) if !@topics.nil? i=0 @topics.each { |topic| ...... i = i + 1 end #Render a view different for staggered deadlines assignment from the add_sign_up.html.erb render "add_signup_topics_staggered" end end
[b] After Changes to Code
def add_signup_topics load_add_signup_topics(params[:id]) if @assignment.staggered_deadline ==true @review_rounds = Assignment.find(params[:id]).get_review_rounds @topics = SignUpTopic.find_all_by_assignment_id(params[:id]) #Function call to set up due dates assignment_due_date #Render a view different from the add_sign_up.html.erb , so rendering a view for staggered deadlines. render "add_signup_topics_staggered" end end
def assignment_due_date #Use this until you figure out how to initialize this array @duedates = SignUpTopic.find_by_sql("SELECT s.id as topic_id FROM sign_up_topics s WHERE s.assignment_id = " + params[:id].to_s) if !@topics.nil? i=0 @topics.each { |topic| ....... i = i + 1 } end end
Case 3 : Modularize Code in application.rb
In the assignment.rb model, there exists one method(check_condition) which checks whether review, meta-review, etc are allowed or not. This method makes database calls to TopicDeadline table, DueDate and DeadlineType tables. The database queries have been moved to their respective models. Following is the code which was existing before the changes were made. It has the logic to find the nearest due date that hasn't passed.
drop_topic_deadline_id = DeadlineType.find_by_name('drop_topic').id if self.staggered_deadline? if topic_id next_due_date = TopicDeadline.find(:first, :conditions => ['topic_id = ? and due_at >= ? and deadline_type_id <> ?', topic_id, Time.now, drop_topic_deadline_id], :order => 'due_at') else next_due_date = TopicDeadline.find(:first, :conditions => ['assignment_id = ? and due_at >= ? and deadline_type_id <> ?', self.id, Time.now, drop_topic_deadline_id], :joins => {:topic => :assignment}, :order => 'due_at') end else next_due_date = DueDate.find(:first, :conditions => ['assignment_id = ? and due_at >= ? and deadline_type_id <> ?', self.id, Time.now, drop_topic_deadline_id], :order => 'due_at') end