CSC/ECE 517 Fall 2021 - E2146. Introduce a Student View for instructors

From Expertiza_Wiki
Jump to navigation Jump to search

Problem Statement

Currently, Expertiza is designed such that an instructor can view all the pages a student can, and is given access to a view very similar to that of a student. However, the difference between the navigational bar shown to an instructor and that which is shown to a student means that an instructor cannot perfectly emulate the view that a student has of Expertiza.

This makes activities such as demonstrating how to navigate around the website and access certain parts of assignments more difficult. While an instructor can choose to impersonate a student, this is cumbersome if they simply wish to view things as a student would from within their instructor account.

Additionally, certain features of the navigational bar are redundant. The main feature in question is the Assignments navigational link, which navigates to the same location as Manage... -> Assignments. Having such redundancy is confusing.

To summarize the problem, there is no easy way for an instructor to have the same view as a student at a moment's notice due to a difference between an instructor's and a student's navigational bars.

Goal of this Topic

The goal of this topic has two primary components:

  • Remove the redundant "Assignments" link in the current navigational bar
  • Add a new "Student View" link to the navigational bar. When clicked, the navigational bar changes to that of a student for the remainder of the session, with the addition of an "Instructor View" link. When that link is clicked, the navigational bar changes back to that of an instructor.

By making these changes, the unnecessary redundancy in the navigational bar is removed, and an instructor has an easy ability to demonstrate or visualize expertiza from the perspective of a student.

Specific Statement of Intent

The following is a precise, mechanical description of the intended changes.

When an instructor logs in, they should see a navigational bar with the following components: Home | Manage... | Survey Deployments | Course Evaluation | Profile | Contact Us | Student View

These are the same navigational components that an instructor can currently view, with the removal of Assignments and the addition of Student View.

If an instructor clicks on Student View, they are redirected to the home page and their navigational bar changes to the following:

Home | Assignments | Pending Surveys | Profile | Contact Us | Anonymized view | Instructor View

These are the same navigational links that a student can view, with the addition of Instructor View.

This navigational bar change should persist throughout the session, until the instructor chooses to change their view.

Design

To implement the feature, we first start with the design in our mind for the control flow as shown below.

Implementation

To implement these changes, our theoretical framework was as follows: 1. Add a link to the navigational bar which directs to a new controller and its controller method. 2. Write a controller method which flips the value of a session flag. This session flag determines which navigational bar you see at any time. 3. Whenever the navigational bar is loaded, this flag is checked. If in the "student view" state, it renders the student view version of the instructor's navigational bar. if not, it renders the instructor's normal navigational bar (both with the addition of the link to switch to the other state).

To make this change, the following files were affected:

  • controllers/student_view_controller.rb: a controller with a single unique method, "flip_view", which flips the [:flip_user] session variable when called.
  • views/shared/_navigation.html.erb: the view which handles rendering the navigation bar. Has been edited to check the status of [:flip_user] and render a view accordingly.
  • config/role_instructor.yml: determines various aspects about those assigned the role of instructor. Only change made is the addition of the student_view and instructor_view Menu::Node objects, which are rendered in the navigation bar.
  • config/routes.rb: the routes for expertiza. Modified to allow a route to the newly made "flip_view" method.
  • spec/controllers/student_view_controller_spec.rb: A series of unit tests for the student view controller, ensuring proper functionality and permissions.

Each major change and/or will now be explained in detail.

student_view_controller.rb

This controller contains only two methods, flip_view and _action_allowed? The later is a method common to all controllers, which determines who has access to the method. This is defined for the student_view_controller such that only those with instructor privileges may use it.

The flip_view function is shown below.

  def flip_view
    if(session[:flip_user] == nil || session[:flip_user] == false) then
      session[:flip_user] = true
      redirect_to '/'
    elsif(session[:flip_user] == true) then
      session[:flip_user] = false
      redirect_to '/'
    end
  end

This relatively simple method's purpose is to flip the parity of a session variable named :flip_user between true and false, before then redirecting back to the home page. It also sets this variable to true if uninitialized, as would be the case at the beginning of a session. The route to this method is specified in the routes file as "student_view/flip_view".

_navigation.html.erb

The purpose of this variable becomes clear when we investigate _navigation.html.erb. This file is the first step in rendering the navigation menu at the top. While this process does involve other files, the determination of which menu items to render is handled here, which is the point at which student view interacts. The relevant portion of code is given below:

 ...
 <% if session[:menu] %>
   <% if !session[:flip_user] || session[:flip_user] == nil  %>
     <%= render :partial => 'menu_items/suckerfish',
            :locals => { items: session[:menu].get_menu(0) - [26, 59], level: 0 } %>
   <% elsif session[:user].role.name == 'Instructor' %>
     <% session[:menu] = Role.student.cache[:menu] %>
       <%= render :partial => 'menu_items/suckerfish',
                        :locals => { items: session[:menu].get_menu(0), level: 0 } %>
       <% session[:menu] = Role.instructor.cache[:menu] %>
       <%= render :partial => 'menu_items/suckerfish',
                        :locals => { items: session[:menu].get_menu(0) & [59], level: 0 } %>
   ...

The second line is where the [:flip_user] session variable becomes relevant. If this variable is either false or nil (set to false or uninitialized), then the standard instructor view is rendered (the 26 and 59 subtracted from "items" are removing menu items exclusive to the Student View, Assignments and Instructor View, respectively). If this condition fails (and the user is an instructor), it implies the instructor has set that variable to true. In that case, the student view menu items are found (the cache for the student menu items is pulled from Role.student.cache[:menu]) and then rendered, with the addition of the "Instructor View" button (indicated by the & [59]).

Thus, by clicking the view button, the re-rendered page gains a newly updated navigation bar, depending on the state of session[:flip_user], which will continue to persist for the entire expertiza session.

_role_instructor.yml

It is typically said that one should not edit config files lightly, and so we made sure to be careful with our changes to role_instructor.yml. However, changes were necessary, as this file is what supplies the data called in _navigation.html.erb by Role.instructor.cache[:menu]. It took a while to determine this, but the navigation menu items displayed at the top of the Expertiza view are defined under the :menu: items of this file. Each listed Menu::Node item is rendered within the navigational menu, with associated label, name, and url.

Because there was no way to know what other files or controllers may rely on this file, the only change we made was the addition of two new menu nodes: student_view and instructor_view, both with a url leading to the route for student_view_controller's flip_user method. Each was added to all three databases (development, testing, and deployment), and each was assigned a corresponding id (58 for student_view, and 59 for instructor_view). The added code can be seen below:

   ...
     58: &58 !ruby/object:Menu::Node
       content_page_id:
       controller_action_id: 58
       id: 58
       label: Student View
       name: student_view
       parent:
       parent_id:
       site_controller_id: 58
       url: "/student_view/flip_view"
     59: &59 !ruby/object:Menu::Node
       content_page_id:
       controller_action_id: 59
       id: 59
       label: Instructor View
       name: instructor_view
       parent:
       parent_id:
       site_controller_id: 59
       url: "/student_view/flip_view"
   ...

Thus, these two nodes are now loaded when the navigational bar is loaded, allowing them to appear. However, 59 is removed from the normal instructor view, and is added back in with the student view.

It's worthy to note that, currently, the Assignments menu item is manually removed from the navigation bar in _navigation.html.erb (the subtracted 26 when rendering the instructor view). While it would presumably be more clean to remove the menu node object all together, due to an abundance of caution, we decided it best not to remove any data from this config file, as again, its contents may be used by other controllers and methods we are not aware of. Future development may be able to determine if this caution is unnecessary, and make this change.

This concludes all major changes and additions. All other modifications were minor changes already referenced, and so will not be discussed in detail.

Test Plan

In order to test the functionality of this change, a spec file was created to enforce unit tests on the student view controller, in a file titled "student_view_controller_spec.rb". The goals of this testing file were as follows:

  • only instructors are able to switch between student and instructor view
  • the flip user flag is worked as expected
  • navigation is occurring as expected

We believe that, so long as the above functionality is working correctly, the controller is adequately tested. Testing that only instructors can access the method ensures that it is not accessed by inappropriate users such as students, matching similar functionality for features like student impersonation. Checking that the flip user flag works when navigating between pages and when calling the method ensures that the changes to the view will occur successfully, and that both student and instructor views can be toggled between. Finally, ensuring that navigation works as expected ensures that the method is being called correctly and is having no issues regarding redirection back to the homepage.

A reasonable criticism would be that only using a unit test for the controller would not test to ensure that a user could actually interact with the controller through a visible link in the view. Doing so would require a feature test, presumably involving something like Capybara ensuring that the link is visible and accessible, and that clicking it changes the view as expected. In fact, as was recently taught in Dr. Gehringer's Object Oriented Design class, this is arguably the only sort of test necessary. The internals of how the flip user flag functions and such can be considered internal functions, while we only care about an incoming command (switching views should lead to a measurable change in the navigation bar) and an outgoing command (clicking the switch view button sends a message to switch the views).

Unfortunately, for a reason we were unable to determine, the simplified view Capybara renders would not render the newly updated navigation bar. Various options and variations were tried to correct this issue, but no change had the desired effect (including ensuring we made our changes to the testing data for "role_instructor.yml"). This issue seems to only affect Capybara, as the link is visible and clickable on both the development build and a deployed Heroku build. For the time being, we were thus unable to create a feature test for this topic, although we strongly believe the feature to be working as intended. Should anyone work on this project in the future, this would be a great area of improvement.

Thus, for the moment, asserting the value of the flip_user variable is a stand-in for testing the incoming command (the :flip_user variable is treated as a public side-effect), and the access testing is a stand-in for the outgoing command (the controller must be accessible and able to make its changes).

Actions taken before testing

Before any testing occurs, an instructor and student are built using factories. Then, before each test, the current user is set to be the created instructor. This ensures we have the proper permissions to use the method. The created student is used for testing that we can only access this method as an instructor, not a student. The code for this test context is given below.

describe StudentViewController do
 let(:instructor) { build(:instructor, id: 2) }
 let(:student1) { build(:student, id: 30, name: :Amanda)}
 # student view is only accessible to instructors, so we will make one
 before(:each) do
   stub_current_user(instructor, instructor.role.name, instructor.role)
 end

Access Tests

The first component tested is to ensure that access is properly handled. First, we simply check if we are allowed to access the student_view_controller when logged in as an instructor. We then change our login to that of a student, and check that we are no longer able to take actions with the controller. The code that accomplishes this is given below.

 describe "check access" do
   it "instructor should be able to call flip user" do
     expect(controller.action_allowed?).to be true
   end
   it "student should not be able to call flip user" do
     stub_current_user(student1, student1.role.name, student1.role)
     expect(controller.action_allowed?).to be false
   end
 end

Student View Flag Tests

To ensure that requesting the navigational view be flipped is working as expected, we first test that the flag is nil by default (defaulting to the instructor view), that calling "flip_view" once changes the flag to true, and that calling it again changes the flag to false. Assuming that the changes to the navigational view are working as intended (a different set of links is rendered depending upon the status of the :flip_user variable), this adequately tests that the primary functionality of the Student/Instructor View link is working. The test code is given below.

 describe "the student view flag working as expected" do
   it 'flag should not be initialized by default' do
     expect(session[:flip_user]).to eq nil
   end
   it 'flag should be set to true after selecting student view' do
     post :flip_view
     expect(session[:flip_user]).to eq true
   end
   it 'flag should become false after using student view twice' do
     post :flip_view
     post :flip_view
     expect(session[:flip_user]).to eq false
   end
 end

Navigational Tests

These last tests simply determine that, after flipping from one view to another, the user is redirected back to the home page. This redirection is done to avoid the edge cases involving a user being on a page that should not be accessible to the view they currently have selected (for example, being on an instructor-only page after changing to the student view). The code to test this is given below.


 describe "navigation should work as intended" do
   it 'should be redirected to homepage after entering student view' do
     post :flip_view
     expect(response).to redirect_to("/")
   end
   it 'should be redirected to homepage after exiting student view' do
     post :flip_view
     post :flip_view
     expect(response).to redirect_to("/")
   end
 end
end

This concludes the testing for this feature. So long as all the above tests pass, we can be confident the controller method is working as intended, and thus that the student/instructor view is working properly.

Feature Demo

In place of a feature test, we here present a demo of the functionality working as intended. To summarize the contents, a user logs in as an instructor, clicks "Student View" in the navigational bar, and has their bar be changed to that of a student. They then click a newly added "Instructor View" link, and are returned to the navigational bar of an instructor.

Navi Student View for Instructor Feature Demo

If you would like to test this feature yourself (and you are reading this within ~3 weeks of the original project assignment being due), you can find the same website as demonstrated in the video at this link: http://152.7.99.186:8080/. Follow the same steps shown in the video to try using the feature yourself.

Conclusions and Future Work

We believe this feature to be working in its entirety and hope it will be a helpful tool to improve the teaching options of instructors.

If future changes were to be made to this feature, the following suggestions may be applicable:

  • A means of performing feature tests. Due to an unknown error in capybara's simplified testing view, any tests specifically looking for the Instructor View button seemed to fail. This error has not occurred on any fully rendered version of the application, nor on the Heroku-hosted server. It would be benefitial to implement tests to ensure the link appears correctly for users, but it's not clear how to do so with this issue. Finding a way to feature test would be preferrable.
  • As previously noted, if the Menu::Node objects listed in role_instructor.yml are exclusively used for rendering the navigational bar, the Assignments node object should be removed entirely, and the code where 26 is removed from the items rendered in _navigation.html.erb changed to reflect this removal.

Github

The Github corresponding to this task is publicly available here: https://github.com/Aeront39/expertiza

Contributors

This feature was created as part of Dr. Edward Gehringer's "CSC/ECE 517: Object-Oriented Design and Development" class, Fall 2021. The contributors were: WeiRui Wang, Geoff Garrido, and Ayush Luthra. Our project mentor was Yi Qiu (yqiu9@ncsu.edu)