CSC/ECE 517 Fall 2014/final E1472 gjfz

From Expertiza_Wiki
Revision as of 02:01, 3 December 2014 by Yzhang44 (talk | contribs) (T)
Jump to navigation Jump to search

Design Document

E1472: Connect changes to score model with changes to score views

Introduction

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. One of the Expertiza features is to report scores to both students and the instructor. The student can see the feedback from other students, such as the max score, the min score and the average score.

Requirements

A way to query db models to return scores, without UI changes

These methods report grades to students and instructors. The view method reports everyone’s grades to an instructor, and the view_my_scores method reports peer-review scores to a student. This code is very slow, due to many factors. Two of the most prominent are the fact that separate db queries are used for each rubric that has been filled out by anyone associated with the assignment; these queries are made sequentially while the HTML page is being written; and the fact that HTML for the whole page is generated, largely by controller methods, before anything is displayed.

UI changes for reporting scores

The grades_controller class is responsible for displaying grades to students (via view_my_scores) and instructors or Teaching Assistant (via the view method). We will modify the Score class, and reduce the number of variables in order to remove code duplication and improve the loading speed.

What we need to do

  • Remove the get_ and set_ accessor names; just use Ruby accessors.
  • Change camel-case vs. underscores to match the Ruby convention.
  • In grades/_participant.html.erb, don’t create local variables in the view; rather, create an object that has max, min, and avg fields for each kind of reviews. Try to avoid the need to check review type; instead, just pass the kind of review when referring to a max, min, or avg field and use polymorphism.
  • Also, you shouldn't have to check for the existence topic in the view; if it is null, a null string should just be returned.
  • There are lots of calculations in the views, which should be moved to the model.


Files Involved

Model:

  • models/participant.rb
  • models/rscore.rb
  • models/assignment_participant.rb
  • models/assignment.rb
  • models/assignment_participant.rb
  • models/assignment_team.rb
  • models/course.rb
  • models/course_participant.rb
  • models/course_team.rb

Controller:

  • controllers/grades_controller.rb
  • controllers/scores_controller.rb
  • controllers/assignments_controller.rb
  • controllers/assignment_controller.rb

View:

  • grades/_participant.html.erb
  • grades/_scores_author_feedback.html.erb
  • grades/_scores_metareview.html.erb
  • grades/_scores_submitted_work.html.erb
  • grades/_tabbing.html.erb
  • grades/edit.html.erb
  • response/_review.html.erb
  • response/_submitted_files.html.erb
  • response/view.html.erb

Calculations Found in Views:

  • grades/_participant.html.erb
  • grades/_scores_author_feedback.html.erb

What we are going to do

  • Modify the setters and getters in the models/participant.rb by refactoring
  • Modify the camel-case variables to underscores to match the Ruby convention in views/grades/... , models/participant.rb and controllers/grades_controller.rb
  • Modify the data structure of the score, instead of using pScore which contains lots of hash tables in it, we set up a new object, Rscore, which only consists of max, min and average fields of each reviews. Also, using inheritance to implement this data structure to avoid check review type.
  • delete checking for topics in views
  • Move the calculations in the views to the model so that there won't be many calculations in the views of MVC framework.(Model file:assignment_participant.rb)

What we have done

  • We modified the camel-case variables in views/grade to Ruby convention by replacing them with under_score format while keeping the camel-case in JavaScript, as in JavaScript the camel-case should be its convention.
  • We set up new data structure Rscore to delegate the score and reduce the local variables.
  • We analysed the system and found out that polymorphism for Rscore is not necessary, delegation will handle all the usages.
  • We moved the calculations in the views into model file,assignment_participants.rb, which follows the single responsibility principle and DRY principle.
  • We modified the response_map to expedite the rendering speed of showing scores.

Details of Implementation

Moving calculation from view to model

Why move calculation from view to model

First, MVC-Model-View-Controller - is a design pattern for the architecture of web applications. It is a widely adopted pattern, across many languages and implementation frameworks, whose purpose is to achieve a clean separation between three components of most any web application. Second,


why use mvc calculation's function why put them in the model

OOP

Design Pattern and System Architecture

MVC design pattern

Responsibility of view_my_scores html.erb is to display all the scores with respect to a participant and an assignment. For achieving optimal functionality, a good amount of refactoring had to be done in the views that were responsible for displaying the results to a user.(By removing all the calculations in the views to models)


Apart from the template methods that are present in other controller class, the grades_controller specifically has two more important methods: view and view_my_scores.

  • view method: handle the functionality of viewing the assignments of the whole class. Understandably, this is done through an admin/instructor's profile as only they have the privilege of viewing all participants' scores.
  • view_my_scores: take care of displaying the scores pertaining to an assignment to a single user.


Scores.rb has two methods which are responsible to compute scores for the participants based on assignments and courses.

  • get_total_scores() method: make multiple sequential queries to the database to calculate scores.
  • Model class PartcipantScores.rb and AssignmentScores.rb have been created to retrieve the scores for participant and assignment respectively.

DRY Principle

This ensures that information is not repeated in the application. We use DRY Principle to design our object and modify all other work.(By setting up Rscore data structure)

Encapsulation

Set up a new data structure, Rscore, and use this data structure to delegate score in all the views.

In the original code, there are plenty of local variables such as min, max and avg. This not only ruins the readability of the code, but also tends to cause problem for future development. Since min, max and avg are the mostly used variables, we created a new data structure Rscore, which put all these three variables together.

In the project specification, it suggested that we use polymorphism to reduce the redundant local variables. However, we found that this simple data structure will suffice without bothering to use polymorphism, and it makes the code looks cleaner. What is more, pscore is a complicated data structure, it is a huge hash table, which contains at least two more hash tables in it. This situation makes polymorphism not only hard to implement, but also

Single Responsibility Principle

This ensures that every controller and model method will be responsible in performing exactly one functionality.(By setting up Rscore data structure)

Use Case

The figure above mainly illustrates the process of viewing scores for different actors. The instructor and teaching assistant are able to view scores of all the team and could see the reviews of a selected team. Student actor can see his own scores and reviews.

Actor Description
Instructor This actor is responsible for viewing scores and reviews of all the students(teams).
Admin This actor has the same responsibility as Instructor along with the ability to create instructor(however, this is not our concern here).
Teaching Assistant This actor is responsible for viewing scores and reviews of all the students(teams).
Student This actor is responsible for viewing scores and reviews of his own or of his team.


The following table is showing the use case of different users of the expertiza and how they interact with it. For example, when a student want to view his/her grades, there must be something in the database (precondition). He will need to login, and choose one of the assignments which he want to view grade on. He can also see the review of his work.

Name Actor Other Participants Precondition Primary Sequence
Student views his team score Student None At least team's score exists.
  • Log in to Expertiza
  • Choose Assignment from multiple Assignments
  • Select Your Scores button from an Assignment menu
  • Select show_review in score page
TA views teams' review TA None At least team's review exists.
  • Log in to Expertiza
  • Choose Assignment from multiple Assignments
  • Select Your Scores button from an Assignment menu
  • Select show_review in score page
Instructor/ Teaching Assistant/ Admin views the list of teams scores Instructor/ Teaching Assistant/ Admin None At least one team's score exists.
  • Log in to Expertiza
  • Choose Assignment from multiple Assignments
  • Select View Scores button from an Assignment menu

Objected Oriented Design

The following class diagram shows a combination of new and existing classes along with the attributes and operations that are relevant to the implementation of this solution.

The RScore class stores the variables maximum,minimum, average review scores. Different type of reviews score is declared as subclass of RScore using ploymorphism. The type field is specified in their initialize method.

Create object to avoid local variables

Create rscore class in models (rscore.rb):

class Rscore
  attr_accessor :my_max,:my_min,:my_avg,:my_type

  def initialize(my_score,type)
    @my_max=my_score[type][:scores][:max]
    @my_min=my_score[type][:scores][:min]
    @my_avg=my_score[type][:scores][:avg]
    @my_type=type
  end
end

Use instance variables in views

In grades/_participant.html.erb, we create an object that has max, min, and avg fields for each kind of reviews, and pass the kind of review when referring to a max, min, or avg field.

Before:(views/grades/_participant.html.erb)

  if pscore[:review]
  	s_max = pscore[:review][:scores][:max]
  	s_min = pscore[:review][:scores][:min] 
  	s_avg = pscore[:review][:scores][:avg] 
  end
  if pscore[:metareview]
    r_max = pscore[:metareview][:scores][:max]
  	r_min = pscore[:metareview][:scores][:min] 
  	r_avg = pscore[:metareview][:scores][:avg]
  end 
  if pscore[:feedback]
  	f_max = pscore[:feedback][:scores][:max]
  	f_min = pscore[:feedback][:scores][:min] 
  	f_avg = pscore[:feedback][:scores][:avg]
  end
  if pscore[:teammate]
  	tr_max = pscore[:teammate][:scores][:max]
  	tr_min = pscore[:teammate][:scores][:min] 
  	tr_avg = pscore[:teammate][:scores][:avg]   
  end 

After:(views/grades/_participant.html.erb)

  if pscore[:review]
    @rscore_review=Rscore.new(pscore,:review)
  end
  if pscore[:metareview]
    @rscore_metareview=Rscore.new(pscore,:metareview)
  end
  if pscore[:feedback]
    @rscore_feedback=Rscore.new(pscore,:feedback)
  end
  if pscore[:teammate]
    @rscore_teammate=Rscore.new(pscore,:teammate)

Moving Calculation from view to model

We move lots of calculations from views to model.

Before:(views/grades/_participant.html.erb)

 <% if controller.action_name == 'view' or controller.action_name == "view_my_scores" %>
    	<TD ALIGN="CENTER">
    	<% if stage == "Finished" %>
        <% if participant.grade
          total_score = participant.grade
          title = "A score in blue indicates that the value was overwritten by the instructor or teaching assistant."
        else
          total_score = pscore[:total_score] 
          title = nil
        end %>

            <% hardline = 85
               if tr_avg > hardline
                 total_score = total_score + 0.05*total_score
               elsif tr_avg < hardline and (hardline -tr_avg) > 40
                 total_score = total_score - 10
               elsif tr_avg < hardline and (hardline -tr_avg) > 20
                 total_score = total_score - (hardline -tr_avg)*0.5
            %>
            <%end%>
              <% if total_score>100
              total_score = 100
              %>
            <%end%>
 <div <% if title %>title="<%=title%>" style="color:#0033FF"<% end %>><%= sprintf("%.2f",total_score) %><%= score_postfix %></div>

After:(model/assignment_participant.rb)

    # move lots of calculation from view(_participant.html.erb) to model
    if self.grade
      scores[:total_score] = self.grade
    end
    else
      total_score = scores[:total_score]
      hardline = 85
      if scores[:teammate][:scores][:avg].to_f > hardline
        total_score = total_score + 0.05*total_score
      elsif scores[:teammate][:scores][:avg].to_f < hardline and (hardline - scores[:teammate][:scores][:avg].to_f) > 40
        total_score = total_score - 10
      elsif scores[:teammate][:scores][:avg].to_f < hardline and (hardline - scores[:teammate][:scores][:avg].to_f) > 20
        total_score = total_score - (hardline - scores[:teammate][:scores][:avg].to_f)*0.5
      end
      if total_score > 100
        total_score = 100
      end
      scores[:total_score] = total_score
    scores

Speeding up view score's function

At the beginning of the final project, the function of viewing scores is very slow by students and instructor. Our team found the real factor which lead to the slow problem and solved it in an easy approach.

Optimize response searching method in the model

  • Modify get_assessments_for method in response_map.rb

After doing this, the time cost of view function decreased by more than 90% Before Refactoring:

 # the original method to find all response 
 @all_resp=Response.all
 for element in @all_resp
     if (element.map_id == map.map_id)
         @array_sort << element
         @test << map
     end
 end

After Refactoring:

 @all_resp=Response.find_by_map_id(map.map_id)
 @array_sort << @all_resp
 @test << map

Test Result

Name Before Refactoring After Refactoring Reduced By
View all team's score(instructor) 484988ms 8642ms 98.21%
View own score(student) 8941ms 651ms 92.71%
  • Original Time for Instructor to View all scores
Original Time for Instructor to View all scores
  • Time for Instructor to View all scores after Refactoring
Time for Instructor to View all scores after Refactoring
  • Original Time for Student to View all scores
Original Time for Student to View all scores
  • Time for Student to View all scores after Refactoring
Time for Student to View all scores after Refactoring

Snapshots

Instructor Login

Select Assignment

View Scores

See Also

Wiki page for E804

Wiki page for E805

Github link for E912

Future Work

We will stick to the UI design of the grades view and see what else we can do to further improve the performance of the system. We are trying to figure out other reasons that may lead to the bad performance in addition to the two main reasons mentioned and solved in E804 and E805 projects.