CSC/ECE 517 Fall 2023 - E2372. Reimplement QuizQuestion and its child classes
Objectives
There are four methods in QuizQuestion super class. edit, view_question_text, view_completed_question, and complete. These 4 (model) methods will return different html strings depends on the question type.
edit
: What to display` - if an instructor (etc.) is creating or editing a questionnaire (questionnaires_controller.rb)view_question_text
: What to display if an instructor (etc.) is viewing a questionnaire (questionnaires_controller.rb)complete
: What to display - if a student is filling out a questionnaire (response_controller.rb)view_completed_question
: What to display if a student is viewing a filled-out questionnaire (response_controller.rb)
Our goals are
- Make a complete UML diagram for these classes with their attributes, methods, and class methods.
- Check all implementations of instance methods, extract duplicated codes, and put them into the highest-level class.
- Thoroughly test the QuizQuestion class
Overview of the Classes
quiz_question.rb
QuizQuestion is a base class used to represent a question in a quiz. It inherits from the Question class. This class is associated with QuizQuestionChoice objects through a has_many relationship, indicating that a QuizQuestion can have multiple choices. It handles the creation of HTML for different views related to a question, such as editing, completing, and viewing the question text.
Methods:
edit
: to generate the HTML form for editing a question,complete
: used for completing the question in the quizview_question_text
: to display the question and its choices)view_completed_question
: abstract method used after quiz has been submitted, andisvalid
: to validate the question, especially ensuring it has text
multiple_choice_radio.rb
This class extends QuizQuestion and represents a multiple-choice question where the participant can select only one answer from a set of radio buttons.
Methods:
edit
: overridden to add HTML for a set of radio buttons that allows the user to select one choice as the correct answer; also includes text inputs for the choices' textcomplete method
: renders HTML for the user to complete this type of question in a quiz interfaceview_completed_question
: displays the correct answer, the user's answer, and visual feedback on whether the user's answer was correctisvalid
: extended to ensure that not only does each choice have text, but also that exactly one correct answer is selected
multiple_choice_checkbox.rb
The MultipleChoiceCheckbox class is a subclass of QuizQuestion and represents a multiple-choice question where the participant can select multiple answers through checkboxes.
Methods:
edit
: add HTML input elements for checkboxes, allowing multiple correct answers to be selected, along with text inputs for entering the choices.complete
: allows users to complete the question, displaying checkboxes for them to select their answersview_completed_question
: shows correct answers and whether the user's selections were correctisvalid
: validates the question, ensures there are multiple correct answers if it's a checkbox question.
true_false.rb
This class also extends QuizQuestion and is specialized for true/false questions.
Methods:
edit
: includes HTML radio buttons specifically for 'True' and 'False' optionscomplete
: render the true/false question for the user to answerview_completed_question
: displays the correct answer and the user's answerisvalid
: checks to ensure that the question has text and that a correct answer is designated
Our Solution
- We implemented
isvalid(choice_info)
in QuizQuestion.
def isvalid(choice_info) @valid = 'valid' @valid = 'Please make sure all questions have text' if txt == '' @valid end
- We factored out duplicate code in
edit
from the subclasses and placed it into the superclass QuizQuestion. Nowedit
in the subclasses inherit partially from QuizQuestion.
edit
in QuizQuestion
def edit @quiz_question_choices = QuizQuestionChoice.where(question_id: id) @html = '<tr><td>' @html += '<textarea cols="100" name="question[' + id.to_s + '][txt]" ' @html += 'id="question_' + id.to_s + '_txt">' + txt + '</textarea>' @html += '</td></tr>' @html += '<tr><td>' @html += 'Question Weight: ' @html += '<input type="number" name="question_weights[' + id.to_s + '][txt]" ' @html += 'id="question_wt_' + id.to_s + '_txt" ' @html += 'value="' + weight.to_s + '" min="0" />' @html += '</td></tr>' end
edit
in MultipleChoiceCheckbox
def edit super # for i in 0..3 [0, 1, 2, 3].each do |i| @html += '<tr><td>' @html += '<input type="hidden" name="quiz_question_choices[' + id.to_s + '][MultipleChoiceCheckbox][' + (i + 1).to_s + '][iscorrect]" ' @html += 'id="quiz_question_choices_' + id.to_s + '_MultipleChoiceCheckbox_' + (i + 1).to_s + '_iscorrect" value="0" />' @html += '<input type="checkbox" name="quiz_question_choices[' + id.to_s + '][MultipleChoiceCheckbox][' + (i + 1).to_s + '][iscorrect]" ' @html += 'id="quiz_question_choices_' + id.to_s + '_MultipleChoiceCheckbox_' + (i + 1).to_s + '_iscorrect" value="1" ' @html += 'checked="checked" ' if @quiz_question_choices[i].iscorrect @html += '/>' @html += '<input type="text" name="quiz_question_choices[' + id.to_s + '][MultipleChoiceCheckbox][' + (i + 1).to_s + '][txt]" ' @html += 'id="quiz_question_choices_' + id.to_s + '_MultipleChoiceCheckbox_' + (i + 1).to_s + '_txt" ' @html += 'value="' + @quiz_question_choices[i].txt + '" size="40" />' @html += '</td></tr>' end @html.html_safe # safe_join(@html) end
- We reimplemented QuizQuestion class and child classes methods to use
@html
(an instance variable) instead ofhtml.
UML Diagram
Final Phase Changes
Further Refactor quiz_question.rb
and subclasses
- Currently,
quiz_question.rb
is a subclass ofquestion.rb
, however it does not follow the Liskov Substitution Principle because the methodisvalid
is not present in the superclass. By refactoringisvalid
into the superclass, the entireQuestion
hierarchy can be made more maintainable.
- Most methods of
quiz_question.rb
contain hard-coded HTML, which makes the code inflexible and prone to breaking, and also violates SOP. We should refactor the HTML building logic out of the methods to improve readability and maintainability. This would also make testing easier.
- The
edit
method inmultiple_choice_checkbox.rb
andmultiple_choice_radio.rb
uses the following code to generate the options:[0, 1, 2, 3].each do |I|
. This limits the number options to exactly 4. This should be refactored to allow an arbitrary number of choices in a range.
Testing
- Most existing Rspec tests for
quiz_question.rb
and child classes test for exact strings that match the HTML. These tests will break if anything about the classes are changed, therefore they are not robust tests. The tests should be refactored to test the logic of these methods rather than their exact outputs.
Relevant Links
Github repository: https://github.com/opheliasin/expertiza
Pull request: https://github.com/expertiza/expertiza/pull/2682
Team
Mentor
Aditi Vakeel
Members
Nathan Sudduth (ncsuddut@ncsu.edu)
Ophelia Sin (oysin@ncsu.edu)
Yi Chen (ychen282@ncsu.edu)