CSC/ECE 517 Spring 2026 - E2601. Reimplement student quizzes
Expertiza Background
Expertiza is an open-source peer review platform built on Ruby on Rails and maintained at North Carolina State University. It is used across multiple courses at NCSU and other universities to facilitate structured peer feedback, team-based assignments, and multi-round review workflows.
In Expertiza, instructors define assignments that walk students through a sequence of tasks — submitting work, reviewing peers, taking quizzes, and providing feedback. The system tracks participants, teams, response maps, and responses across all of these activities.
The ongoing reimplementation effort modernizes the Expertiza codebase into a clean Rails JSON API backend and a React SPA frontend, with a focus on proper separation of concerns, RESTful conventions, and comprehensive test coverage. Each semester, CSC 517 students contribute targeted reimplementations of specific subsystems as part of their graduate coursework in object-oriented design.
Project Description
This project — E2601: Reimplement Student Quizzes — focused on the backend infrastructure that governs how students interact with quiz tasks within an assignment workflow. Quizzes in Expertiza are not standalone — they are one task type within a larger ordered sequence that may also include submission, peer review, and feedback stages.
For quizzes to work correctly in this context, two backend systems needed to be built:
- A student-facing task API that surfaces the current state of a student's task queue, including which tasks are complete, which is next, and how to start a quiz task
- A response management API that handles the creation and updating of quiz responses with proper authentication, ownership checks, and queue-order enforcement
Task sequencing and eligibility logic lives directly inside StudentTasksController, supported by two polymorphic inner classes (QuizTaskItem and ReviewTaskItem) that encapsulate task-type-specific behavior without introducing an extra namespace or layer.
What We Built
Task Sequencing (Inside StudentTasksController)
Rather than a separate module, all task ordering and eligibility logic is encapsulated in private methods of StudentTasksController, supported by two inner classes:
| Class | Role |
|---|---|
QuizTaskItem |
Inner class for quiz response maps — handles quiz-specific completion detection, response map lookup/creation, and serialization |
ReviewTaskItem |
Inner class for review response maps — complete when is_submitted is true
|
Both classes share a common interface via their parent BaseTaskItem:
| Method | Purpose |
|---|---|
response_map |
Returns the associated response map (creates one for quiz tasks if needed) |
ensure_response_map! |
Guarantees a response map record exists |
ensure_response! |
Creates or finds the Response record (round: 1, is_submitted: false by default)
|
completed? |
Returns true when a submitted response exists for this map |
to_h |
Serializes the task to a stable JSON payload shape |
The key private controller methods that orchestrate task flow are:
| Method | Role |
|---|---|
build_tasks(context) |
Builds the ordered task list (quiz before review) for a participant |
ensure_response_objects!(tasks) |
Ensures response maps and responses exist for all tasks |
prior_tasks_complete?(tasks, target) |
Returns false if any task before the target is incomplete |
find_task_for_map(tasks, map_id) |
Looks up a task by its response map id |
resolve_context_for_assignment(assignment_id) |
Resolves participant, team membership, assignment, and duty from a given assignment id |
Student Tasks API
We implemented StudentTasksController with five endpoints that give students full visibility into their task workload:
| Endpoint | Method | What It Returns |
|---|---|---|
/student_tasks/list |
GET | All tasks for the current user across their assignments |
/student_tasks/view |
GET | Detailed information for a specific participant task |
/student_tasks/queue |
GET | The full ordered task queue for a given assignment |
/student_tasks/next_task |
GET | The next incomplete task the student should work on |
/student_tasks/start_task |
POST | Attempts to start a task — blocked if prerequisites are incomplete |
Every endpoint requires a valid JWT token and resolves the student's AssignmentParticipant and TeamsParticipant records before delegating to the private task-building flow for eligibility decisions.
Response Management API
We implemented ResponsesController with three endpoints that handle quiz and review response lifecycle:
| Endpoint | Method | What It Does |
|---|---|---|
/responses |
POST | Creates a new response for a quiz or review map |
/responses/:id |
GET | Retrieves a specific response |
/responses/:id |
PATCH | Updates an existing response |
Response creation enforces two layers of protection before any data is written:
- Ownership check — the requesting user must be the reviewer assigned to the response map
- Queue order check — the same task-ordering logic used by
StudentTasksControllermust confirm that all prerequisite tasks are complete before this response can be created
Technical Deep Dive
How Task Sequencing Works
When a student attempts to start a quiz task or create a response, the following sequence occurs inside StudentTasksController:
StudentTasksController or ResponsesController
|
| resolve_context_for_assignment(assignment_id)
v
→ finds AssignmentParticipant by current_user + assignment_id
→ finds TeamsParticipant by participant_id
→ resolves duty (team_participant.duty_id fallback to participant.duty_id)
|
| build_tasks(context)
v
→ loads ReviewResponseMaps for this participant
→ loads quiz questionnaire and existing QuizResponseMaps
→ instantiates QuizTaskItem / ReviewTaskItem in order
|
| prior_tasks_complete?(tasks, current_task)
v
→ iterates tasks before the target
→ calls task.completed? on each
→ returns false if any prior task is incomplete
|
v
Controller either proceeds or renders 403/428
Task ordering rules:
- If review maps exist: quiz task is added first (when duty allows and quiz is available or an existing quiz map is present), then review task (when duty allows).
- If no review maps exist: a quiz-only task is added when duty allows and a quiz questionnaire exists.
How Authentication and Authorization Are Layered
All requests pass through two concerns registered in ApplicationController before reaching any controller logic:
Incoming Request
|
v
JwtToken concern → authenticate_request!
→ reads Authorization: Bearer <token> header
→ decodes token using RSA public key
→ sets @current_user via User.find(auth_token[:id])
→ halts with 401 if token missing, expired, or invalid
|
v
Authorization concern → authorize
→ calls all_actions_allowed?
→ checks super-admin privileges OR action_allowed?
→ halts with 403 if not permitted
|
v
Controller before_actions (e.g. find_and_authorize_map_for_create)
→ map-level ownership checks using current_user
|
v
Controller action
The critical insight here is that find_and_authorize_map_for_create must be a standard before_action — not a prepend_before_action — so that current_user is always populated by the time ownership checks run.
Round-Aware Response Handling
Expertiza supports multi-round assignment workflows. When a response is created, the controller scopes its lookup by both map_id and round:
Response.where(map_id: @map.id, round: round)
.order(:created_at)
.last || Response.new(map_id: @map.id, round: round)
This means that if a response already exists for this map and round, it is updated in place rather than duplicated. If no response exists yet, a new one is initialized. This supports quiz retakes and multi-round review scenarios cleanly.
Design Decisions
Keeping Sequencing Logic in the Controller
A key architectural decision was to keep all task sequencing and eligibility logic directly inside StudentTasksController private methods, rather than in a separate module or namespace. This keeps the flow obvious and readable — a developer can trace the entire request lifecycle without jumping across files.
Controllers are still responsible only for handling HTTP requests and responses. The inner task classes (QuizTaskItem and ReviewTaskItem) operate purely on domain objects such as assignments, participants, and response maps, keeping HTTP concerns out of task behavior.
Polymorphism Without Over-Engineering
Rather than a factory pattern with separate files for each task type, quiz-versus-review differences are encapsulated in two inner classes inside the controller file itself. This preserves polymorphism where it matters — completed?, response_map, and to_h behave differently for quiz and review tasks — without introducing a builder, pipeline, or factory layer.
New task types can still be added by defining a new inner class with the same interface, requiring no changes to orchestration logic.
Single Orchestration Owner
All task ordering decisions flow through one set of private controller methods. Whether a student is viewing their queue, starting a quiz, or submitting a response, the same build_tasks and prior_tasks_complete? logic is reused. This eliminates the risk of subtle inconsistencies that arise when multiple components implement ordering rules independently.
Layered Authorization and Validation
Validation is enforced at multiple layers of the request lifecycle. Authentication and high-level authorization are handled globally through concerns in ApplicationController, while resource-specific checks (such as map ownership) are implemented as controller before_action callbacks.
Task-order enforcement is then handled by the private controller flow, ensuring that even if a request passes authentication and ownership checks, it cannot violate assignment workflow constraints. This layered approach provides defense in depth and reduces the likelihood of invalid state transitions.
Round-Aware Response Handling
To support Expertiza's multi-round workflow model, responses are scoped by both map_id and round. Instead of creating duplicate responses for the same round, the system updates the most recent response if it exists, or initializes a new one otherwise.
This design avoids redundant data while enabling clean support for quiz retakes and iterative peer review cycles.
Emphasis on RESTful and Stateless Design
All APIs were designed following RESTful principles, with clear resource-based endpoints and appropriate use of HTTP methods. Authentication is handled using JWT tokens, allowing the backend to remain stateless and scalable.
This approach ensures compatibility with modern frontend frameworks such as React and simplifies horizontal scaling by eliminating the need for server-side session storage.
Test Coverage
Controller / Integration Specs
| File | Key Scenarios Tested |
|---|---|
spec/requests/api/v1/student_tasks_controller_spec.rb |
Queue order (quiz before review), quiz-only, review-only, empty queue, start_task blocking out-of-order attempts, unauthorized map access
|
spec/requests/api/v1/responses_controller_spec.rb |
Response create/update blocked when prior task incomplete, allowed when prerequisites complete, authorization checks |
Inner Class Unit Specs
| File | Key Scenarios Tested |
|---|---|
spec/controllers/student_tasks_controller_task_items_spec.rb |
QuizTaskItem: reuses existing map, creates map when questionnaire exists, returns nil when questionnaire absent; ReviewTaskItem: returns given map, completion based on submitted response; shared to_h payload shape contract
|
Response Codes Tested
| Endpoint Group | Codes |
|---|---|
| Student Tasks (list, view, queue, next_task, start_task) | 200, 401, 403, 404, 500 |
| Responses (POST, GET, PATCH) | 201, 200, 401, 403, 404 |
Running the Tests
bundle exec rspec \ spec/requests/api/v1/student_tasks_controller_spec.rb \ spec/requests/api/v1/responses_controller_spec.rb \ spec/controllers/student_tasks_controller_task_items_spec.rb
Expected result: all examples passing, 0 failures
Demo Video
Demo Video: https://youtu.be/Zg-fQmIUCSc
The demo will walk through:
- The task sequencing logic enforcing quiz-before-review ordering for quiz tasks
- Live API calls to the student tasks endpoints showing queue state and next task resolution
- Response creation flow including JWT authentication, map ownership verification, and task order enforcement
- Full RSpec test suite run showing all examples passing
Future Work
- Extend
build_tasksto support deadline-aware ordering — tasks past their due date could be automatically skipped or flagged - Add per-question completion tracking for quiz responses, rather than treating a response as fully submitted or not
- Expose a participant-level quiz completion percentage endpoint for frontend progress indicators
- Extend
QuizTaskItemand add new inner task classes to support additional response map types as new assignment workflow stages are added to Expertiza - Add admin endpoints to inspect or manually override a participant's queue state for debugging and support purposes
References
- Expertiza GitHub Repository
- Ruby on Rails Guides
- RSpec Documentation
- Rswag GitHub Repository
- JWT Ruby Gem
Team
Members:
- Akhil Kumar
- Dev Patel
- Arnav Merjeri
Mentor:
- Vihar Manojkumar Shah
Last updated: April 2026 | CSC 517, Spring 2026, NCSU