CSC/ECE 517 Fall 2019 - E1966. Tabbed reviews partial file refactor for displaying the alternate view of reviews: Difference between revisions

From Expertiza_Wiki
Jump to navigation Jump to search
No edit summary
 
(33 intermediate revisions by 2 users not shown)
Line 7: Line 7:




1) On student-end, Expertiza use grades/_reviews.html.erb partial to display reviewers, scores and review details; on instructor-end, it uses response controller and model to generate reviews page (the tables and charts are generated by grades/_team_statistics.html.erb (display reviewers and scores) in Statistics tab). We need to use the same kinds of partials in instructor-end and student-end, either _reviews partial or tabbed_reviews partial.
1) On student end, Expertiza uses grades/_reviews.html.erb partial file to display reviewers, scores and review details; on instructor end, it uses Response model methods to construct html file for review pages. We need to use the same kinds of partials in instructor-end and student-end.
 
How to get the view of reviewers, scores and review details on student end? Click "Alternate View" button, and click "show reviews" link. The reviewers and scores pop up on the top and review details are below them.
How to get the view of reviewers, scores and review details on instructor end? Click "Scores" for a certain assignment and choose a team. Click "Statistics" tab and then reviewers and scores table show up. Click "Reviews" tab and then click one of students' name link in the table head. And a new page pops up with review details.


2) Give Feedback link at view_my_score at student-end should not appear at folded view.
2) Give Feedback link at view_my_score at student-end should not appear at folded view.
Where to find Give Feedback link? On student end, click "Alternate View" button and click "show reviews" link. At the bottom of a certain review, a give feedback link shows up.


===What to do===
===What to do===
1) We need to use the same kinds of partials in instructor-end and student-end.
1) We need to use the same kinds of partials in instructor-end and student-end. So we decide to use  grades/_team_statistics.html.erb to display reviewers and scores and to use grades/_tabbed_reviews.html.erb to display review details for both student-end and instructor-end.


2) Give Feedback link at view_my_score should appear at the bottom of expanded view.
2) Give Feedback link at view_my_score should appear at the bottom of expanded view.


== '''Solution''' ==
== '''Solution''' ==
For student end, split reviews.html.erb into two partial files. One is grades/team_review_statistics.html.erb for reviewers and scores. The other one is grades/tabbed_reviews.html.erb for review details.
For instructor end, it used to use response model methods to construct html files for review details, now we use grades/tabbed_reviews.html.erb to display review details.
So student end and instructor end both use grades/team_review_statistics and grades/tabbed_reviews to display reviewers, scores and review details.
===Deploy Link===
[http://152.46.17.70:8080  Our project is deployed at VCL ]
===Student End===
===Student End===


* In student end, add render partial to tabbed reviews.
* In student end, render grades/team_review_statistics and grades/tabbed_reviews two partial files instead of grades/reviews partial file.
 
'''app/views/grades/_participant.html.erb '''
<pre>
<TR id="<%= prefix %>_reviews" style="display:none; background-color: white;">
    <TD COLSPAN="10">
    <%= render :partial=>'grades/team_review_statistics', :locals => {:prefix => 'user', :participant => participant, :pscore => pscore} %>
    <%= render :partial=>'grades/tabbed_reviews', :locals => {:prefix => 'user', :participant => participant, :rscore => pscore[:review]} %>
    </TD>
</TR>
</pre>
* Add statistics view to student end by using the team_review_statistics.html.erb file.
'''app/views/grades/_participant.html.erb '''
'''app/views/grades/_participant.html.erb '''
<pre>
<pre>
<TR id="<%= prefix %>_reviews" style="display:none; background-color: white;">
<TR id="<%= prefix %>_reviews" style="display:none; background-color: white;">
     <TD COLSPAN="10"><%= render :partial=>'grades/reviews', :locals => {:prefix => 'user', :participant => participant, :rscore => pscore[:review]} %></TD>
     <TD COLSPAN="10">
     <TD COLSPAN="10"><%= render :partial=>'grades/tabbed_reviews', :locals => {:prefix => 'user', :participant => participant, :rscore => pscore[:review]} %></TD>
    <%= render :partial=>'grades/team_review_statistics', :locals => {:prefix => 'user', :participant => participant, :pscore => pscore} %>
     <%= render :partial=>'grades/tabbed_reviews', :locals => {:prefix => 'user', :participant => participant, :rscore => pscore[:review]} %>
    </TD>
</TR>
</TR>
</pre>
</pre>
'''app/views/grades/_team_review_statistics.html.erb'''
<pre>
<% if controller.action_name != "view_my_scores"%>
    <tr>
        <td colspan="10">
<% end %>
<% if controller.action_name != "view_my_scores"%>   
        </td>
    </tr>
<% end %>
</pre>
[[File:student-table.png ]]
[[File:Student-detail.png]]
* Add feed back link in student end view.
* Add feed back link in student end view.


'''app/views/grades/_tabbed_reviews.html.erb '''
'''app/views/grades/_tabbed_reviews.html.erb '''
<pre>
<pre>
     <% rounds.each do |round| %>
     <% if controller.action_name == "view_my_scores"%>
        <h3>Round <%= round %></h3>
                    <!-- Generate the feedback html -->
        <a href="#" name= <%= participant.id.to_s + '_' + round.to_s + "Link" %> onClick="toggleElement('<%= participant.id.to_s +
                    <% map = FeedbackResponseMap.where(reviewed_object_id: review.id, reviewer_id: participant.id).first
                '_' + round.to_s %>','round <%= round.to_s%> reviews');return false;">show round <%= round.to_s%> reviews</a>
                        review_feedbacks = map.try :response %>
        <table class="table table-striped" id=<%= participant.id.to_s + '_' + round.to_s %> <%= style="display: none;" if controller.action_name != "view_my_scores"%>>
                    <% if review_feedbacks && !review_feedbacks.empty? %>
            <% rscore[:assessments].select{|response| response.round == round}.reverse.uniq{|response| response.map_id}.sort_by{|response| response.map_id}.each_with_index do |review, index| %>
                        <%= link_to "View", :controller => 'response', :action => 'view', :id => review_feedbacks.first.id %> or
                <tr><td>
                        <%= link_to "Edit", :controller => 'response', :action => 'edit', :id => review_feedbacks.first.id, :return => "feedback" %> feedback for Review <%= count %>
                <% review_feedback = nil %>
                    <% else %>
                         <%= display_directory_tree(@participant, files, true).html_safe %>
                         <%= link_to "Give feedback", :controller => 'response', :action => 'new_feedback', :id => review.id %> for Review <%= count %>
                     <% end %>
                     <% end %>
                <% end %>
                <%= review.display_as_html(prefix, index + 1, nil, true) %>
                <!-- Generate the feedback html -->
                <% map = FeedbackResponseMap.where(reviewed_object_id: review.id, reviewer_id: participant.id).first
                    review_feedbacks = map.try :response %>
                <% if review_feedbacks && !review_feedbacks.empty? %>
                    <%= link_to "View", :controller => 'response', :action => 'view', :id => review_feedbacks.first.id %> or
                    <%= link_to "Edit", :controller => 'response', :action => 'edit', :id => review_feedbacks.first.id, :return => "feedback" %> feedback for Review <%= count %>
                <% else %>                 
                    <%= link_to "Give feedback", :controller => 'response', :action => 'new_feedback', :id => review.id %> for Review <%= count %>
                <% end %>
                 </td></tr>
                 </td></tr>
            <% end %>
    <% end %>
        </table><hr>
</pre>
</pre>
[[File:Student-feedback.png]]


* Add javascript to tabbed review so it can be hidden or shown by clicking the show/hide reviews.
* Add javascript to tabbed review so it can be hidden or shown by clicking the show/hide reviews.
Line 75: Line 101:
   });
   });
</script>
</script>
</pre>
* Add statistic view to student end by using the team_review_statistics.html.erb file.
'''app/views/grades/_participant.html.erb '''
<pre>
<TR id="<%= prefix %>_reviews" style="display:none; background-color: white;">
    <TD COLSPAN="10">
    <%= render :partial=>'grades/team_review_statistics', :locals => {:prefix => 'user', :participant => participant, :pscore => pscore} %>
    <%= render :partial=>'grades/tabbed_reviews', :locals => {:prefix => 'user', :participant => participant, :rscore => pscore[:review]} %>
    </TD>
</TR>
</pre>
'''app/views/grades/_tabbed_reviews.html.erb'''
<pre>
<% if @assignment.is_answer_tagging_allowed %>
            <span class="spn_qsttog" id="tag_prompt_toggler" title="Click to display/hide tags" onclick="toggle_tag_prompt()">hide tags</span>
        <% end %>
</pre>
'''app/views/grades/_team_review_statistics.html.erb'''
<pre>
<% if controller.action_name != "view_my_scores"%>
    <tr>
        <td colspan="10">
<% end %>
<% if rscore and rscore[:assessments].length > 0 %>
    <%flag_varying_rubrics=false%>
@@ -25,6 +29,9 @@
    <%if flag_varying_rubrics==false%>
        <%= render :partial => 'grades/review_table', :locals => { :ctrl => 'review', :caction => 'view_review', :symbol => "review".to_sym, :prefix => prefix, :assignment => participant.assignment, :reviews => rscore[:assessments], :collabel => "Review", :rowlabel => "Reviewer" }%>
    <%end%>
    </td>
</tr>
<% end %> 
<% if controller.action_name != "view_my_scores"%>   
        </td>
    </tr>
<% end %>
</pre>
</pre>
===Instructor End===
===Instructor End===


* Create new method view2 for response_controller
* Create new method view_instructor for response_controller


''' app/controllers/response_controller.rb '''
''' app/controllers/response_controller.rb '''
<pre>
<pre>
def view2
def view_instructor
     @response = Response.find(params[:id])
     @response = Response.find(params[:id])
     @map = @response.map
     @map = @response.map
     set_content2
     set_content_instructor
     render "response/view"
     render "response/view"
   end  
   end  
......
......
......
# sets content in instructor end
    @participant = @map.reviewer
def set_content_instructor(new_response = false)
......
def set_content2(new_response = false)
     @contributor = @map.contributor
     @contributor = @map.contributor
     members = TeamsUser.where(team_id: params[:team])
     members = TeamsUser.where(team_id: params[:team])
     @user = members.first
     @user = members.first
     @participant = AssignmentParticipant.where(user_id: @user.user_id, parent_id: params[:assignment]).first
     @participant = AssignmentParticipant.where(user_id: @user.user_id, parent_id: params[:assignment]).first
@@ -264,7 +273,9 @@ def set_content(new_response = false)
    @assignment = @participant.assignment
    questionnaires = @assignment.questionnaires
     @questions = retrieve_questions questionnaires, @assignment.id
     @questions = retrieve_questions questionnaires, @assignment.id
     # @pscore has the newest versions of response for each response map, and only one for each response map (unless it is vary rubric by round)
     # @pscore has the newest versions of response for each response map, and only one for each response map (unless it is vary rubric by round)
Line 145: Line 129:
     @round = params[:round]
     @round = params[:round]
   end   
   end   
  # retrieves questions for questionnaires
  def retrieve_questions(questionnaires, assignment_id)
    questions = {}
    questionnaires.each do |questionnaire|
      round = AssignmentQuestionnaire.where(assignment_id: assignment_id, questionnaire_id: questionnaire.id).first.used_in_round
      questionnaire_symbol = if !round.nil?
                              (questionnaire.symbol.to_s + round.to_s).to_sym
                            else
                              questionnaire.symbol
                            end
      questions[questionnaire_symbol] = questionnaire.questions
    end
    questions
  end
</pre>
</pre>


'''app/views/grades/_tabbed_reviews.html.erb '''
* change the url jumping to after clicking to a certain reviewer.
<pre>
                <% prefix = nil %>
......
<% if controller.action_name == "view_my_scores"%>
                    <!-- Generate the feedback html -->
                    <% map = FeedbackResponseMap.where(reviewed_object_id: review.id, reviewer_id: participant.id).first
                        review_feedbacks = map.try :response %>
                    <% if review_feedbacks && !review_feedbacks.empty? %>
                        <%= link_to "View", :controller => 'response', :action => 'view', :id => review_feedbacks.first.id %> or
                        <%= link_to "Edit", :controller => 'response', :action => 'edit', :id => review_feedbacks.first.id, :return => "feedback" %> feedback for Review <%= count %>
                    <% else %>
                        <%= link_to "Give feedback", :controller => 'response', :action => 'new_feedback', :id => review.id %> for Review <%= count %>
                    <% end %>
                </td></tr>
                <% end %>
            <% end %>
        </table><hr>
    <% end %>
</pre>
 
''' app/views/grades/_view_heatgrid.html.erb '''
''' app/views/grades/_view_heatgrid.html.erb '''
<pre>
<pre>
                      <th class="sorter-false"> <a target="_blank" href="../response/view2?id=<%= review.id.to_s %>&&team=<%= @team.id %>&&round=<%= vm.round.to_s %>&&assignment=<%= @assignment.id %>"   
<th class="sorter-false"> <a target="_blank" href="../response/view_instructor?id=<%= review.id.to_s %>&&team=<%= @team.id %>&&round=<%= vm.round.to_s %>&&assignment=<%= @assignment.id %>"  data-toggle="tooltip" data-placement="right" title="Click to see details"><%= user_name.to_s %></a>  </th>
data-toggle="tooltip" data-placement="right" title="Click to see details"><%= user_name.to_s %></a>  </th>
 
</pre>
</pre>


* render grades/tabbed_reviews for review details at instructor end
''' app/views/response/view.html.erb '''
''' app/views/response/view.html.erb '''
<pre>
<pre>
<% if controller.action_name != "view2" %>
<!-- If not on instructor end, then use display_as_html method to construct html -->
<% if controller.action_name != "view_instructor" %>
     <% file_url = nil %>
     <% file_url = nil %>
     <%= @response.display_as_html(nil, nil, file_url) %>
     <%= @response.display_as_html(nil, nil, file_url) %>
Line 190: Line 168:
</pre>
</pre>


* show right round for instructor-end
* show right round according to which round user click at instructor-end
'''app/views/grades/_tabbed_reviews.html.erb'''
'''app/views/grades/_tabbed_reviews.html.erb'''
<pre>
<pre>
<% rounds.each do |round| %>
        <% if controller.action_name == "view_instructor"%>
         <% next if round.to_s != @round.to_s %>
         <% next if round.to_s != @round.to_s %>
        <% end %>
......
<% end %>
</pre>
* Create construct_instructor_htm_helper in respond model in order to not show review number
'''app/views/grades/_tabbed_reviews.html.erb'''
<pre>
    <% prefix = "instructor" if controller.action_name == "view_instructor" %>
</pre>
</pre>


* Create construct_instructor2_html in order to not show review number
''' app/models/response.rb '''
''' app/models/response.rb '''
<pre>
<pre>
if prefix == "instructor"
if prefix == "instructor"
       self_id = self.id.to_s
       self_id = self.id.to_s
       code = construct_instructor2_html identifier, self_id, count
       code = construct_instructor_html_helper identifier, self_id, count
    elsif prefix # has prefix means view_score page in instructor end
      self_id = prefix + '_' + self.id.to_s
      code = construct_instructor_html identifier, self_id, count
......
......
def construct_instructor2_html identifier, self_id, count
def construct_instructor_html_helper identifier, self_id, count
     identifier += '<table width="100%">'\
     identifier += '<table width="100%">'\
'<tr>'\
'<tr>'\
Line 218: Line 203:
</pre>
</pre>


* Show just one review in instructor-end
'''app/views/grades/_tabbed_reviews.html.erb'''
'''app/views/grades/_tabbed_reviews.html.erb'''
<pre>
<pre>
<% if controller.action_name != "view2"%>
<% rscore[:assessments].select{|response| response.round == round}.reverse.uniq{|response| response.map_id}.sort_by{|response| response.map_id}.each_with_index do |review, index| %>
            <h3>Round <%= round %></h3>
    <% if controller.action_name == "view_instructor"%>
            <% if controller.action_name != "view_my_scores" %>
          <% next if review.reviewer != @reviewer || count > 0 %>
                <a href="#" name= <%= participant.id.to_s + '_' + round.to_s + "Link" %> onClick="toggleElement('<%= participant.id.to_s + '_' + round.to_s %>','round <%= round.to_s%> reviews');return false;">show round <%= round.to_s%> reviews</a>
    <% end %>
            <% end %>
......
        <% end %>
<% end %>
        <% if @assignment.is_answer_tagging_allowed %>
            <span class="spn_qsttog" id="tag_prompt_toggler" title="Click to display/hide tags" onclick="toggle_tag_prompt()">hide tags</span>
@@ -37,6 +39,7 @@
                <% review_feedback = nil %>
                <% count = count + 1 %>
                <% prefix = nil %>
                <% prefix = "instructor" if controller.action_name == "view2" %>
</pre>
</pre>
[[File:instructorend.png ]]
=='''Test Plans'''==
*The rspec test plan is written such that each requirement(for student-end and instructor-end) will have at least 1 different test.
*The test plan is also used as a check of bug fix implementation. It ensures that each required function is implemented and each bug is well fixed.
*The test file can be manually executed the by running "rspec ./spec/features/tabbed_reviews_spec.rb" on rails terminal.
*Use Unit test to test all the codes mentioned above with respect to views, models and controllers.
=='''Test files'''==


* Show just one review in instructor-end
*tabbed_review_spec.rb created and added 3 test cases for alternate view of reviews
'''app/views/grades/_tabbed_reviews.html.erb'''
'''spec/features/tabbed_review_spec.rb'''
<pre>
<pre>
<% rounds.each do |round| %>
require "spec_helper"
        <% if controller.action_name == "view2"%>
require 'rspec'
        <% next if round.to_s != @round.to_s %>
describe "alternate view of reviews" do
        <% end %>
    before(:each) do
        <% if controller.action_name != "view2"%>
      assignment1 = create(:assignment, name: "111", directory_path: 'test_assignment')
            <h3>Round <%= round %></h3>
      create_list(:participant, 3)
            <% if controller.action_name != "view_my_scores" %>
      create(:assignment_node)
@@ -33,8 +35,13 @@
      create(:deadline_type, name: "submission")
        <% if @assignment.is_answer_tagging_allowed %>
      create(:deadline_type, name: "review")
            <span class="spn_qsttog" id="tag_prompt_toggler" title="Click to display/hide tags" onclick="toggle_tag_prompt()">hide tags</span>
      create(:deadline_type, name: "metareview")
        <% end %>
      create(:deadline_type, name: "drop_topic")
        <% if controller.action_name != "view2"%>
      create(:deadline_type, name: "signup")
        <table class="table table-striped" id=<%= participant.id.to_s + '_' + round.to_s %> <%= style="display: none;" if controller.action_name != "view_my_scores"%>>
      create(:deadline_type, name: "team_formation")
        <% end %>
      create(:deadline_right)
            <% rscore[:assessments].select{|response| response.round == round}.reverse.uniq{|response| response.map_id}.sort_by{|response| response.map_id}.each_with_index do |review, index| %>
      create(:deadline_right, name: 'Late')
                <% if controller.action_name == "view2"%>
      create(:deadline_right, name: 'OK')
                <% next if review.reviewer != @reviewer || count > 0 %>
      create(:assignment_due_date, deadline_type: DeadlineType.where(name: 'review').first, due_at: Time.now.in_time_zone + 1.day)
                <% end %>
      create(:topic)
                <tr><td>
      create(:topic, topic_name: "TestReview")
                <% review_feedback = nil %>
      create(:team_user, user: User.where(role_id: 2).first)
                <% count = count + 1 %>
      create(:team_user, user: User.where(role_id: 2).second)
@@ -72,7 +79,9 @@
      create(:assignment_team)
                </td></tr>
      create(:team_user, user: User.where(role_id: 2).third, team: AssignmentTeam.second)
                <% end %>
      create(:signed_up_team)
            <% end %>
      create(:signed_up_team, team_id: 2, topic: SignUpTopic.second)
        <% if controller.action_name != "view2"%>   
      create(:assignment_questionnaire)
        </table><hr>
      create(:question)
        <% end %>
      # create(:review_response_map, reviewer_id: User.where(role_id: 2).third.id)
     <% end %>
      # create(:review_response_map, reviewer_id: User.where(role_id: 2).second.id, reviewee: AssignmentTeam.second)
<% else %>
      # sleep(10000)
</pre>
    end
 
  def load_alternate
    login_as('student2064')
    expect(page).to have_content "User: student2064"
    expect(page).to have_content "111"
 
    click_link "111"
    expect(page).to have_content "Submit or Review work for 111"
    expect(page).to have_content "Alternate View"
 
    click_link "Alternate View"
    expect(page).to have_content "Contributor"
  end
 
  it "shows the correct alternate view" do
    # Load questionnaire with generic setup
    load_alternate
    expect(page).to have_content "Contributor"
    expect(page).to have_content "Stats"
    expect(page).to have_content "Submitted work"
    expect(page).to have_content "Author Feedback"
    expect(page).to have_content "Teammate Review"
    expect(page).to have_content "Final Score"
    expect(page).to have_content "Range"
    expect(page).to have_content "Average"
    expect(page).to have_content "student2064"
 
    expect(page).to have_css "a[href='#']", text: 'hide stats'
    expect(page).to have_css "a[href='#']", text: 'show submission'
    #expect(page).to have_css "a[href='#']", text: 'show reviews'
    #page.should have_selector('table tr', text: 'show reviews')
    #find(:xpath, "//tr[contains(.,'show reviews')]/td/a", :text => 'show reviews').click
    #expect(page).to have_content "Writeup"
    end
      #describe "grades/participant", :type => :view do
        #it 'exists' do
          #find(:xpath, "//tr[contains(.,'show reviews')]/td/a", :text => 'show reviews').click
        #end
      #end
end
 
describe "test for instructor" do
  before(:each) do
    # assignment and topic
    create(:assignment,
          name: "Test Assignment",
          directory_path: "Test Assignment",
          rounds_of_reviews: 2,
          staggered_deadline: true,
          max_team_size: 1,
          allow_selecting_additional_reviews_after_1st_round: true)
    create_list(:participant, 3)
    create(:topic, topic_name: "Topic_1")
    create(:topic, topic_name: "Topic_2")
    create(:topic, topic_name: "Topic_3")
    assignment_id = Assignment.where(name: 'Test Assignment')[0].id
 
    #visit "/response/view2?id=#{review_id}&&team=#{team_id}&&round=1&&assignment=#{assignment_id}"
    # rubric
    create(:questionnaire, name: "TestQuestionnaire1")
    create(:questionnaire, name: "TestQuestionnaire2")
    create(:question, txt: "Question1", questionnaire: ReviewQuestionnaire.where(name: 'TestQuestionnaire1').first, type: "Criterion")
    create(:question, txt: "Question2", questionnaire: ReviewQuestionnaire.where(name: 'TestQuestionnaire2').first, type: "Criterion")
     create(:assignment_questionnaire, questionnaire: ReviewQuestionnaire.where(name: 'TestQuestionnaire1').first, used_in_round: 1)
    create(:assignment_questionnaire, questionnaire: ReviewQuestionnaire.where(name: 'TestQuestionnaire2').first, used_in_round: 2)
    questionnaire_id = ReviewQuestionnaire.first.id.to_s


=='''Test files'''==
    # deadline type
    create(:deadline_type, name: "submission")
    create(:deadline_type, name: "review")
    create(:deadline_type, name: "metareview")
    create(:deadline_type, name: "drop_topic")
    create(:deadline_type, name: "signup")
    create(:deadline_type, name: "team_formation")
    create(:deadline_right)
    create(:deadline_right, name: 'Late')
    create(:deadline_right, name: 'OK')


* what test done
    create(:team_user, user: User.where(role_id: 2).first)
::* how to do that
      create(:team_user, user: User.where(role_id: 2).second)
      create(:assignment_team)
      create(:team_user, user: User.where(role_id: 2).third, team: AssignmentTeam.second)
      create(:signed_up_team)
      create(:signed_up_team, team_id: 2, topic: SignUpTopic.second)
    visit "response/view2?id=#{questionnaire_id}&&team=1&&round=1&&assignment=#{assignment_id}"
  end


'''path to add '''
  it "can go to review details" do
    expect(page).to have_content "Toggle navigation"
    expect(page).to have_content "Papers on Expertiza"
    expect(page).to have_content "response"
  end


<pre>
end
test
</pre>
</pre>


=='''Team Information'''==
=='''Team Information'''==
#[https://github.com/aranne/expertiza Forked Issue on Github]
#[https://github.com/aranne/expertiza Forked Issue on Github]
#Weiran Fu (@ncsu.edu)
#Weiran Fu (wfu4@ncsu.edu)
#Qingyan Wang (qwang20@ncsu.edu)
#Qingyan Wang (qwang20@ncsu.edu)
#Hongli Wang (hwang85@ncsu.edu)
#Hongli Wang (hwang85@ncsu.edu)
Line 293: Line 366:
#[http://expertiza.ncsu.edu/ The live Expertiza website]  
#[http://expertiza.ncsu.edu/ The live Expertiza website]  
#[http://wikis.lib.ncsu.edu/index.php/Expertiza Expertiza project documentation wiki]
#[http://wikis.lib.ncsu.edu/index.php/Expertiza Expertiza project documentation wiki]
#[https://github.com/WintersLt/expertiza GitHub Project Repository Fork]
#[http://bit.ly/myexpertiza  Demo link]
#[https://relishapp.com/rspec Rspec Documentation]
#[https://relishapp.com/rspec Rspec Documentation]

Latest revision as of 22:26, 7 November 2019

About Expertiza

Expertiza is a open source project currently for CSC517 instructor and students forming groups, submit work, review, and view grades. The project is based on Ruby on Rails framework and the code is on Github: https://github.com/expertiza/expertiza. Expertiza serves wiki page, collecting information of all internal information and updates of all versions.

Problem Statement

Issue details

1) On student end, Expertiza uses grades/_reviews.html.erb partial file to display reviewers, scores and review details; on instructor end, it uses Response model methods to construct html file for review pages. We need to use the same kinds of partials in instructor-end and student-end.

How to get the view of reviewers, scores and review details on student end? Click "Alternate View" button, and click "show reviews" link. The reviewers and scores pop up on the top and review details are below them. How to get the view of reviewers, scores and review details on instructor end? Click "Scores" for a certain assignment and choose a team. Click "Statistics" tab and then reviewers and scores table show up. Click "Reviews" tab and then click one of students' name link in the table head. And a new page pops up with review details.

2) Give Feedback link at view_my_score at student-end should not appear at folded view. Where to find Give Feedback link? On student end, click "Alternate View" button and click "show reviews" link. At the bottom of a certain review, a give feedback link shows up.

What to do

1) We need to use the same kinds of partials in instructor-end and student-end. So we decide to use grades/_team_statistics.html.erb to display reviewers and scores and to use grades/_tabbed_reviews.html.erb to display review details for both student-end and instructor-end.

2) Give Feedback link at view_my_score should appear at the bottom of expanded view.

Solution

For student end, split reviews.html.erb into two partial files. One is grades/team_review_statistics.html.erb for reviewers and scores. The other one is grades/tabbed_reviews.html.erb for review details. For instructor end, it used to use response model methods to construct html files for review details, now we use grades/tabbed_reviews.html.erb to display review details. So student end and instructor end both use grades/team_review_statistics and grades/tabbed_reviews to display reviewers, scores and review details.

Deploy Link

Our project is deployed at VCL

Student End

  • In student end, render grades/team_review_statistics and grades/tabbed_reviews two partial files instead of grades/reviews partial file.

app/views/grades/_participant.html.erb

<TR id="<%= prefix %>_reviews" style="display:none; background-color: white;">
    <TD COLSPAN="10">
    <%= render :partial=>'grades/team_review_statistics', :locals => {:prefix => 'user', :participant => participant, :pscore => pscore} %>
    <%= render :partial=>'grades/tabbed_reviews', :locals => {:prefix => 'user', :participant => participant, :rscore => pscore[:review]} %>
    </TD>
</TR>
  • Add statistics view to student end by using the team_review_statistics.html.erb file.

app/views/grades/_participant.html.erb

<TR id="<%= prefix %>_reviews" style="display:none; background-color: white;">
    <TD COLSPAN="10">
    <%= render :partial=>'grades/team_review_statistics', :locals => {:prefix => 'user', :participant => participant, :pscore => pscore} %>
    <%= render :partial=>'grades/tabbed_reviews', :locals => {:prefix => 'user', :participant => participant, :rscore => pscore[:review]} %>
    </TD>
</TR>

app/views/grades/_team_review_statistics.html.erb

<% if controller.action_name != "view_my_scores"%>
    <tr>
        <td colspan="10">
<% end %> 

<% if controller.action_name != "view_my_scores"%>    
        </td>
    </tr>
<% end %> 

  • Add feed back link in student end view.

app/views/grades/_tabbed_reviews.html.erb

    <% if controller.action_name == "view_my_scores"%>
                    <!-- Generate the feedback html -->
                    <% map = FeedbackResponseMap.where(reviewed_object_id: review.id, reviewer_id: participant.id).first
                        review_feedbacks = map.try :response %>
                    <% if review_feedbacks && !review_feedbacks.empty? %>
                        <%= link_to "View", :controller => 'response', :action => 'view', :id => review_feedbacks.first.id %> or
                        <%= link_to "Edit", :controller => 'response', :action => 'edit', :id => review_feedbacks.first.id, :return => "feedback" %> feedback for Review <%= count %>
                    <% else %>
                        <%= link_to "Give feedback", :controller => 'response', :action => 'new_feedback', :id => review.id %> for Review <%= count %>
                    <% end %>
                </td></tr>
    <% end %>

  • Add javascript to tabbed review so it can be hidden or shown by clicking the show/hide reviews.

app/views/grades/_tabbed_reviews.html.erb

<script type="text/javascript">
  toggle_tag_prompt = function() {
    $('.tag_prompt_container').toggle();
  };
  $( document ).ready(function() {
    $('#tag_prompt_toggler').click(function () {
      if ($('#tag_prompt_toggler').text() == "hide tags")
        $('#tag_prompt_toggler').text("show tags")
      else
        $('#tag_prompt_toggler').text("hide tags")
    });
  });
</script>

Instructor End

  • Create new method view_instructor for response_controller

app/controllers/response_controller.rb

def view_instructor
    @response = Response.find(params[:id])
    @map = @response.map
    set_content_instructor
    render "response/view"
  end 
......
# sets content in instructor end
def set_content_instructor(new_response = false)
    @contributor = @map.contributor
    members = TeamsUser.where(team_id: params[:team])
    @user = members.first
    @participant = AssignmentParticipant.where(user_id: @user.user_id, parent_id: params[:assignment]).first
    @assignment = @participant.assignment
    questionnaires = @assignment.questionnaires
    @questions = retrieve_questions questionnaires, @assignment.id
    # @pscore has the newest versions of response for each response map, and only one for each response map (unless it is vary rubric by round)
    @pscore = @participant.scores(@questions)
    @reviewer = @map.reviewer
    @round = params[:round]
  end  
  # retrieves questions for questionnaires
  def retrieve_questions(questionnaires, assignment_id)
    questions = {}
    questionnaires.each do |questionnaire|
      round = AssignmentQuestionnaire.where(assignment_id: assignment_id, questionnaire_id: questionnaire.id).first.used_in_round
      questionnaire_symbol = if !round.nil?
                               (questionnaire.symbol.to_s + round.to_s).to_sym
                             else
                               questionnaire.symbol
                             end
      questions[questionnaire_symbol] = questionnaire.questions
    end
    questions
  end
  • change the url jumping to after clicking to a certain reviewer.

app/views/grades/_view_heatgrid.html.erb

<th class="sorter-false"> <a target="_blank" href="../response/view_instructor?id=<%= review.id.to_s %>&&team=<%= @team.id %>&&round=<%= vm.round.to_s %>&&assignment=<%= @assignment.id %>"  data-toggle="tooltip" data-placement="right" title="Click to see details"><%= user_name.to_s %></a>  </th>
  • render grades/tabbed_reviews for review details at instructor end

app/views/response/view.html.erb

<!-- If not on instructor end, then use display_as_html method to construct html -->
<% if controller.action_name != "view_instructor" %>
    <% file_url = nil %>
    <%= @response.display_as_html(nil, nil, file_url) %>
    <br/>
<% else %>
    <TR style="display:none; background-color: white;">
        <TD COLSPAN="10">
        <%= render :partial=>'grades/tabbed_reviews', :locals => {:prefix => 'user', :participant => @participant, :rscore => @pscore[:review], :reviewer => @reviewer, :round => @round} %>
        </TD>
    </TR>
<% end %>
  • show right round according to which round user click at instructor-end

app/views/grades/_tabbed_reviews.html.erb

<% rounds.each do |round| %>
        <% if controller.action_name == "view_instructor"%>
        <% next if round.to_s != @round.to_s %>
        <% end %>
......
<% end %>
  • Create construct_instructor_htm_helper in respond model in order to not show review number

app/views/grades/_tabbed_reviews.html.erb

     <% prefix = "instructor" if controller.action_name == "view_instructor" %>

app/models/response.rb

if prefix == "instructor"
      self_id = self.id.to_s
      code = construct_instructor_html_helper identifier, self_id, count
......
def construct_instructor_html_helper identifier, self_id, count
    identifier += '<table width="100%">'\
						 '<tr>'\
						 '<td align="left" width="70%"><b>Review ' + '</b>   '\
						 '<a href="#" name= "review_' + self_id + 'Link" onClick="toggleElement(' + "'review_" + self_id + "','review'" + ');return false;">hide review</a>'\
						 '</td>'\
						 '<td align="left"><b>Last Reviewed:</b>'\
						 "<span>#{(self.updated_at.nil? ? 'Not available' : self.updated_at.strftime('%A %B %d %Y, %I:%M%p'))}</span></td>"\
						 '</tr></table>'
  end
  • Show just one review in instructor-end

app/views/grades/_tabbed_reviews.html.erb

<% rscore[:assessments].select{|response| response.round == round}.reverse.uniq{|response| response.map_id}.sort_by{|response| response.map_id}.each_with_index do |review, index| %>
     <% if controller.action_name == "view_instructor"%>
          <% next if review.reviewer != @reviewer || count > 0 %>
     <% end %>
......
 <% end %>

Test Plans

  • The rspec test plan is written such that each requirement(for student-end and instructor-end) will have at least 1 different test.
  • The test plan is also used as a check of bug fix implementation. It ensures that each required function is implemented and each bug is well fixed.
  • The test file can be manually executed the by running "rspec ./spec/features/tabbed_reviews_spec.rb" on rails terminal.
  • Use Unit test to test all the codes mentioned above with respect to views, models and controllers.

Test files

  • tabbed_review_spec.rb created and added 3 test cases for alternate view of reviews

spec/features/tabbed_review_spec.rb

require "spec_helper"
require 'rspec'
describe "alternate view of reviews" do
    before(:each) do
      assignment1 = create(:assignment, name: "111", directory_path: 'test_assignment')
      create_list(:participant, 3)
      create(:assignment_node)
      create(:deadline_type, name: "submission")
      create(:deadline_type, name: "review")
      create(:deadline_type, name: "metareview")
      create(:deadline_type, name: "drop_topic")
      create(:deadline_type, name: "signup")
      create(:deadline_type, name: "team_formation")
      create(:deadline_right)
      create(:deadline_right, name: 'Late')
      create(:deadline_right, name: 'OK')
      create(:assignment_due_date, deadline_type: DeadlineType.where(name: 'review').first, due_at: Time.now.in_time_zone + 1.day)
      create(:topic)
      create(:topic, topic_name: "TestReview")
      create(:team_user, user: User.where(role_id: 2).first)
      create(:team_user, user: User.where(role_id: 2).second)
      create(:assignment_team)
      create(:team_user, user: User.where(role_id: 2).third, team: AssignmentTeam.second)
      create(:signed_up_team)
      create(:signed_up_team, team_id: 2, topic: SignUpTopic.second)
      create(:assignment_questionnaire)
      create(:question)
      # create(:review_response_map, reviewer_id: User.where(role_id: 2).third.id)
      # create(:review_response_map, reviewer_id: User.where(role_id: 2).second.id, reviewee: AssignmentTeam.second)
      # sleep(10000)
    end

  def load_alternate
    login_as('student2064')
    expect(page).to have_content "User: student2064"
    expect(page).to have_content "111"

    click_link "111"
    expect(page).to have_content "Submit or Review work for 111"
    expect(page).to have_content "Alternate View"

    click_link "Alternate View"
    expect(page).to have_content "Contributor"
  end

  it "shows the correct alternate view" do
    # Load questionnaire with generic setup
    load_alternate
    expect(page).to have_content "Contributor"
    expect(page).to have_content "Stats"
    expect(page).to have_content "Submitted work"
    expect(page).to have_content "Author Feedback"
    expect(page).to have_content "Teammate Review"
    expect(page).to have_content "Final Score"
    expect(page).to have_content "Range"
    expect(page).to have_content "Average"
    expect(page).to have_content "student2064"

    expect(page).to have_css "a[href='#']", text: 'hide stats' 
    expect(page).to have_css "a[href='#']", text: 'show submission' 
    #expect(page).to have_css "a[href='#']", text: 'show reviews' 
    #page.should have_selector('table tr', text: 'show reviews')
    #find(:xpath, "//tr[contains(.,'show reviews')]/td/a", :text => 'show reviews').click
    #expect(page).to have_content "Writeup"
    end
      #describe "grades/participant", :type => :view do
        #it 'exists' do
          #find(:xpath, "//tr[contains(.,'show reviews')]/td/a", :text => 'show reviews').click
        #end
      #end
end

describe "test for instructor" do
  before(:each) do
    # assignment and topic
    create(:assignment,
           name: "Test Assignment",
           directory_path: "Test Assignment",
           rounds_of_reviews: 2,
           staggered_deadline: true,
           max_team_size: 1,
           allow_selecting_additional_reviews_after_1st_round: true)
    create_list(:participant, 3)
    create(:topic, topic_name: "Topic_1")
    create(:topic, topic_name: "Topic_2")
    create(:topic, topic_name: "Topic_3")
    assignment_id = Assignment.where(name: 'Test Assignment')[0].id

    #visit "/response/view2?id=#{review_id}&&team=#{team_id}&&round=1&&assignment=#{assignment_id}"
    # rubric
    create(:questionnaire, name: "TestQuestionnaire1")
    create(:questionnaire, name: "TestQuestionnaire2")
    create(:question, txt: "Question1", questionnaire: ReviewQuestionnaire.where(name: 'TestQuestionnaire1').first, type: "Criterion")
    create(:question, txt: "Question2", questionnaire: ReviewQuestionnaire.where(name: 'TestQuestionnaire2').first, type: "Criterion")
    create(:assignment_questionnaire, questionnaire: ReviewQuestionnaire.where(name: 'TestQuestionnaire1').first, used_in_round: 1)
    create(:assignment_questionnaire, questionnaire: ReviewQuestionnaire.where(name: 'TestQuestionnaire2').first, used_in_round: 2)
    questionnaire_id = ReviewQuestionnaire.first.id.to_s

    # deadline type
    create(:deadline_type, name: "submission")
    create(:deadline_type, name: "review")
    create(:deadline_type, name: "metareview")
    create(:deadline_type, name: "drop_topic")
    create(:deadline_type, name: "signup")
    create(:deadline_type, name: "team_formation")
    create(:deadline_right)
    create(:deadline_right, name: 'Late')
    create(:deadline_right, name: 'OK')

    create(:team_user, user: User.where(role_id: 2).first)
      create(:team_user, user: User.where(role_id: 2).second)
      create(:assignment_team)
      create(:team_user, user: User.where(role_id: 2).third, team: AssignmentTeam.second)
      create(:signed_up_team)
      create(:signed_up_team, team_id: 2, topic: SignUpTopic.second)
    visit "response/view2?id=#{questionnaire_id}&&team=1&&round=1&&assignment=#{assignment_id}"
  end

  it "can go to review details" do
    expect(page).to have_content "Toggle navigation"
    expect(page).to have_content "Papers on Expertiza"
    expect(page).to have_content "response"
  end

end


Team Information

  1. Forked Issue on Github
  2. Weiran Fu (wfu4@ncsu.edu)
  3. Qingyan Wang (qwang20@ncsu.edu)
  4. Hongli Wang (hwang85@ncsu.edu)
  5. Mentor: Mohit Jain (mjain6@ncsu.edu)

References

  1. Expertiza on GitHub
  2. The live Expertiza website
  3. Expertiza project documentation wiki
  4. Rspec Documentation