CSC/ECE 517 Spring 2026 - E2619. Student Quizzes Frontend
E2619: Student Quizzes — Design Document
Introduction
Expertiza is an educational web application collaboratively developed and maintained by students and faculty at NCSU. As an open-source project built on the Ruby on Rails platform, its code is accessible on GitHub. The platform enables students to provide peer reviews and refine their work based on feedback.
This design document describes the implementation of the E2619 Student Quizzes project, covering both the frontend (React/TypeScript) and backend (Ruby on Rails API). The project builds on the E2607 questionnaire rendering code and extends it with quiz-specific capabilities: directing students to quizzes/reviews, scoring quiz responses, specifying correct answers, and handling fill-in-the-blank questions.
Project Overview
Quizzes in Expertiza are designed to ensure that reviewers comprehend the material they
are evaluating. When an assignment includes quizzes (require_quiz = true),
submitting teams create quizzes based on their submissions. Reviewers must complete the
quizzes before reviewing to demonstrate their understanding. If a reviewer performs
poorly, their review can be discounted to maintain quality.
Prior State of the Codebase
The existing implementation provided:
- Backend: Models for
QuizQuestionnaire,QuizItem,
QuizQuestionChoice,QuizResponseMap,Response, andAnswer. Basic CRUD for questionnaires and questions existed.Response#aggregate_questionnaire_scorecalculated scores usinganswer * weightonly, with no correctness check.
- Frontend: Questionnaire editor (create/edit/delete), student task list, review
tableau display, and assignment configuration. The questionnaire editor supported the "Quiz" type but had no UI to specify correct answers for quiz items.
Gaps Addressed
| Area | Gap | Status |
|---|---|---|
| Student Task View | No mechanism to direct a student to a quiz, a review, or both based on require_quiz |
✅ Implemented |
| Quiz Scoring | aggregate_questionnaire_score did not check correctness |
✅ Implemented |
| Correct Answer Specification | No UI to mark correct answers for quiz items | ✅ Implemented |
| Fill-in-the-Blank (TextField) | No correct answer support for text questions | ✅ Implemented (single answer, case-insensitive match) |
| Quiz Taking Interface | No student-facing page to take a quiz | ✅ Implemented
(reuses existing review form in quiz mode) |
| Quiz Response Map Creation | No endpoint to create a QuizResponseMap for a student before taking a quiz |
✅ Implemented via
|
Design
Architecture Overview
┌─────────────────────────────────────────────────────────────┐
│ Frontend (React/TS) │
│ │
│ AssignedReviews ──→ [Take Quiz] ──→ TeammateReview │
│ │ (quiz mode) │ │
│ │ └──→ [redirect] │
│ └──→ [Start/Open Review] ──→ TeammateReview │
│ (review mode) │
│ QuestionnaireEditor ──→ correct_answer fields per type │
└──────────────────────────┬──────────────────────────────────┘
│ REST API
┌──────────────────────────┴──────────────────────────────────┐
│ Backend (Rails API) │
│ │
│ QuizResponseMapsController (POST /quiz_response_maps) │
│ ResponsesController (create / update / submit) │
│ ResponseMapsController (index — filters quiz maps) │
│ Response model (aggregate_questionnaire_score) │
│ StudentTask model (quiz gateway fields) │
└─────────────────────────────────────────────────────────────┘
Implemented Features
1) Student Task View — Quiz/Review Gateway
The StudentTask model was extended with four fields computed per participant:
require_quiz— whether the assignment requires a quizhas_quiz_questionnaire— whether a Quiz-type questionnaire is assignedquiz_questionnaire_id— the id of that questionnairequiz_taken— true only when a submittedResponseexists
against the student's QuizResponseMap
The frontend AssignedReviews component uses these fields to gate each review
row. If require_quiz && has_quiz_questionnaire && !quiz_taken, a yellow
"Take Quiz" button is shown instead of the review button.
2) Quiz Gateway Flow
When a student clicks "Take Quiz":
- The frontend calls
POST /quiz_response_mapswith the assignment id and
user id.
- The backend finds (or creates) a
QuizResponseMapwhere
reviewer_id == reviewee_id(self-referential — the student quizzes themselves) andreviewed_object_idis the quiz questionnaire id.
- The frontend navigates to the existing
TeammateReviewpage with
questionnaire_type=Quizand aredirect_afterparameter encoding the review URL.
- After the student submits the quiz, the page shows the score and automatically
redirects to the actual review after 1.2 seconds.
Quiz maps are distinguished from review maps by the invariant
reviewer_id == reviewee_id. No STI type column is needed.
3) Quiz Scoring
Response#aggregate_questionnaire_score now detects quiz responses by checking
map.reviewer_id == map.reviewee_id.
For quiz item types whose student answer is stored in the comments column
(because the numeric answer column is unused for text/choice items):
| question_type | Student answer location | Scoring method |
|---|---|---|
TextField |
answers.comments |
Case-insensitive exact match
vs
|
MultipleChoiceRadio |
answers.comments |
Case-insensitive
exact match vs
|
MultipleChoiceCheckbox |
answers.comments |
Case-insensitive
exact match vs
|
Checkbox / Scale |
answers.answer (integer) |
|
The final score is returned in the PATCH /responses/:id/submit response body
as total_score and displayed to the student before the redirect.
4) Correct Answer Specification (Frontend)
The QuestionnaireItemsFieldArray component renders a "Correct answer" row for
every item when the questionnaire type is "Quiz":
- Checkbox — a checkbox (is correct / is not correct)
- Scale — a numeric input bounded to the item's weight range
- Multiple choice / Multiple choice checkbox — a dropdown pre-populated from the
item's alternatives
- Text field — a free-text input (case-insensitive match at scoring time)
The correct_answer string column was added to the items table
and is persisted through QuestionnairesController and exposed in
Item#as_json only for quiz items.
5) Review Map Filtering
ResponseMapsController#index was updated to skip quiz maps using a two-layer
guard:
next if map.reviewer_id == map.reviewee_id— reliably excludes quiz maps
regardless of id coincidences between questionnaire ids and assignment ids.
next unless assignment— belt-and-suspenders fallback.
