CSC/ECE 517 Spring 2015 S1524 FSZZ

From PG_Wiki
Jump to: navigation, search

E1524. Refactor staggered-deadline assignments


Introduction to Expertiza

Expertiza is a web application where students can submit and peer-review learning objects (articles, code, web sites, etc). It is used in select courses at NC State and by professors at several other colleges and universities[1].

Staggered Deadlines

Staggered deadlines involve planning alternate submission dates for papers, projects, or exams when a student has conflicting due dates for these. The key part of staggered deadlines is the planning. Staggered deadlines are always established well in advance of the scheduled due date. It is the advanced planning of these deadlines that makes them "staggered deadlines" rather than extensions[2].






Problem Statement


In this semester's 517 class. Wiki 1a and Wiki 1b are structured as separate assignments, with separate signup sheets, teams, and reviews. But really, since only one of the two was done by any student, it would've been better to have a single assignment. Still, some topics could be done soon after the course started, whereas others were better done after we had studied related topics in class.

Staggered-deadline Assignment

This raises the idea of a staggered-deadline assignment, where different topics have different submission and review deadlines, rather than all topics having the same deadline.


Product Verion Functionality

Set different deadline for each topic

Dependency graph for all topics

Design Pattern

Use Case

Use Case Diagram

Error Message Present

Assignment panel 1

Create Topics

Assignment panel 2


Problem Analysis

See the function code below:

def add_signup_topic

  @review_rounds = Assignment.find(params[:id]).get_review_rounds
  @topics = SignUpTopic.where(assignment_id: params[:id])

  #Use this until you figure out how to initialize this array
  @duedates = SignUpTopic.find_by_sql("SELECT as topic_id FROM sign_up_topics s WHERE s.assignment_id = " + params[:id].to_s)

  unless @topics.nil?
    @topics.each { |topic|

      @duedates[i]['t_id'] =
      @duedates[i]['topic_identifier'] = topic.topic_identifier
      @duedates[i]['topic_name'] = topic.topic_name

      for j in 1..@review_rounds
        duedate_subm = TopicDeadline.where(topic_id:, deadline_type_id:  DeadlineType.find_by_name('submission').id).first
        duedate_rev = TopicDeadline.where(topic_id:, deadline_type_id:  DeadlineType.find_by_name('review').id).first
        if !duedate_subm.nil? && !duedate_rev.nil?
          @duedates[i]['submission_'+ j.to_s] = DateTime.parse(duedate_subm['due_at'].to_s).strftime("%Y-%m-%d %H:%M:%S")
          @duedates[i]['review_'+ j.to_s] = DateTime.parse(duedate_rev['due_at'].to_s).strftime("%Y-%m-%d %H:%M:%S")
          #the topic is new. so copy deadlines from assignment
          set_of_due_dates = DueDate.where(assignment_id: params[:id])
          set_of_due_dates.each { |due_date|
            create_topic_deadline(due_date, 0,

          @duedates[i]['submission_'+ j.to_s] = DateTime.parse(duedate_subm['due_at'].to_s).strftime("%Y-%m-%d %H:%M:%S")
          @duedates[i]['review_'+ j.to_s] = DateTime.parse(duedate_rev['due_at'].to_s).strftime("%Y-%m-%d %H:%M:%S")

      duedate_subm = TopicDeadline.where(topic_id:, deadline_type_id:  DeadlineType.find_by_name('metareview').id).first
      @duedates[i]['submission_'+ (@review_rounds+1).to_s] = !(duedate_subm.nil?)?(DateTime.parse(duedate_subm['due_at'].to_s).strftime("%Y-%m-%d %H:%M:%S")):nil
      i = i + 1


The main confusions are:


[Professor] In a staggered-deadline assignment, there is one submission deadline and one review deadline per round. These are always set on a per-topic basis. If the view currently works (and I think it might), as soon as you make an assignment a staggered-deadline assignment, the Signup Sheet (Topics) page will have a link at the bottom to show deadlines for each topic. Click it, and you will see a separate text box for each deadline for each topic.

[Professor] No, there are also separate meta-review deadlines for a staggered-deadline assignment. I think in the current implementation, only submission deadlines, review deadlines, and meta-review deadlines vary by topic. In a more complete implementation, there would be a way for ANY deadline to vary by topic. This would include signup deadlines, drop-topic deadlines, team-formation deadlines, and any other kind of deadline that is defined later.

[Professor] Basically, the staggered-deadline is set at the same time when an assignment is created. But if one team cannot hand in their work with sufficient reasons, instructor may extend the deadline of their topic.

[Professor] The dependencies of topics means one topic cannot start until another topic finishes. For instance, Ruby topic must be finished before Rails topic.


Set topics staggered deadline

At very beginning, we find that the type of due_date variable SignUpTopics. However, we found that many attributes used not belonging to SignUpTopics class. So we decide to use hash tables instead. In order to keep update the staggered deadline, we put this variable in session. We also comprehended the relationship of corresponding tables. The original due dates are stored in due_date table. And staggered-deadlines are stored in TopicDeadline table. This table also stores different deadline types.

Below is save_topic_deadlines method after refactoring.

def save_topic_deadlines
 #session[:duedates] stores all original duedates info
 #due_dates stores staggered duedates
 due_dates = params[:due_date]
 topics = SignUpTopic.where(assignment_id: params[:assignment_id])
 review_rounds = Assignment.find(params[:assignment_id]).get_review_rounds
 # j represents the review rounds
 j = 0
 topics.each { |topic|
   for i in 1..review_rounds
     topic_deadline_type_subm = DeadlineType.find_by_name('submission').id
     topic_deadline_subm = TopicDeadline.where(topic_id: session[:duedates][j]['id'].to_i, deadline_type_id: topic_deadline_type_subm, round: i).first
     topic_deadline_subm.update_attributes({'due_at' => due_dates[session[:duedates][j]['id'].to_s + '_submission_' + i.to_s + '_due_date']})
     flash[:error] = "Please enter a valid " + (i > 1 ? "Resubmission deadline " + (i-1).to_s : "Submission deadline") if topic_deadline_subm.errors.length > 0
     topic_deadline_type_rev = DeadlineType.find_by_name('review').id
     topic_deadline_rev = TopicDeadline.where(topic_id: session[:duedates][j]['id'].to_i, deadline_type_id: topic_deadline_type_rev, round:i).first
     topic_deadline_rev.update_attributes({'due_at' => due_dates[session[:duedates][j]['id'].to_s + '_review_' + i.to_s + '_due_date']})
     flash[:error] = "Please enter a valid Review deadline " + (i > 1 ? (i-1).to_s : "") if topic_deadline_rev.errors.length > 0
   topic_deadline_subm = TopicDeadline.where(topic_id: session[:duedates][j]['id'], deadline_type_id:  DeadlineType.find_by_name('metareview').id).first
   topic_deadline_subm.update_attributes({'due_at' => due_dates[session[:duedates][j]['id'].to_s + '_submission_' + (review_rounds+1).to_s + '_due_date']})
   flash[:error] = "Please enter a valid Meta review deadline" if topic_deadline_subm.errors.length > 0
   j = j + 1

Save topics dependency and show dependency graph

In edit signup sheet for assignment, we want to save topic dependency and show the depandency graph, as show below:

Assignment panel 1

In order to show the dependency graph, we must first save topics dependency and save the topic dependency graph under public/assets/staggered_deadline_assignment_graph path. We first add new method in 'due_date' model:

  def self.assign_start_due_date(assignment_id, set_of_topics)

    #Remember, in create_common_start_time_topics function we reversed the graph so reverse it back
    set_of_topics = set_of_topics.reverse

    set_of_topics_due_dates =
    days_between_submissions = Assignment.find(assignment_id)['days_between_submissions'].to_i
    set_of_topics.each { |set_of_topic|
      set_of_due_dates = nil
      if i==0
        #take the first set from the table which user stores
        set_of_due_dates = DueDate.where(assignment_id)
        offset = 0
        set_of_due_dates = TopicDeadline.where(set_of_topics[i-1][0])
        set_of_due_dates.sort_by { |a, b| a.due_at <=> b.due_at }
        offset = days_between_submissions

      set_of_topic.each { |topic_id|
        #if the due dates have already been created and the save dependency is being clicked,
        #then delete existing n create again
        prev_saved_due_dates = TopicDeadline.where(topic_id)

        #Only if there is a dependency for the topic
        if !prev_saved_due_dates.nil?
          num_due_dates = prev_saved_due_dates.length
          #for each due date in the current topic he want to compare it to the previous due date
          for x in 0..num_due_dates - 1
            #we don't want the old date to move earlier in time so we save it as the new due date and destroy the old one  
            if DateTime.parse(set_of_due_dates[x].due_at.to_s) + offset.to_i < DateTime.parse(prev_saved_due_dates[x].due_at.to_s)
              set_of_due_dates[x] = prev_saved_due_dates[x]
              offset = 0

        set_of_due_dates.each { |due_date|
          create_topic_deadline(due_date, offset, topic_id)
      i = i+1

Then, change the save_topic_dependencies method in sign_up_sheet_controller.rb :

  - redirect_to_sign_up(params[:assignment_id])
  + redirect_to :action => 'add_signup_topics_staggered', :id => params[:assignment_id] 

Since we use RGL:Graph#write_to_graphic_file method to turn the dependency graph into jpg file, we need to install graphviz in our gems. Otherwise, the RGL will uses dot files as an intermediary format to produce image formats. If we download the GraphViz packege, RGL will invoke it to produce a jpg file.

gem 'graphviz' 

Then, after we click the Save dependency button, and click show dependency graph button, the dependency graph is shown below:

Assignment panel 2


Since we have done several modifications to the Expertiza program, some new tests are needed to test the features. We use the stub to simulate an authorized login (as instructor).


Each time the program executes the current_role_name function, it gets "instructor". In the test, we mainly test two things: staggered assignments and their corresponding dependency graph.

it "should be able to create topic for assignment" do
  get :create, id:, topic: {topic_name: "New Topic", max_choosers: 2, topic_identifier: "Ch1", category: "Programming"}
  expect(response).should redirect_to(edit_assignment_path( + "#tabs-5")

it "should be able to edit topic" do
  get :edit, id:
  expect(response).to be_success

it "should be able to delete topic" do
  delete :destroy, id:, assignment_id:
  expect(response).should redirect_to edit_assignment_path( + "#tabs-5"
it "should be able to generate topic dependency" do
    post :save_topic_dependencies, assignment_id:
    expect(File).to exist("public/assets/staggered_deadline_assignment_graph/graph_#{}.jpg")

it "should be able to detect cycles" do
  post :save_topic_dependencies, assignment_id:,
       ('topic_dependencies_' +>{"dependent_on"=>[]},
       ('topic_dependencies_' +>{"dependent_on"=>[]}
  expect(flash[:error]).to eq("There may be one or more cycles in the dependencies. Please correct them")

Deploy on VCL

Related materials


  1. Expertiza. Github
  2. Elmichaels. Difficulties with Staggered Deadlines. Jan 15, 2013
  3. Model–view–controller. Wikipedia
  4. Publish–subscribe pattern. Wikipedia
  5. Expertiza. Wolfwikis
Personal tools