CSC/ECE 517 Fall 2022 - E2278. Improve assessment360 controller
This is the design document for Expertiza project E2278 Improve assessment360_controller.rb. This document describes the current implementation of the assessment360 controller and the expected design changes as part of this project which is primarily UI focused.
Introduction
[1] is an open source project based on [2] framework. Expertiza provides a functionality that allows instructors get a full or 360-degree overview of a course they teach that includes all students in the course, all assignments, and the scores of every student in each assignment, averages, and final scores as well. This feature is supported by the assessment360 controller.
Issues and Description of Fixes
In our work on improving this feature, we have handled the following issues and made the following improvements:
Refactoring
The previous implementation of the controller made a number of database calls, several of which were extraneous and not required. We have refactored the functions pertaining to these calls so that the number of unneeded calls to the database is reduced for loading teammate and metareviews.
The older implementation of the computation for class averages, and students average scores incorrectly factored in students who had not received grades for an assignment or who had not taken part in an assignment. In addition to correcting this, we also simplified the process by refactoring the code (please see the next section for the code).
The past implementation of the controller was unclear and non-Rubyistic in naming conventions, function names, and implementation in the code. We have refactored the controller to make it more Rubyistic overall and also re-written parts of the code to make it more understandable, as well as more CRUD-like. Additionally, we made several methods whose functionality was required and used only inside this controller private.
UI changes
The previous implementation had 2 views – all_students_all_reviews and course_student_grade_summary. Both of these views cover some of the data required for a 360-degree view of the course. We have combined these pages together into a single page that contains checkboxes which allow the user to select what data they would like to view and shows only those columns they would like to see. This both simplifies and enhances the functioning of the feature.
Variable Names, Cosmetic Issues, Comments
In the older implementation, there were some unclear variable names such as teamed_count, and some other minor cosmetic issues such as a hyphen being displayed for empty fields where a dash would more visible and clearer. These have been fixed in both the code and the UI of the application to be user-friendly and developer friendly. Additionally, comments have been added everywhere to make the code easier to understand and modify for future improvements.
Fix details and Code Explanation
Files Changed
The following files were altered and edited in the course of our work on this feature: Assessment360_Controller.rb (under app/controller) This file contains the controller for the assessment 360 feature which we have improved in our work. The code has been refactored to be cleaner, modular, easier to understand and also to incorporate DRY.
Index.html.erb (under app/views/assessment360) The view for the assessment 360 feature have been combined as stated in the requirements and all the data is now viewable from the index.html.erb page. This page also features checkboxes so the user can view only the data they are interested in without cluttering caused by unneeded data.
Issue Fix Details
1) app/controllers/assessment360_controller.rb
Code changes
1. In all_student_all_reviews function, the DB calls are by creating init_data function.
2. The init_data function, removes DB calls inside nested for-loops (look for old code, below) which makes db call for every iteration, the code has been refactored in such a way that it preloads the required data using assignments.includes([:participants]) (look for new code, below). This way, instead of making multiple db calls across diff functions, we load the required data and thereby reduce the processing time.
3. The major change, which we've incorporated here is moving the functions which weren't used in other modules into private. Moving excess functions to private, helps keep the controller more CRUD-like and differentiates to the developer which functions are used by the controller to work, and which ones are the functions of the controller.
Old Code (with complex for loops and more db calls)
@course_participants.each do |cp| # for each assignment # [aggregrate_review_grades_per_stu, review_count_per_stu] --> [0, 0] %w[teammate meta].each { |type| instance_variable_set("@#{type}_review_info_per_stu", [0, 0]) } students_teamed = StudentTask.teamed_students(cp.user) @teamed_count[cp.id] = students_teamed[course.id].try(:size).to_i @assignments.each do |assignment| @meta_review[cp.id] = {} unless @meta_review.key?(cp.id) @teammate_review[cp.id] = {} unless @teammate_review.key?(cp.id) assignment_participant = assignment.participants.find_by(user_id: cp.user_id) next if assignment_participant.nil? teammate_reviews = assignment_participant.teammate_reviews meta_reviews = assignment_participant.metareviews calc_overall_review_info(assignment, cp, teammate_reviews, @teammate_review, @overall_teammate_review_grades, @overall_teammate_review_count, @teammate_review_info_per_stu) calc_overall_review_info(assignment, cp, meta_reviews, @meta_review, @overall_meta_review_grades, @overall_meta_review_count, @meta_review_info_per_stu) end
Refactored Code (with limited iteration and db calls)
@assignments.each do |assignment| assignment.participants.each do |assignment_participant| reviews[assignment_participant.user_id] = {} unless reviews.key?(assignment_participant.user_id) assignment_reviews = assignment_participant.public_send(reviews_type) if assignment_participant.respond_to? reviews_type # If a student has not taken an assignment or if they have not received any grade for the same, # assign it as nil(not returning anything). This helps in easier calculation of overall grade score = calc_avg_score(assignment_reviews) reviews[assignment_participant.user_id][assignment.id] = score unless score.nil? end end
2) app/views/assessment360/index.html.erb
1. We've created a new file called index.html.erb. This file contains all the logic for the frontend of this module.
2. Major change that has been made is the addition of checkboxes. This way, users can filter the data that they view and view the data efficiently.
3. This page loads all the assignment data, and their metadata like - meta reviews, teammate reviews, peer score, topic and instructor grade. The table also contains Aggregate score and Total grade.
4. The checkboxes have been programmed in a way that whenever a checkbox is disabled, the corresponding column gets removed and the colspan rendering gets adjusted. Along with that, when all assignment parameters are unchecked, the assignment columns disappear completely. Same applies for aggregate score and Total Instructor Grade.
3) config/routes.rb
Removed old route to this page and pointed it to the newly created index page.
You can check all the issues fixed here - https://github.com/expertiza/expertiza/pull/2337
Test Plan
Automated Testing using RSPEC
Please note that we have removed several older tests which tested functions that are now private and inaccessible outside the controller class itself. However, the methods that call these private functions are still tested and tests are passing successfully.
1. After refactoring, most of our new test cases focus on functions which are the core of the controller - covering important aspects like who accesses the page, checking permissions for the page, how the page behaves on edge cases etc.
2. Using RSpec, the test cases cover various scenarios and tests each scenario in depth. Eg, for access control, we check the access for all roles for a user comprehensively to ensure that only people with permissions can view the page.
3. Since most of the work in this module is computations in the back and rendering data in the frontend, we've ensured that the computations happen in their specific order and enough test cases are present for edge cases.
4. For backend computations, we've covered lot of edge cases like no participants to a course, when there are no reviews made by a participant, when assignment doesn't exists, insure existence_of being called before executing index page etc.
5. For frontend, we've covered cases like formatting of the data in the page, null dataset, score being int, float, null, displaying percentages when data is int, float, null etc.
Even though we've moved most of the code into private functions, the core functionalities of the controller are refactored and appropriate test cases are written to ensure high coverage.
Please run the following command to execute the tests:
rspec spec/controllers/assessment360_controller_spec.rb
Testing from UI
The following steps needs to be performed to test this code from UI:
1. Log in as instructor(username: instructor6 , password: password). Please either do step 2 or 3, as both are not required.
2. Select Courses, and the page will show a list of courses. Please select the rightmost icons for any course (we recommend CSC517 Fall 2017, CSC517 Fall 2011 so there’s more data, but it can take some time for it load since there’s a lot of pre-loaded data in the VCL)
3. Alternatively, you can log in and go to this URL: http://152.7.99.75:8080/assessment360/index?course_id=184
4. You should be able to see a webpage with a table and checkboxes at the top. This table is an instructor’s 360-degree view of a course, containing the scores of each students in the course for every assignment the instructor has added to Expertiza.
5. Unchecking some or all of the checkboxes removes and brings back various columns from/to the page. Please feel free to test this out.
Code Coverage
Scope for future improvement
The UI of the index page which shows all of the data can be improved to make it look neater and more aesthetically pleasing. The table’s positioning and centering can be improved for a more convenient experience overall.
The user’s preferred (checked on the UI checkboxes) can be saved to either the Database or a cache, and on subsequent reloads be the fields loaded on the page for a particular. We suggest using the database for this rather than a cache.
Lazy loading can be implemented for the columns the user last chose(assuming these are stored in the DB) and other columns can lazy-loaded if the user clicks on the checkboxes on the screen.
course_student_grade_summary function inside the assessment360_controller.rb contains few unnecessary db calls which can be refactored further to increase the efficiency and reduce the processing time of the module.