CSC/ECE 517 Fall 2021 - E2131. Improve assessment360 controller.rb

From Expertiza_Wiki
Revision as of 23:19, 24 October 2021 by Nkotche (talk | contribs) (→‎Code Improvement)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

E2131. Improving the Assessment 360 Controller

This page provides a description of the Expertiza based OSS project.



About Expertiza

Expertiza is an open source project based on Ruby on Rails framework. Expertiza allows the instructor to create new assignments and customize new or existing assignments. It also allows the instructor to create a list of topics the students can sign up for. Students can form teams in Expertiza to work on various projects and assignments. Students can also peer review other students' submissions. Expertiza supports submission across various document types, including the URLs and wiki pages.

Problem Statement

The following tasks were accomplished in this project:

  • Improved the clarity of code by improving the variable and parameter names.
  • Long character strings were taken and given appropriate names.
  • Combined two views: Grade summary by student & Aggregate teammate and Meta Review to 1 page : All Students All Reviews.
  • Added a new functionality for the user to customise the table to view relevant columns.
  • Added RSPEC test cases for testing changes made in the Assessment360 Controller

About Assessment 360 Controller

This controller provides functionality for displaying course-level data, specifically, review averages, and for each student who is a course participant, a list of the projects they worked on and the grades that they received. The reason it is called “assessment 360” is because that’s a term that relates to evaluating performance by combining a lot of different perspectives into a single view. Its externally callable methods are all_students_all_reviews and course_student_grade_summary. I believe that all other methods are simply helper methods for these methods, but please check, because I’m not sure of it.

Current Implementation

Functionality
  • Every course instructor that logs in to Expertiza should be able to view the course grade summary and the teammate and meta review scores corresponding to each assignment for all the students enrolled in a particular course
  • The instructor can navigate to 2 different views: all_students_all_review and course_student_grade_summary to view the scores.
  • all_students_all_review view - Allows the instructor to view the teammate review and the meta review scores corresponding to each assignment of every student enrolled in a particular course in a tabular form. These details can be viewed along with details like:
  • course_student_grade_summary - Allows the instructor to view the peer review score, instructor grade corresponding to each assignment of every student enrolled in a particular course in a tabular form. These details can be viewed along with details like:

Drawbacks and Solutions=

  • Problem 1: The instructor has to navigate to two different views to view the data regarding the scores of all the students enrolled in a particular course. The following are the issues caused due to 2 views:
  • 1. Difficult to view consolidated data regarding student scores at one place.
  • 2. Redundant columns
  • all_students_all_review view


  • course_student_grade_summary



  • Solution: The implementation has been changed in the following way to address the issues:
  • One consolidated view is made : all_student_all_reviews which displays all the scores corresponding to each assignment for every student enrolled in a particular course
  • No redundant columns exist since the entire data is consolidated in a single view.
  • all_students_all_review view

  • Problem 2: Every course instructor that logs in to Expertiza is able to view the course grade summary and the teammate and meta review scores corresponding to each assignment for all the students enrolled in a particular course in a tabular form in 2 different views. Although, it is difficult to view respective teammate and meta review scores on sorting by peer review scores or instructor grades and vice versa.

For example: The instructor cannot sort the peer review scores in the table based on the teammate reviews or the meta reviews since they are in 2 different views

  • Solution: The implementation has been modified such that an instructor can sort any column in the table and view respective data for other columns. This can be done since the entire data can now be seen in a single consolidated view.
 $(function() {
        /*Function for sorting the table */
        $(".sortable").tablesorter({
            sortList: [[0,0]] // sort by Student name
        });
    });



<table class="table table-striped sortable">
    <thead>
        <tr>
          <th rowspan=2 width="320" class="noborder sorter-true">Students</th>
          <th rowspan=2 width="30" class="noborder">Teammate Count</th>
          <% @assignments.each do |assignment| %>
              <th colspan="<%= @colspan %>" class="sorter-false"><%= assignment.name %> </th>
          <% end %>
          .....
        </tr>
        <tr>
          <% @assignments.each do |assignment| %>
            .......
        </tr>
    </thead>
    ...............
</table>
  • Problem 3: Page loading time for all_student_all_reviews is extremely high since a network call is being made as soon as a user clicks on a link to navigate to that page.
  <table class="table table-striped">
    <tr>
      <th rowspan=2 width="320">Students</th>
      .....
    </tr>
    <tr>
      <% (0..@assignments.size).each do %>
        <td><b>Metareviews</b></td>
        <td><b>Teammate Reviews</b></td>
      <% end %>
    </tr>
    <% @course_participants.each do |cp|%>
        <tr>
        .....
        </tr>
    <%end%>
        <tr>
            ....
        </tr>
    </table>
</div>
  • Since there is no condition on rendering the entire view is loaded as soon as the user navigates to the page. This makes the page load extremely slow since the network call is made immediately on page load making it a bad user experience.
  • Solution: The implementation has been changed such that instructor can customise the table to only view the columns relevant to them and post selecting the network call is made. Due to this the page loads faster, as soon as the user clicks a link to navigate to the page.
<div>
  <%# This section renders the checkboxes and passes values to controller as different variables %>
  <%= form_tag({:action => 'all_students_all_reviews'},{:method => :get}) %>
  <p>Which field do you want to see?</p>
  <div style="display: inline-flex;">
    <p><input type="checkbox" name="show_topics" id="show_topics" value=true>Topics  </p>
    <p><input type="checkbox" name="show_teammate_reviews" id="show_teammate_reviews" value=true>Teammate reviews  </p>
    <p><input type="checkbox" name="show_meta_reviews" id="show_meta_reviews" value=true>Meta-reviews  </p>
    <p><input type="checkbox" name="show_peer_scores" id="show_peer_scores" value=true>Peer scores  </p>
    <p><input type="checkbox" name="show_instructor_grades" id="show_instructor_grades" value=true>Instructor grades </p>
  </div>
  <%= button_tag 'Go', value: @course_id, type: :submit, name: :course_id%>
</div>
<% if @render_partial %>
    <%= render 'assessment' %> 
<% end %>
  • The assessment view is rendered only if the render_partial variable is true. Due to this check, the all_students_all_reviews page loads faster on navigation since the network call is not made immediately. Instead it is made after a user action which makes it a better user experience.

New Implementation

  • Consolidation of views - The 2 views all_students_all_review and course_student_grade_summary are now merged into 1 view: views all_students_all_review. This view can be accessed using the list icon from the Manage Courses page.

Navigation to the manage courses page:

  • 1. Choose Manage on the navigation bar on the top of the screen.
  • 2. Choose Courses from the list

  • 3. Choose the list icon corresponding to the course for which you need to view the student score summary.
  • Previous Implementation

   

  • Modified Implementation

   

  • Customisation - New checkbox functionality is added which allows the instructor to customise the view by selecting from a list of columns and viewing only those that are relevant to them. Checkboxes for the following columns exist:
  • 1. Topics
  • 2. Teammate reviews
  • 3. Meta reviews
  • 4. Peer scores
  • 5. Instructor grades
  • The instructor can customise the table by selecting the any or all of the above mentioned columns and only view columns that are relevant to them.

  • Dynamic UI - A new functionality in the UI is added that ensures dynamic resizing of the table based on the number of columns to be displayed.
  • UI Enhancement - The UI of the all_student_all_reviews page is enhanced for better user experience.


Code Improvement

  • Implementation of partials - Refactoring of the code was done to ensure better readability by implementing partials.
<%# Partial to render the table %>
.......
</style>

<script>
    $(function() {
        /*Function for sorting the table */
        $(".sortable").tablesorter({
            sortList: [[0,0]] // sort by Student name
        });
    });
</script>

<table class="table table-striped sortable">
    <thead>
        <tr>
          ....
        </tr>
        <tr>
          <% @assignments.each do |assignment| %>
              <% if @show_meta_reviews %>
                <th><b>Metareviews</b></th>
              <% end %>
              <% if @show_teammate_reviews %>
                <th><b>Teammate Reviews</b></th>
              <% end %>
              <% if @show_topics%>
                <th><b>Topic</b></th>
              <% end %>
              <% if @show_peer_scores %>
                <th><b>Peer Score</b></th>
              <% end %>
              <% if @show_instructor_grades %>
                <th><b>Instructor Grade</b></th>
              <% end %>
          <% end %>
          <% if @show_instructor_grades %>
            <th class="sorter-true"><b>Total Instructor Grade</b></th>
          <% end %>
          <% if @show_meta_reviews %>
            <th><b>Metareviews</b></th>
          <% end %>
          <% if @show_teammate_reviews %>
            <th><b>Teammate Reviews</b></th>
          <% end %>
        </tr>
    </thead>
    <% @course_participants.each do |cp|%>
      <tr>
          <td align="center"><%= "#{cp.name(session[:ip])} (#{cp.fullname(session[:ip])})" %> </td>
          <td><%= @teammate_count[cp.id] %></td>
          <% @assignments.each do |assignment|%>
            <% if @show_meta_reviews %>
              <td><%= @meta_review[cp.id][assignment.id] %></td>
            <% end %>
            <% if @show_teammate_reviews %>
              <td><%= @teammate_review[cp.id][assignment.id] %></td>
            <% end %>
            <% if @show_topics%>
              <% topic = format_topic(@topics[cp.id][assignment.id]) %>
              <td title="<%= topic %>" class="topic topic-tooltip">
                <%= topic %>
              </td>
            <% end %>
            <% if @show_peer_scores %>
              <td><%= format_score(@peer_review_scores[cp.id][assignment.id]) %></td>
            <% end %>
            <% if @show_instructor_grades %>
              <td><%= format_score(@assignment_grades[cp.id][assignment.id]) %> </td>
            <% end %>
          <% end %>
          <% if @show_instructor_grades %>
            <td><%= @final_grades[cp.id] %></td>
          <% end %>
          <% if @show_meta_reviews %>
            <td><%= @meta_review[cp.id][:avg_grade_for_assgt] %></td>
          <% end %>
          <% if @show_teammate_reviews %>
            <td><%= @teammate_review[cp.id][:avg_grade_for_assgt] %></td>
          <% end %>
      </tr>
    <% end %>
    <tr>
      <td><b>Class Average</b></td>
      <td></td>
      <% @assignments.each do |assignment|%>
        <% if @show_meta_reviews %>
          <td><%= "#{@overall_meta_review_grades[assignment.id] / @overall_meta_review_count[assignment.id]}%" %></td>
        <% end %>
        <% if @show_teammate_reviews %>
          <td><%= "#{@overall_teammate_review_grades[assignment.id] / @overall_teammate_review_count[assignment.id]}%" %></td>
        <% end %>
        <% if @show_topics%>
          <td><%= "—" %></td>
        <% end %>
        <% if @show_peer_scores %>
          <td><%= "—" %></td>
        <% end %>
        <% if @show_instructor_grades %>
          <td><%= "—" %></td>
        <% end %>
      <% end%>
      <% total_meta_review_grade = @overall_meta_review_grades.inject(0) {|sum, (k, v)| sum + v } %>
      <% total_meta_review_count = @overall_meta_review_count.inject(0) {|sum, (k, v)| sum + v } %>
      <% if @show_instructor_grades %>
        <td><%= "—" %></td>
      <% end %>
      <% if @show_meta_reviews %>
        <td><%= "#{total_meta_review_grade / total_meta_review_count}%" %></td>
      <% end %>
      <% total_teammate_review_grade = @overall_teammate_review_grades.inject(0) {|sum, (k, v)| sum + v } %>
      <% total_teammate_review_count = @overall_teammate_review_count.inject(0) {|sum, (k, v)| sum + v } %>
      <% if @show_teammate_reviews %>
      <td><%= "#{total_teammate_review_grade / total_teammate_review_count}%" %></td>
      <% end %>
    </tr>
</table>
  • Code comments - Code comments for every new implementation are added to provide a better understanding and easier experience for developers viewing the code.
  • Refactoring - Refactoring of the code is done by renaming certain variables based on the naming convention, choosing ideal names for all the newly added variables, removing unused code.
  • 1. Removed the view - course_student_grade_summary
  • 2. Replaced teamed_count to teammate count
  • Previous code
<% @course_participants.each do |cp|%>
        <tr>
            <td align="center"><%= "#{cp.name(session[:ip])} (#{cp.fullname(session[:ip])})" %> </td>
            <td><%= @teamed_count[cp.id] %></td>
            <% @assignments.each do |assignment|%>
                <td><%= @meta_review[cp.id][assignment.id] %></td>
                <td><%= @teammate_review[cp.id][assignment.id] %></td>
            <% end%>
            <td><%= @meta_review[cp.id][:avg_grade_for_assgt] %></td>
            <td><%= @teammate_review[cp.id][:avg_grade_for_assgt] %></td>
        </tr>
    <%end%>
  • Modified code
 <% @course_participants.each do |cp|%>
      <tr>
          <td align="center"><%= "#{cp.name(session[:ip])} (#{cp.fullname(session[:ip])})" %> </td>
          <td><%= @teammate_count[cp.id] %></td>
          <% @assignments.each do |assignment|%>
            <% if @show_meta_reviews %>
              <td><%= @meta_review[cp.id][assignment.id] %></td>
            <% end %>
            <% if @show_teammate_reviews %>
              <td><%= @teammate_review[cp.id][assignment.id] %></td>
            <% end %>
            <% if @show_topics%>
              <% topic = format_topic(@topics[cp.id][assignment.id]) %>
              <td title="<%= topic %>" class="topic topic-tooltip">
                <%= topic %>
              </td>
            <% end %>
            <% if @show_peer_scores %>
              <td><%= format_score(@peer_review_scores[cp.id][assignment.id]) %></td>
            <% end %>
            <% if @show_instructor_grades %>
              <td><%= format_score(@assignment_grades[cp.id][assignment.id]) %> </td>
            <% end %>
          <% end %>
          <% if @show_instructor_grades %>
            <td><%= @final_grades[cp.id] %></td>
          <% end %>
          <% if @show_meta_reviews %>
            <td><%= @meta_review[cp.id][:avg_grade_for_assgt] %></td>
          <% end %>
          <% if @show_teammate_reviews %>
            <td><%= @teammate_review[cp.id][:avg_grade_for_assgt] %></td>
          <% end %>
      </tr>
    <% end %>
  • 3. Added variables names according to the naming convention
    @topics = {}
    @assignment_grades = {}
    @peer_review_scores = {}
    @final_grades = {}
    course = Course.find(params[:course_id])
    @course_id = params[:course_id]
    @assignments = course.assignments.reject(&:is_calibrated).reject {|a| a.participants.empty? }
    @course_participants = course.get_participants
    # variable to check if we have to render just the checkboxes or partials too
    @render_partial = false
    insure_existence_of(@course_participants,course)
    # hashes for view
    @meta_review = {}
    @teammate_review = {}
    @teammate_count = {}
    # instance variables based on each checkbox
    @show_teammate_reviews = params[:show_teammate_reviews] || false
    @show_meta_reviews = params[:show_meta_reviews] || false
    @show_peer_scores = params[:show_peer_scores] || false
    @show_instructor_grades = params[:show_instructor_grades] || false
    @show_topics = params[:show_topics] || false
    @checkboxes_array = [@show_teammate_reviews,@show_meta_reviews,@show_peer_scores,@show_instructor_grades,@show_topics]
    # number of columns to span for each assignment
    @colspan = assignment_colspan @checkboxes_array
    # number of columns to span for Aggregate Score table header
    @colspan_review = review_colspan @checkboxes_array
    # for course
    # eg. @overall_teammate_review_grades = {assgt_id1: 100, assgt_id2: 178, ...}
    # @overall_teammate_review_count = {assgt_id1: 1, assgt_id2: 2, ...}
    # network calls to be done only when we need to render_partial
    @render_partial = show_table? @checkboxes_array
  • Improved Performance - Improved the application performance by enhancing the user experience on navigation to the all_students_all_reviews page by changing when the network call should be made.
  • Relevant Test Cases - Added relevant test cases for all the newly implemented code.

Modified File

  • The following are the list of modified files:
  • 1. assessment_360.controller.rb
  • 2. course_student_grade_summary.html.erb - removed
  • 3. all_students_all_review.html.erb - removed
  • 4. tree_display.jsx
  • 5. assessment360_controller_spec.rb

Steps to run the application on your local

Test Plan - TDD Approach

Testing on the assessment 360 controller was done for the following cases:

  • 1. Edge cases :
  • When no score currently exists for a particular assignment.
  • When an entire column does not exist for an assignment
  • 2. Ideal case:
  • When there exists atleast one student with a score for every assignment.
  • 3. Invalid Inputs: Atleast one column needs to be selected by the user to view the table. No column selected is an invalid input case.

Testing using RSPEC

In the existing version of Expertiza there existed certain test cases for the Assessment360 controller.We have added an additional set of RSPEC tests for Assessment360 contoller, to test all the modifications made to the controller and have also removed the testcases corresponding to the code that was removed. The tests can be executed "rpec rspec spec/controllers/assessment360_controller_spec.rb" command as shown below.

user-expertiza $rspec spec/controllers/assessment360_controller_spec.rb
Randomized with seed 52813
........................

Finished in 1 minute 36.91 seconds (files took 6.76 seconds to load)
24 examples, 0 failures

Randomized with seed 52813

Project Team

  • Mentor - Dr. Edward Gehringer
  • Members
  • 1. Neha Kotcherlakota
  • 2. Ankit Singh
  • 3. Supriya Krishna

References

  1. Expertiza on GitHub
  2. GitHub Project Repository Fork
  3. The live Expertiza website
  4. Expertiza project documentation wiki
  5. Rspec Documentation
  6. Clean Code: A handbook of agile software craftsmanship. Author: Robert C Martin