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, three backend systems needed to be built or significantly repaired:
- A task ordering engine that knows the correct sequence of tasks for a participant and can determine whether a student is eligible to take a quiz at any given moment
- 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
Without all three of these systems working correctly together, a student could take a quiz before completing required prerequisite tasks, or be blocked from taking a quiz they were legitimately eligible for.
What We Built
Task Ordering Engine
We built a self-contained TaskOrdering module under app/models/task_ordering/ that encapsulates all logic for determining task eligibility and sequence. This module is intentionally decoupled from controllers — it knows nothing about HTTP requests or responses, only about assignments, participants, and response maps.
The module consists of five classes:
| Class | Role |
|---|---|
BaseTask |
Abstract base defining the shared interface: complete? and map_id
|
ReviewTask |
Concrete task for ReviewResponseMap — complete when is_submitted is true
|
QuizTask |
Concrete task for quiz response maps — the central task type for this project |
TaskFactory |
Factory that instantiates the correct task class given a response map |
TaskQueue |
Orchestrator that builds and queries the ordered task list for a participant |
The TaskQueue is the primary interface that controllers use. Given an assignment and a TeamsParticipant, it can answer three questions:
- Is this map part of the participant's task queue? (
map_in_queue?) - Have all tasks before this one been completed? (
prior_tasks_complete_for?) - What is the next task the student should work on? (
next_incomplete_task)
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 a TaskQueue instance 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
TaskQueuemust confirm that all prerequisite tasks are complete before this response can be created
Technical Deep Dive
How Task Ordering Works
When a student attempts to start a quiz task or create a response, the following sequence occurs:
StudentTasksController or ResponsesController
|
| TaskQueue.new(assignment, teams_participant)
v
TaskQueue
→ fetches all ResponseMaps for this participant
→ calls TaskFactory.build(map) for each
→ builds ordered array of BaseTask subclass instances
|
| queue.prior_tasks_complete_for?(map_id)
v
→ iterates tasks in order
→ for each task before the target, calls task.complete?
→ returns false if any prior task is incomplete
|
v
Controller either proceeds or renders 403/428
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. This was a central bug we fixed (see Bugs We Fixed).
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
The reimplementation of Student Quizzes emphasized modularity, separation of concerns, and extensibility to ensure the system can evolve alongside future Expertiza features.
Decoupling Task Logic from Controllers
A key architectural decision was to encapsulate all task sequencing and eligibility logic within the TaskOrdering module, rather than embedding it directly in controllers. Controllers are responsible only for handling HTTP requests and responses, while TaskOrdering operates purely on domain objects such as assignments, participants, and response maps.
This separation improves testability, as task logic can be validated independently of the API layer, and ensures that future interfaces (e.g., background jobs or alternative frontends) can reuse the same core logic without duplication.
Use of the Factory Pattern
The introduction of TaskFactory allows the system to dynamically instantiate the correct task type based on the underlying response map. This avoids conditional logic scattered throughout the codebase and centralizes object creation in a single location.
By adhering to the factory pattern, the system becomes easier to extend — new task types can be added with minimal modification to existing code, requiring only the addition of a new task class and a corresponding mapping in the factory.
Centralized Task Orchestration via TaskQueue
The TaskQueue class serves as the single source of truth for task ordering and progression. Instead of allowing each controller to independently determine task eligibility, all such decisions are delegated to TaskQueue.
This design ensures consistency across the system: whether a student is viewing their tasks, starting a quiz, or submitting a response, the same ordering rules are enforced. It also reduces duplication and prevents subtle inconsistencies that could arise if multiple components attempted to implement ordering logic independently.
Layered Authorization and Validation
Another important design decision was to enforce validation 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.
Finally, task-order enforcement is delegated to TaskQueue, 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
Model Specs
| File | Key Scenarios Tested |
|---|---|
spec/models/task_ordering/base_task_spec.rb |
Default complete? behavior, map_id delegation, interface contract
|
spec/models/task_ordering/review_task_spec.rb |
Completion based on is_submitted, incomplete when not submitted
|
spec/models/task_ordering/quiz_task_spec.rb |
Quiz-specific completion detection and eligibility |
spec/models/task_ordering/task_factory_spec.rb |
Correct class returned for each map type, error on unknown type |
spec/models/task_ordering/task_queue_spec.rb |
Queue ordering, map_in_queue?, prior_tasks_complete_for?, next_incomplete_task
|
Request Specs
| File | Endpoints | Response Codes Tested |
|---|---|---|
spec/requests/api/v1/student_tasks_controller_spec.rb |
list, view, queue, next_task, start_task | 200, 401, 404, 500 |
spec/requests/api/v1/responses_controller_spec.rb |
POST /responses, GET /responses/:id, PATCH /responses/:id | 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/models/task_ordering/base_task_spec.rb \ spec/models/task_ordering/review_task_spec.rb \ spec/models/task_ordering/quiz_task_spec.rb \ spec/models/task_ordering/task_factory_spec.rb \ spec/models/task_ordering/task_queue_spec.rb
Expected result: 77 examples, 0 failures
Demo Video
Demo Video: https://youtu.be/Zg-fQmIUCSc
The demo will walk through:
- The task ordering system enforcing sequential task completion 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 queue enforcement
- Full RSpec test suite run showing all 77 passing examples
Future Work
- Extend
TaskQueueto 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
- Expand
TaskFactoryto 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: March 2026 | CSC 517, Spring 2026, NCSU