CSC/ECE 517 Spring 2018- Project E1804: OSS project Yellow: Topic management
This page provides a description of our Expertiza project.
>>>>>>>>>>>>> RUN "./setup.sh" BEFORE RUNNING APP; CONTENTS OF "bower.json" HAVE CHANGED <<<<<<<<<<<<<<
Important links
- Deployed app: https://vast-bastion-53966.herokuapp.com/
- Github repo: https://github.com/dburcal/expertiza
- Github pull request: https://github.com/expertiza/expertiza/pull/1144
Introduction
Expertiza Background
Expertiza is a web-based framework designed in Ruby on Rails to serve as a medium for students and professors to interact, provide feedback, and manage their assignments. It facilitates all of the electronic turn-ins and provides a good place to review your peers' work.
Problem Statement
To enhance topic management for instructors and students.
- Issue 971: Change create topic UI to AJAX
- Issue 926: Sort topics by topic number in assignment#edit
- Issue 718: Allow instructors to give feedback when accepting or rejecting suggestions,add comments on those suggestions.
Our Goals
We planned to improve the functionality such that when instructors have to manage the topics for an assignment, they can do so much quicker and more reliably.
Files Edited
- app/spec/controllers/sign_up_sheet_controller_spec.rb
- app/spec/features/topic_suggestion_spec.rb
- app/assets/javascripts/application.js
- app/assets/javascripts/signup.js
- app/assets/stylesheets/application.scss
- app/assets/stylesheets/signup.scss.erb
- app/controllers/sign_up_sheet_controller.rb
- app/controllers/suggestion_controller.rb
- app/models/sign_up_topic.rb
- app/views/sign_up_sheet/_add_signup_topics.html.erb
- app/views/sign_up_sheet/_add_topics.html.erb
- app/views/sign_up_sheet/_topic.html.erb
- app/views/signup/signup_topics.html.erb
- app/views/suggestion/show.html.erb
- app/views/suggestion/update_comment.html.erb
- app/views/suggestion/edit_comment.html.erb
- app/views/suggestion/_form_comment.html.erb
- config/routes.rb
- bower.json
Issue 971: Change create topic UI to AJAX
Implementation
- The implementation of this functionality was started using < http://wiki.expertiza.ncsu.edu/index.php/CSC/ECE_517_Fall_2017/E1781_Topic_Management > as a baseline. This issue calls for the replacement of the html table with a javascript table using jsGrid. The use of jsGrid in conjunction with AJAX calls allow for the user to complete actions such as edit and add for topics from a single page.
- Furthermore, replacing the html table with a dynamic table should not disrupt the html table for topic signup available to the student.
Further Implementation Required
- Addition of advertisements is pending. Values for formatting the width of the table columns to enable the table to fit the jQuery UI tab is present, but not fully operational.
Functionality walkthroughs
- The functionality below is for an instructor user unless mentioned otherwise.
- Log in to < https://vast-bastion-53966.herokuapp.com > using the username "instructor6" and password "password".
- Select "Manage..." -> "Assignments" from the dropdown at the top.
- Click the for the "Final Project (and Design Document)" assignment.
- Select the "Topics" tab for the assignment.
- Note the available actions(add,edit,delete) and bookmarks available for each unfinished topic.
Add topic
To add a topic to an assignment,
- Select the on the Topics tab of the unfinished assignment that was navigated to in the first section.
- In the subsequent Add Dialog, enter values for the Topic id, Topic name and Number of slots and click Save.
- The newly added topic will appear in the table sorted by Topic #.
Delete topic
To Delete a topic from the Topics tab,
- Select the for the topic you wish to delete.
- Confirm that you would like to delete the selected topic.
- Note that the topic is no longer in the table.
Edit topic
To edit a topic to an assignment,
- Select the on the desired topic of the unfinished assignment that was navigated to in the first section.
- In the subsequent Edit Dialog, make the desired edits and click Save.
- The newly editted topic will appear with the new value(s).
- Log in to < https://vast-bastion-53966.herokuapp.com > using the username "instructor6" and password "password".
- Select "Manage..." -> "Assignments" from the dropdown at the top.
- Click the for the "OSS project/Writing assignment 2" assignment.
- Select the "Topics" tab for the assignment.
- Note the unavailable actions(add,edit,delete) and bookmarks absent for each unfinished topics.
Sign up for a topic as a student
- Log in as "student5918" with the password "password" at < https://vast-bastion-53966.herokuapp.com >.
- Select "Final Project (and Design Document)" from the assignments list.
- Select "Signup sheet" from the assignment menu.
- Select the check icon next to the desired topic.
Testing
Automatic Testing
- For testing the new functionality of methods in the sign_up_sheet_controller, we tested that JSON was correctly rendered from the load_add_signup_topics method, which loads the topics into the edit assignment table
- There were four JSON outputs which were tested:
- slots_waitlisted is correctly outputted as JSON
- slots_filled_value is correctly outputted as JSON
- slots_available is correctly outputted as JSON
- participants is correctly outputted as JSON
- There were four JSON outputs which were tested:
- app/spec/controllers/sign_up_sheet_controller_spec.rb
- For testing the new functionality of methods in the sign_up_sheet_controller, we tested that JSON was correctly rendered from the load_add_signup_topics method, which loads the topics into the edit assignment table
describe'#load_add_signup_topics' do before(:each) do allow(SignUpTopic).to receive(:where).with("assignment_id = ?", "1").and_return([topic]) allow(SignUpTopic).to receive(:find_slots_filled).with("1").and_return([double('SignedUpTeam', count: 0,topic_id: 1)]) allow(SignUpTopic).to receive(:find_slots_waitlisted).with("1").and_return([double('SignedUpTeam', count: 0,topic_id: 1)]) allow(SignedUpTeam).to receive(:find_team_participants).with("1").and_return([double('User', topic_id: 0)]) params = {id: 1, topic_id: 1} get :load_add_signup_topics, params @parsed_response = JSON.parse( response.body ) end it 'returns slots_waitlisted correctly as JSON' do expect(@parsed_response["sign_up_topics"][0]["slots_waitlisted"]).to eq(0) end it 'returns slots_filled_value correctly as JSON' do expect(@parsed_response["sign_up_topics"][0]["slots_filled_value"]).to eq(0) end it 'returns slots_available correctly as JSON' do expect(@parsed_response["sign_up_topics"][0]["slots_available"]).to eq(1) end context 'There are no participants' do it 'returns participants as empty as JSON' do expect(@parsed_response["sign_up_topics"][0]["participants"]).to be_empty end end end
Testing Walkthrough
- In order to test this area of the project, you need to:
- Install the environment using ./setup.sh
- run bundle install
- run 'rspec spec/controllers/sign_up_sheet_controller_spec.rb'
- In order to test this area of the project, you need to:
Further Testing Required
- Overall there are a few more outputs from the method that could be tested. These are values that are pivotal to the table and so testing them is important, so if we are able to, we would like to add those to the project as well, but besides that are proud of the tests that do work.
Issue 926: Sort topics by topic number in assignment#edit
Implementation
- We let JSGrid handle the sorting of topics by the headers in the table. We altered the AJAX controller to enable this sorting.
Further Implementation Required
- While the table is able to be sorted by the header row, whenever you make a new topic the initial list of topics is not resorted right away. This leads to say E1502, a new topic just made, to be at the bottom of the list when it would normally precede say E1700, if it were in the same topic list.
Testing
- There is no testing required in this aspect, since sorting is a feature of the AJAX table itself, and therefore it wouldn't make sense to test the implementation.
Issue 718: Allow instructors to give feedback when accepting or rejecting suggestions
Implementation
- We implemented the functionality for approving and rejecting any topic suggestions made by the students. Furthermore we have included comments which the instructor can give for a particular topic suggestion. Some tests have been added to verify the above functionality. The functionality of editing comments also has been correctly implemented.
Goals achieved according to the spec file:
- 1. Implement approving and rejecting suggestions.
- 2. Adding comments to the suggestions(can be multiple).
Additional Goals achieved:
- 1. Editing comments in the suggestions view.
- 2. Removing redundant Approve and Reject suggestion button which were present on the suggestion screen.
Files which have been changed to reflect the changes:
- 1. app/view/suggestion/show.html.erb
- To see all the comments and to edit individual comments:
- 1. app/view/suggestion/show.html.erb
<table class="general" cellpadding=5 border="1"> <colgroup> <col width=15% /> <col width=15% /> <col width=25% /> </colgroup> <tr> <th>Commenter</th> <th>Vote</th> <th>Comment</th> </tr> <% for suggestioncomment in @suggestion.suggestion_comments %> <tr> <td class="listingRow" align="center"><%=suggestioncomment.commenter %></td> <td class="listingRow" align="center"> <%if suggestioncomment.vote == 'Y' %> Yes <%elsif suggestioncomment.vote == 'N'%> No <%elsif suggestioncomment.vote == 'R'%> Revise <!-- Two more crieteria for displaying comments. During Approval and Denial. Issue #1781-718 --> <%elsif suggestioncomment.vote == 'A'%> Approved <%elsif suggestioncomment.vote == 'D'%> Denied <%else%> -- <%end%> </td> <td class="listingRow" align="center"><%= suggestioncomment.comments%></td> <td><%= link_to "Edit Comment",{:controller=>"suggestion",:action=>"edit_comment",:suggestioncomment=>suggestioncomment}%></td> </tr> <%end%> </table>
- To add a comment:
<% if @suggestion.status != 'Approved' && @suggestion.status != 'Rejected' %> <b>Add new comment and vote</b> <br> <%= form_tag :action => 'submit', :id => @suggestion do %> <%= text_area "suggestion_comment", "comments", :rows => 5, :cols => 50 %> <p> <%= "Vote"%> <%= select("suggestion_comment","vote",{"Yes" =>"Y","No"=>"N","Revise"=>"R"}) %> </p> <%= submit_tag "Submit vote", :name => 'add_comment' %> <% if @suggestion.status != 'Approved' && @suggestion.status != 'Rejected' && session[:user] != nil && (session[:user].role_id == 2 || session[:user].role_id == 3 || session[:user].role_id == 4) %> <%= submit_tag "Approve suggestion", :name => 'approve_suggestion' %> <%= submit_tag "Reject suggestion", :name => 'reject_suggestion' %> <% end %> <% end %> <% end %>
- 2. app/views/suggestion/edit_comment.html.erb
- Link to form for editing
- 2. app/views/suggestion/edit_comment.html.erb
< Editing suggestion comments> <tr><%= render :partial =>"form_comment", suggestion_comment: @suggestioncomment %></tr> <tr><%#= link_to 'Back', :action => 'show', :id => suggestion %></tr>
- 3. app/views/suggestion/_form_comment.html.erb
- Form for editing:
- 3. app/views/suggestion/_form_comment.html.erb
<%= form_for :suggestioncomment,:url=>{:controller=>"suggestion",:action=>"update_comment",:id=>@suggestioncomment.id},:method=>"post" do |form|%> <div class="field"> <%= form.label :comments %> <%= form.text_area :comments,value: @suggestioncomment.comments,:rows => 5, :cols => 50%> </div> <div class = "field"> <%= form.label :vote%> <%= form.text_field :vote,value: @suggestioncomment.vote %> </div> <%= form.hidden_field :suggestioncomment,value: @suggestioncomment%> <%= form.hidden_field :id,value: @suggestioncomment.suggestion_id%> <div class="actions"> Click to Update <%= form.submit%> </div> <% end %> <tr><%= link_to 'Back',:controller=>"suggestion", :action => "show",:id => @suggestioncomment.suggestion_id %></tr>
- 4. app/controllers/suggestion_controller.rb
- For approving and rejecting suggestions:
def approve_suggestion # 1781 - 718 issue # The Instructor should be able to give feed backs during the times of approval as well. # Thus we are getting the comments through the request when approval is made and saving those in # Database with Vote type as A - meaning approval if params[:suggestion_comment][:comments] && params[:suggestion_comment][:comments] != "" @suggestioncomment = SuggestionComment.new(vote: 'A', comments: params[:suggestion_comment][:comments]) @suggestioncomment.suggestion_id = params[:id] @suggestioncomment.commenter = session[:user].name @suggestioncomment.save end @suggestion = Suggestion.find(params[:id]) if @suggestion.update_attribute('status', 'Accepted') flash[:notice] = 'The suggestion has been successfully accepted.' else flash[:error] = 'An error occurred when accepting the suggestion.' end redirect_to action: 'show', id: @suggestion end def reject_suggestion # 1781 - 718 issue # The Instructor should be able to give feed backs during the times of rejection as well. # Thus we are getting the comments through the request when denial is made and saving those in # Database with Vote type as D - meaning reject if params[:suggestion_comment][:comments] && params[:suggestion_comment][:comments] != "" @suggestioncomment = SuggestionComment.new(vote: 'D', comments: params[:suggestion_comment][:comments]) @suggestioncomment.suggestion_id = params[:id] @suggestioncomment.commenter = session[:user].name @suggestioncomment.save end @suggestion = Suggestion.find(params[:id]) if @suggestion.update_attribute('status', 'Rejected') flash[:notice] = 'The suggestion has been successfully rejected.' else flash[:error] = 'An error occurred when rejecting the suggestion.' end redirect_to action: 'show', id: @suggestion end
- For adding a comment:
def add_comment @suggestioncomment = SuggestionComment.new(vote: params[:suggestion_comment][:vote], comments: params[:suggestion_comment][:comments]) @suggestioncomment.suggestion_id = params[:id] @suggestioncomment.commenter = session[:user].name if @suggestioncomment.save flash[:notice] = "Your comment has been successfully added." else flash[:error] = "There was an error in adding your comment." end if current_role_name.eql? 'Student' redirect_to action: "student_view", id: params[:id] else redirect_to action: "show", id: params[:id] end end
- For editing comments:
def edit_comment @suggestioncomment = SuggestionComment.find(params[:suggestioncomment]) end
- For updating comments:
def update_comment @suggestioncomment = SuggestionComment.find(params[:id]) @suggestioncomment.update(suggestioncomment_params) #if @suggestioncomment.update(suggestioncomment_params) #redirect_to :action=>"show",:id=>@suggestioncomment.suggestion_id #end end
- Images showing the jist for testing
Testing
UI Testing
- 'The following steps should be followed to test the functionality:'
- '1. Login as a student2065/2064/2066 with password:password'
- '2. Select any assignment,for example Click on view(RHS of the assignment)"wiki Textbook" and then click on "suggest a topic".'
- '3. Add your suggestion.'
- '4. Login as instructor6 and password:password.'
- '5. Go to assignments and search for "wiki textbook".'
- '6. Click on the "view suggestion" icon on the right side of the screen.'
- '7. Add a comment on that suggestion and submit.'
- '8. Can edit the comment by clicking on the "Edit Comment" button.'
- '9. Login as student and you can see the comment when you go to the assignment and associated topic.'
- Tests have been added to the spec for testing the above functionality.The following changes are made to the related spec files.
- spec/features/topic_suggestion_spec.rb
Automatic Testing =
#part 2########################### #######part 2##################### #############part 2###############issue 718 ###################part 2######### #########################part 2### describe "topic_suggestion" do it "Instructor set an assignment which allow student suggest topic and register student2066" do user = User.find_by(name: 'student2066') stub_current_user(user, user.role.name, user.role) visit '/student_task/list' expect(page).to have_content "Assignment_suggest_topic" # student2066 suggest topic click_link('Assignment_suggest_topic',match: :first) expect(page).to have_content "Suggest a topic" click_link('Suggest a topic',match: :first) fill_in 'suggestion_title', with: 'suggested_topic' fill_in 'suggestion_description', with: 'suggested_description' click_button 'Submit',match: :first expect(page).to have_content "Thank you for your suggestion" user = User.find_by(name: 'instructor6') stub_current_user(user, user.role.name, user.role) # instructor approve the suggestion topic # DUE date need to be added here visit '/suggestion/list?id=1&type=Assignment' expect(page).to have_content "Assignment_suggest_topic" click_link('View',match: :first) expect(page).to have_content "suggested_description" click_button 'Approve suggestion',match: :first expect(page).to have_content "The suggestion was successfully approved." end it " student2066 hold suggest topic and suggest a new one and student2064 enroll on waitlist of suggested topic", js: true do # login_as "student2064" user = User.find_by(name: 'student2064') stub_current_user(user, user.role.name, user.role) visit '/student_task/list' expect(page).to have_content "Assignment_suggest_topic" # student2064 suggest topic click_link('Assignment_suggest_topic',match: :first) expect(page).to have_content "Suggest a topic" click_link('Suggest a topic',match: :first) fill_in 'suggestion_title', with: 'suggested_topic' fill_in 'suggestion_description', with: 'suggested_description' click_button 'Submit',match: :first expect(page).to have_content "Thank you for your suggestion" user = User.find_by(name: 'instructor6') stub_current_user(user, user.role.name, user.role) # instructor approve the suggestion topic visit '/suggestion/list?id=1&type=Assignment' expect(page).to have_content "Suggested topics for Assignment_suggest_topic" expect(page).to have_content "suggested_topic" click_link('View',match: :first) expect(page).to have_content "suggested_description" click_button 'Approve suggestion',match: :first expect(page).to have_content "The suggestion was successfully approved." # case 2 student already have topic switch to new topic # need two students one to be on the waitlist of previous suggested topic, # the other one (student2064) is holding it and suggest another topic and wish to switch to the new one user = User.find_by(name: 'student2066') stub_current_user(user, user.role.name, user.role) visit '/student_task/list' click_link('Assignment_suggest_topic',match: :first) click_link('Signup sheet',match: :first) first("img[title='Signup']").click # log in student2064 user = User.find_by(name: 'student2064') stub_current_user(user, user.role.name, user.role) visit '/student_task/list' click_link('Assignment_suggest_topic',match: :first) expect(page).to have_content "Suggest a topic" click_link('Suggest a topic',match: :first) fill_in 'suggestion_title', with: 'suggested_topic2_will_switch' fill_in 'suggestion_description', with: 'suggested_description_2' click_button 'Submit',match: :first expect(page).to have_content "Thank you for your suggestion" # login_as instructor6 to approve the 2nd suggested topic user = User.find_by(name: 'instructor6') stub_current_user(user, user.role.name, user.role) # instructor approve the suggestion topic visit '/tree_display/list' visit '/suggestion/list?id=1&type=Assignment' expect(page).to have_content "Suggested topics for Assignment_suggest_topic" expect(page).to have_content "suggested_topic2_will_switch" # find link for new suggested view visit '/suggestion/2' # click_link('View') expect(page).to have_content "suggested_description" click_button 'Approve suggestion',match: :first expect(page).to have_content "The suggestion was successfully approved." # login as student 2064 to switch to new approved topic user = User.find_by(name: 'student2064') stub_current_user(user, user.role.name, user.role) visit '/student_task/list' click_link('Assignment_suggest_topic',match: :first) click_link('Signup sheet',match: :first) expect(page).to have_content "Your approved suggested topic" expect(page).to have_content "suggested_topic" expect(page).to have_content "suggested_topic2_will_switch" first("img[title='Switch Topic']").click # login as student 2066 to see if it's holding the topic rather than on the wait list user = User.find_by(name: 'student2066') stub_current_user(user, user.role.name, user.role) visit '/student_task/list' expect(page).to have_content "suggested_topic" # login as studnet 2064 to see if it's already shifted to the new suggested topic user = User.find_by(name: 'student2064') stub_current_user(user, user.role.name, user.role) visit '/student_task/list' expect(page).to have_content "suggested_topic2_will_switch" end ######################################## # Case 3: # One team is holding a topic. They sent a suggestion for new topic, and keep themselves in old topic ######################################## it "student2066 hold suggest topic and suggest a new one, but wish to stay in the old topic", js: true do # login_as "student2066" user = User.find_by(name: 'student2066') stub_current_user(user, user.role.name, user.role) visit '/student_task/list' expect(page).to have_content "Assignment_suggest_topic" # student2066 suggest topic click_link('Assignment_suggest_topic',match: :first) expect(page).to have_content "Suggest a topic" click_link('Suggest a topic',match: :first) fill_in 'suggestion_title', with: 'suggested_topic' fill_in 'suggestion_description', with: 'suggested_description' click_button 'Submit',match: :first expect(page).to have_content "Thank you for your suggestion" # login_as "instructor6" user = User.find_by(name: 'instructor6') stub_current_user(user, user.role.name, user.role) # instructor approve the suggestion topic # DUE date need to be added here visit '/suggestion/list?id=1&type=Assignment' click_link('View',match: :first) expect(page).to have_content "suggested_description" click_button 'Approve suggestion',match: :first expect(page).to have_content "The suggestion was successfully approved." ###################################### # One team is holding a topic. They sent a suggestion for new topic ###################################### # login_as "student2066" user = User.find_by(name: 'student2066') stub_current_user(user, user.role.name, user.role) visit '/student_task/list' expect(page).to have_content "Assignment_suggest_topic" # student2066 suggest topic click_link('Assignment_suggest_topic',match: :first) expect(page).to have_content "Suggest a topic" click_link('Suggest a topic',match: :first) fill_in 'suggestion_title', with: 'suggested_topic2_without_switch' fill_in 'suggestion_description', with: 'suggested_description2_without_switch' find('#suggestion_signup_preference').find(:xpath, 'option[2]').select_option click_button 'Submit',match: :first expect(page).to have_content "Thank you for your suggestion" # login_as "instructor6" user = User.find_by(name: 'instructor6') stub_current_user(user, user.role.name, user.role) # instructor approve the suggestion topic visit '/tree_display/list' visit '/suggestion/list?id=1&type=Assignment' expect(page).to have_content "Suggested topics for Assignment_suggest_topic" expect(page).to have_content "suggested_topic2_without_switch" find(:xpath, "//tr[contains(.,'suggested_topic2_without_switch')]/td/a", text: 'View').click # click_link('View') expect(page).to have_content "suggested_description2_without_switch" click_button 'Approve suggestion',match: :first expect(page).to have_content "The suggestion was successfully approved." # login_as "student2066" user = User.find_by(name: 'student2066') stub_current_user(user, user.role.name, user.role) visit '/student_task/list' expect(page).to have_content "Assignment_suggest_topic" click_link('Assignment_suggest_topic',match: :first) expect(page).to have_content "Signup sheet" click_link('Signup sheet',match: :first) expect(page).to have_content "suggested_topic2_without_switch" # click_link('publish_approved_suggested_topic') visit '/sign_up_sheet/publish_approved_suggested_topic/2?assignment_id=1' # find(:xpath, "//tr[contains(.,'suggested_topic2_without_switch')]/td/a", :figure=>"Publish Topic").click visit '/student_task/list' expect(page).to have_content "suggested_topic" # login_as "student2064" user = User.find_by(name: 'student2064') stub_current_user(user, user.role.name, user.role) visit '/student_task/list' expect(page).to have_content "Assignment_suggest_topic" click_link('Assignment_suggest_topic',match: :first) expect(page).to have_content "Signup sheet" click_link('Signup sheet',match: :first) expect(page).to have_content " suggested_topic2_without_switch" find(:xpath, "(//img[@title='Signup'])[2]").click visit '/student_task/list' expect(page).to have_content " suggested_topic2_without_switch" end it "professor could approve anonymous suggestion topic" do # login_as "student2064" user = User.find_by(name: 'student2064') stub_current_user(user, user.role.name, user.role) visit '/student_task/list' expect(page).to have_content "Assignment_suggest_topic" # student2064 suggest topic click_link('Assignment_suggest_topic',match: :first) expect(page).to have_content "Suggest a topic" click_link('Suggest a topic',match: :first) fill_in 'suggestion_title', with: 'suggested_topic' fill_in 'suggestion_description', with: 'suggested_description' find(:xpath, "//input[@name='suggestion_anonymous']").click click_button 'Submit',match: :first expect(page).to have_content "You have submitted an anonymous suggestion." user = User.find_by(name: 'instructor6') stub_current_user(user, user.role.name, user.role) # instructor approve the suggestion topic visit '/suggestion/list?id=1&type=Assignment' expect(page).to have_content "Suggested topics for Assignment_suggest_topic" expect(page).to have_content "suggested_topic" click_link('View',match: :first) expect(page).to have_content "suggested_description" click_button 'Approve suggestion',match: :first expect(page).to have_content "The suggestion was successfully approved." end end
Further Testing Required
- More tests need to be added to test whether an instructor can edit a comment or not.