CSC/ECE 517 Fall 2016/E1631. Refactoring Bidding Interface

From Expertiza_Wiki
Jump to navigation Jump to search

Introduction

On Expertiza, the bidding interface allows students to sort topics by preference. This is needed in order to run the team assignment algorithm to match students with others based off the similarity in their topic preferences. The problem with the present method is that the students have to type in their priorities into the boxes beside the topic. While doing this the students end up making mistakes in setting up priorities by assigning random numbers to the topics, sometimes, even numbers greater than total number of topics available to prioritize. This phenomena causes various problems when running the topic assignment algorithm.

We built an interface in sign up sheet that would allow students to drag and drop the topics into the priority table. Where this priority table will be used to run the selection algorithm with ease. This interface would help avoid the errors caused by the previous interface.

Old Bidding Interface

As shown in the picture below, in the old bidding interface users need to manually enter the priority. They can't repeat the same priority number twice. It's difficult for them to remember all the priority numbers entered previously. If they enter the same priority value, system throws an error. Some users made mistake and entered some random priority values (e.g priority value greater then number of topics etc). This caused many problems in the intelligent team assignment algorithm. This situations can be handled by adding more checks. But we can avoid these errors by only giving the users to order their topics by topic name and expertiza code can assign required priority numbers internally.

Old Signup Table

How It Works

Under the previous interface users can only choose which project they want to work on through setting the priority of the projects based on some distinct integers, where any smaller integer has higher precedence, and empty represents the lowest. For instance “1” has the highest precedence among other integers. The user can input only one instances of the same integer. If no desired topics are selected, then the user will be assigned some topic depending on the heuristics used inside the selection algorithm.

Problems

There exists a serious user interface problem with the previous design. Note when a user is scanning through a hugh list of the topics, he/she might want to choose the topic as they are reading the project description. In order to achieve it, they will have to remember the priorities of the less desired topics, thus they will have to constantly updating their priorities for other topics. Therefore this will cause a lot of trouble and inconvenience, compared with the new design, where the user just needs to drag the topics without constantly changing the previous priorities.

Project Requirements

  • Improve the user interface
  • Allow the user to set their priorities based on the position of the topic in the selection list
  • Do not allow the user to set duplicate priorities


New Bidding Interface

We proposed sortable list interface to solve the problem associated with the old interface. New interface has two tables. First table holds all the topic lists and second table holds all the chose user priorities. User can select the topic by moving the item from Topics table to Selection table. Users can also resort already selected topics. System also provides the ability to remove already selected topic from the selection by just moving it back to the topic selection table.

New Signup Table


How It Works

These are the set of operations a user can perform on this signup page using the cursor: 1) The user can drag the topics from topics table on the left into the selection table on the right. As a result, the topics are moved from one table to another. 2) The user can rearrange the already added topics in the selection table. As a result, the topics can be moved up and down within this table. The order of topics in selection table on the right, from top to bottom, are taken in the order of high to low priorities. Then this list of prioritized topics are used to run the intelligent bidding algorithm.

Code changes

New Files

intelligent_topic_selection.html.erb
This file has code to display Topics and selection table. This view is displayed if the assignment type is 'intelligent'. This page is only static. CoffeeScript is used to provide dynamic sorting functionality to the tables in this view. Whenever the user drags and drops the item, it calls corresponding CoffeeScript associated with the table body. Both topics and selection tables are assigned same class name "connectedSortable". This class name is used in the CoffeeScript to connect the tables. Only connected tables can exchange the elements between each other.
<%# Display Topics and Selection table headings%>
<div style="display:block">
  <h3 style="width: 40%; float: left">Topics</h3>
  <h3 style="width: 40%; float:right">Selections</h3>
</div>

<%# Display Topic number and Topic name of all available topics for selection in Topics table%>
<div style="display:block">
  <div style="display:inline-block" >
    <table style="width: 40%; float: left" class="general">
      <tr><%= render :partial => 'table_header' %></tr>
      <% if !@selected_topics.nil? && @selected_topics.size != 0 %>
          <b>Your topic(s):</b>
          <% for selected_topic in @selected_topics %>
              <br/><%= selected_topic.topic_name %>
              <% if selected_topic.is_waitlisted == true %>
                  <font color='red'>(waitlisted)</font>
              <% end %>
          <% end %>
      <% end %>
      <br/><br/>

      <% i=1 %>
      <tbody id="topics"  class="connectedSortable">
      <tr class="sort-disabled"><td></td><td></td></tr>
    
      <%# Display all the signed up topics in Selection table %>
      <% for topic in @sign_up_topics %>
          <% if !@selected_topics.nil? && @selected_topics.size != 0 %>
              <% for selected_topic in @selected_topics %>
                  <% if selected_topic.topic_id == topic.id and !selected_topic.is_waitlisted %>
                      <tr bgcolor="yellow">
                  <% elsif selected_topic.topic_id == topic.id and selected_topic.is_waitlisted%>
                      <tr bgcolor="lightgray">
                  <% else %>
                      <tr id="topic_<%= topic.id %>">
                  <% end %>
              <% end %>
          <% else %>
              <tr id="topic_<%= topic.id %>">
          <% end %>
          <% is_suggested_topic = false %>
          <%# Render suggested topics partial if the student suggested some topics and if it's approved %>
          <%= render :partial => 'table_line', :locals => {:i=>i, :topic=>topic, :is_suggested_topic=>is_suggested_topic} %>
          </tr>
          <% i=i+1 %>
      <% end %>
      </tbody>
    </table>
_suggested_topic.html.erb
Instructor can allow students to suggest new topic. If the suggested topic is approved by instructor, it'll be displayed at the end of the page. Previously it was part of the list.html.erb file. New interface makes use of the same code. So we created _suggested_topic partial to display common code.
<%# Displays all the suggested topics which are approved by instructor %>
<% if SignUpTopic.has_suggested_topic?(@assignment.id) %>
    <h2>Your approved suggested topic</h2>
    <table class="general">
      <tr><%= render :partial => 'table_header' %></tr>
      <br/>
      <% i=1 %>
      <%
        team_id = TeamsUser.team_id(@assignment_id, session[:user].id)
        teams_users = TeamsUser.where(team_id: team_id)
        teams_users_array = Array.new
        teams_users.each do |teams_user|
          teams_users_array << teams_user.user_id
        end
        @suggested_topics = SignUpTopic.where(assignment_id: @assignment_id, private_to: teams_users_array)
      %>
      <%if !@suggested_topics.nil? %>
          <% @suggested_topics.each do |topic| %>
              <tr>
                <% is_suggested_topic = true %>
                <%= render :partial => 'table_line', :locals => {:i=>i, :topic=>topic, :is_suggested_topic=>is_suggested_topic} %>
              </tr>
              <% i=i+1 %>
          <% end %>
      <% end %>
    </table>
<% end %>
app/javascript/tablelist.js.coffee
This file has a callback function for each table. This callback function is called when the user drags and drops the topic. This callback function generates post request to sign_up_sheet controller. This post method sends topic id of all the selected topics as a list to the controller.
jQuery ->
#This code connects Topics table to selection table and makes the element draggable 
  $("#topics").sortable
    cursor: 'move',
    opacity: 0.65,
    tolerance: 'pointer'
    connectWith: ".connectedSortable"
    items: ">*:not(.sort-disabled)"

#This code connects selection table to topics table and calls post method after each drag and drop
  $("#selections").sortable
    cursor: 'move',
    opacity: 0.65,
    tolerance: 'pointer'
    connectWith: ".connectedSortable"
    items: ">*:not(.sort-disabled)"
    update: ->
      $.post($(this).data('update-url'), $(this).sortable('serialize'))

Old code changes

sign_up_sheet_controller.rb
Two methods (set_priority and list) are changed in sign_up_sheet controller. Previous interface used to call set_priority method to set the manually entered priority of each topic. But the new interface should calculate the priority of each topic based on the order of selected topic list. This method receives the topic as an array and updates the priority of each topic based on the received order.
 def set_priority
    @user_id = session[:user].id
    unless params[:topic].nil?
      @bids = Bid.where("user_id LIKE ?", @user_id )
      signed_up_topics = @bids.map {|bid| bid.topic_id}

      #Remove topics from bids table if the student moves data from Selection HTML table to Topics HTML table
      #This step is necessary to avoid duplicate priorities in Bids table
      signed_up_topics = signed_up_topics - params[:topic].map {|topic_id| topic_id.to_i}
      signed_up_topics.each do |topic|
        Bid.where(topic_id: topic, user_id: @user_id).destroy_all
      end

      params[:topic].each_with_index do |topic_id,index |
        check = @bids.where(topic_id: topic_id)
        if check.empty?
          Bid.create(topic_id: topic_id, user_id: @user_id, priority: index + 1)
        else
          Bid.where("topic_id LIKE ? AND user_id LIKE ?",topic_id, @user_id ).update_all({priority: index + 1})
        end
      end
    else
      #All topics are deselected by user
      Bid.where(user_id: @user_id).destroy_all
    end

    redirect_to action: 'list', assignment_id: params[:assignment_id]
  end
In the previous interface, list method used to display all topics in some random order irrespective of chosen topics priority. But with the new interface, expertiza displays selected topics in Selection table based on the indicated priority and remaining unselected topics in the Topics table. Seggregation of topics into separate tables and ordering of topics happens in list method. This method displays new view only if the instructor created assignment with bidding topic selection option.
 def list
    @assignment_id = params[:assignment_id].to_i
    @slots_filled = SignUpTopic.find_slots_filled(params[:assignment_id])
    @slots_waitlisted = SignUpTopic.find_slots_waitlisted(params[:assignment_id])
    @show_actions = true
    @priority = 0
    assignment = Assignment.find(@assignment_id)
    @sign_up_topics = SignUpTopic.where(assignment_id: @assignment_id, private_to: nil)

    if assignment.is_intelligent
      @bids = Bid.where(user_id: session[:user].id).order(:priority)
      signed_up_topics = []
      @bids.each do |bid|
        sign_up_topic = SignUpTopic.where(id: bid.topic_id)
        unless sign_up_topic.empty?
          signed_up_topics << sign_up_topic.first
        end
      end
      @sign_up_topics = @sign_up_topics - signed_up_topics
      @bids = signed_up_topics
    end

   if assignment.is_intelligent
      render 'sign_up_sheet/intelligent_topic_selection' and return
    end


view/sign_up_sheet/list.html.erb
This view holds the code to display manual FCFS (First Come First Serve) style topic selection. This code also had code to display suggested topic list. New interface makes use of suggested topic list code. So we refactored suggested topic code into separate partial and we replaced that code block with single render method.
<% if SignUpTopic.has_suggested_topic?(@assignment.id) %>
    <%= render :partial => '/sign_up_sheet/suggested_topic' %>
<% end %>
view/sign_up_sheet/_table_line.html.erb
Old interface had lot of unnecessary table headers like number of waitlisted students, number of slots available etc. _table_line partial displays contents of each topic (data for set of columns). These columns doesn't give any useful information to students in bidding topic assignment because topic assignment happens at some predetermined time. We removed these columns from sortable topic list. Only FCFS style topic selection or Instructor/TA/Admin interface display's this information.
<% if not @assignment.is_intelligent or ['Instructor', 'Teaching Assistant', 'Administrator', 'Super-Administrator'].
  include? current_user_role?.name %>
  <% if @assignment.is_microtask? %>
      <td align="center"><%= topic.micropayment %></td>
  <% end %>

  <td align="center"><%= topic.max_choosers %></td>
  <%= render :partial => '/sign_up_sheet/available_slots', :locals => {:topic=>topic} %>

  <%= render :partial => '/sign_up_sheet/num_waiting', :locals => {:topic=>topic} %>
  <td align="center"><%= render :partial => '/sign_up_sheet/actions', :locals => {:i=>i,:topic=>topic, :is_suggested_topic=>is_suggested_topic ||= false} %></td>
  <td align="center">
    <% if SignUpSheet.has_teammate_ads?(topic.id) %>
        <%# link_to :controller => 'sign_up_sheet', :action => 'show_team', :assignment_id=>params[:id], :id=>topic.id do %>
        <%= link_to image_tag('ad.png', :border => 0, :title => 'Ad', :align => 'middle', :style => 'width: 24px; height:24px'), :controller=>'sign_up_sheet', :action=> 'show_team', :assignment_id=>@assignment.id, :id=>topic.id%>
    <% end %>
  </td>
<% end %>
_table_header.html.erb
_table_header displays header of the table. We added check to display only subset of column headers for intelligent topic assignment (bidding).
<% if not @assignment.is_intelligent or  ['Instructor', 'Teaching Assistant', 'Administrator', 'Super-Administrator'].include? current_user_role?.name %>
  <th width="5%">Num. of slots</th>
  <th width="5%">Available slots</th>
  <th width="5%">Num. on waitlist</th>
  <th width="10%">Bookmarks</th>
  <th width="10%">Actions</th>
  <th width="5%">Advertise- ment(s)</th>
<% end %>

Testing

There are only some implementation changes to the methods in the controller. But the end behaviour is not changed. So new unit test cases are not necessary. Existing unit tests cover all the behavior. We ran unit tests for sign_up_sheet_controller. All tests passed without any failures.

Testing Result

Expertiza deployment for manual testing

  • You can access the deployment here http://152.46.16.213:3000/
  • Login as student : user name: student5432, password: password
  • Navigate to http://152.46.16.213:3000/sign_up_sheet/list?assignment_id=776
  • Topics table contains all the topics available for this assignment
  • Selection table contains all the selected topics. First topic indicates highest priority. Last topic in this table indicates least priority
  • Drag and drop the topics from Topics table to Selection table to bid for the topic.
  • You can also remove perticular topic from your selection by moving the topic back to Topics table from Selection table
  • This feature is already merged to expertiza master repository few days back. You already used this interface to select 2nd OSS project topics

Further improvements

Enabling the interface to use keyboard to select topics and drag them into the selection table. This will make the selection very intuitive.