CSC/ECE 517 Fall 2025 - E2552. ProjectTopic and SignedUpTeam

From Expertiza_Wiki
Revision as of 17:54, 31 October 2025 by Dananth (talk | contribs) (→‎References)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search


Introduction

Background

Expertiza utilizes a system for instructors to define topics within assignments, allowing students (often in teams) to sign up for these topics. The previous implementation, centered around the `SignUpTopic` model and associated controllers (`SignUpTopicsController`, `SignedUpTeamsController`), lacked clarity in naming and had business logic distributed across controllers rather than encapsulated within models. Furthermore, the user interface for managing assignment topics by instructors and for students signing up for topics required modernization and improved functionality.

Motivation

This project aimed to improve the maintainability, clarity, and functionality of the topic management and signup features within Expertiza. Key motivations included:

  • Renaming core components (`SignUpTopic` -> `ProjectTopic`) to better reflect their purpose.
  • Created RESTful API endpoints to manage topics and team signups.
  • Creating a more intuitive and feature-rich interface for instructors managing assignment topics.
  • Developing a dedicated and user-friendly interface for students to view, select, and manage their topic signups.
  • Introducing features like bookmarking topics and streamlining the student signup/drop process.
  • Updating dependencies to keep the application current.

Tasks Accomplished

  • Renamed `SignUpTopic` to `ProjectTopic` throughout the backend (models, controllers, routes, tests, database).
  • Refactored `ProjectTopic` and `SignedUpTeam` models to encapsulate business logic (topic creation/update/deletion, team signup/waitlisting, slot management, student signup/drop).
  • Removed `SignUpTopicsController` and implemented `ProjectTopicsController` using the refactored model logic.
  • Refactored `SignedUpTeamsController` to utilize new model methods and added new actions (`drop_topic`, `drop_team_from_topic`).
  • Added `allow_bookmarks` attribute to the `Assignment` model and associated backend/frontend logic.
  • Created a new frontend page (`AssignmentEditPage.tsx`) with a tabbed interface for instructors to manage assignments, including a dedicated "Topics" tab.
  • Implemented modals for creating, editing, importing, and deleting topics on the frontend.
  • Developed a reusable `TopicsTable.tsx` component for displaying topics in instructor and student views.
  • Created a new frontend page (`StudentTasks.tsx`) for students to view assignment topics, bookmark (if enabled), and sign up/drop topics.
  • Updated backend dependencies (Rails, Puma, Nokogiri, etc.) and Docker configuration.
  • Added/updated RSpec tests for `ProjectTopic` and `SignedUpTeam` models and updated relevant existing tests.
  • Updated Swagger API documentation.

Files Modified and Created

  • app/models/project_topic.rb
  • app/models/signed_up_team.rb
  • app/controllers/api/v1/project_topics_controller.rb
  • app/controllers/api/v1/signed_up_teams_controller.rb
  • app/serializers/project_topic_serializer.rb
  • app/serializers/signed_up_team_serializer.rb
  • spec/models/project_topic_spec.rb
  • spec/models/signed_up_team_spec.rb
  • spec/requests/project_topics_spec.rb
  • spec/requests/signed_up_teams_spec.rb

Demo

Demo Video

Click to watch on YouTube

Problem Analysis

Issues with the Previous Implementation are as follows

Naming Convention

The name `SignUpTopic` was ambiguous. Renaming to `ProjectTopic` clarifies that these are topics related to the core project/assignment work students sign up for.

Distributed Business Logic

Logic for creating topics, signing up teams, managing waitlists, and checking slots was spread across `SignUpTopicsController` and `SignedUpTeamsController`. This violated the principle of "fat model, skinny controller", making the code harder to understand, test, and maintain.

Inefficient Data Fetching

Frontend components often needed to make multiple API calls or perform calculations (like available slots) that could be handled more efficiently on the backend. The previous `SignUpTopic` API didn't provide computed data like assigned/waitlisted teams directly.

Lack of Dedicated Student View

Students lacked a clear, dedicated interface to view available topics for an assignment and manage their selection. The signup process was not streamlined.

Inconsistent Assignment Editing

The existing `AssignmentEditor` modal combined general settings with links to other management pages (like topics, participants) in an ad-hoc manner. A more structured approach was needed.

Implementation

Architecture Decisions

  • Model-Centric Logic: Shifted primary business logic for topic management and team signups into the `ProjectTopic` and `SignedUpTeam` models, respectively. Controllers now primarily handle request/response flow and delegate actions to the models.
  • Dedicated Edit Page (Frontend): Created a new full page (`AssignmentEditPage`) for editing assignments instead of using a modal, allowing for a more complex, tabbed interface.
  • Reusable Table Component (Frontend): Developed `TopicsTable` to handle the display and core interactions for topics in different contexts (instructor vs. student), promoting DRY principles.
  • API Enhancements: Modified the `ProjectTopic` index endpoint (`/api/v1/project_topics`) to accept `assignment_id` and return topics with computed data (`to_json_with_computed_data`), reducing frontend calculation needs. Added specific endpoints for student signup (`sign_up_student`) and dropping topics (`drop_topic`, `drop_team_from_topic`).

Core Functionality Implemented

Feature Backend Changes Frontend Changes
Topic Renaming Renamed `SignUpTopic` model, controller, routes, DB table to `ProjectTopic`. Updated associations. Updated API endpoints used.
Topic Management (Instructor) `ProjectTopicsController` CRUD actions delegating to `ProjectTopic` model methods (`create_topic_with_assignment`, `update_topic`, `delete_by_assignment_and_topic_ids`). API returns computed data. New `AssignmentEditPage` with `TopicsTab`. Uses `TopicsTable` (instructor mode). Modals for create, edit, import, delete. API calls for CRUD.
Team Signup Logic `ProjectTopic` methods (`sign_team_up`, `drop_team`, `promote_waitlisted_team`). `SignedUpTeam` methods (`create_signed_up_team`, `remove_team_signups`). Instructor view shows assigned/waitlisted teams in `TopicsTable` details. Drop team button calls backend.
Student Signup/Drop `SignedUpTeamsController` actions (`sign_up_student`, `drop_topic`) delegating to `SignedUpTeam` model methods (`sign_up_student_for_topic`, `drop_topic_for_student`). Automatic handling of switching topics. New `StudentTasks` page. Uses `TopicsTable` (student mode). Select/deselect buttons trigger API calls. Optimistic UI updates for slots. Displays current selection.
Topic Bookmarking Added `allow_bookmarks` boolean to `Assignment`. Added `project_topic_params` permit `allow_bookmarks` in `AssignmentsController`. `AssignmentEditPage` allows instructors to toggle `allow_bookmarks` (persisted via PATCH). `StudentTasks` shows bookmark toggle if `allow_bookmarks` is true (local state for now).
Assignment Editing UI N/A New `AssignmentEditPage` with tabbed structure. `AssignmentEditor` might be simplified or removed for general settings.
Dependency Updates Updated Ruby, Rails, Puma, Nokogiri etc. Added required gems. N/A

Key Backend Improvements

ProjectTopic Model

# Handles team signup, considering available slots and waitlisting
# Renamed from signup_team for clarity
def sign_team_up(team)
  return false if signed_up_teams.exists?(team: team)
  ActiveRecord::Base.transaction do
    signed_up_team = signed_up_teams.create!(
      team: team,
      is_waitlisted: !slot_available?
    )
    # Remove team from other waitlists if confirmed here
    remove_from_waitlist(team) unless signed_up_team.is_waitlisted?
    true
  end
rescue ActiveRecord::RecordInvalid
  false
end

# Handles dropping a team and promoting waitlisted if necessary
def drop_team(team)
  signed_up_team = signed_up_teams.find_by(team: team)
  return unless signed_up_team
  team_confirmed = !signed_up_team.is_waitlisted?
  signed_up_team.destroy!
  promote_waitlisted_team if team_confirmed # Promote only if a confirmed slot opened
end

# Calculates available slots
def available_slots
  max_choosers - confirmed_teams_count
end

# Checks if slots are positive
def slot_available?
  available_slots.positive?
end

# Business logic for creation, moved from controller
def self.create_topic_with_assignment(topic_params, assignment_id, micropayment = nil)
  # ... (find assignment, build topic, save, return hash { success:, message:, topic:/errors: })
end

# Business logic for update, moved from controller
def update_topic(topic_params)
  # ... (update attributes, return hash { success:, message:, topic:/errors: })
end

# Returns enhanced JSON including computed fields
def to_json_with_computed_data
  as_json.merge(
    available_slots: available_slots,
    confirmed_teams: confirmed_teams.map { |team| # ... format team data },
    waitlisted_teams: waitlisted_teams.map { |team| # ... format team data }
  )
end

private

# Helper for promotion logic
def promote_waitlisted_team
  # ... (find earliest waitlisted team, update is_waitlisted to false, remove from other waitlists)
end

# Helper to remove team from other topics' waitlists
def remove_from_waitlist(team)
  team.signed_up_teams.waitlisted.where.not(project_topic_id: id).destroy_all
end

SignedUpTeam Model

# Scope for confirmed signups
scope :confirmed, -> { where(is_waitlisted: false) }
# Scope for waitlisted signups
scope :waitlisted, -> { where(is_waitlisted: true) }

belongs_to :project_topic # Updated association
# ... validations ...

# Business logic for student signup, including dropping previous topic
def self.sign_up_student_for_topic(user_id, topic_id)
  team_id = get_team_participants(user_id) # Find student's team
  return { success: false, message: "User is not part of any team" } unless team_id

  # Drop any existing topic signups for this team FIRST
  drop_existing_signups_for_team(team_id)

  # Sign up for the new topic using create_signed_up_team (which calls topic.sign_team_up)
  signed_up_team = create_signed_up_team(topic_id, team_id)

  if signed_up_team
    { success: true, message: "Signed up team successful!", signed_up_team: signed_up_team, available_slots: signed_up_team.project_topic.available_slots }
  else
    { success: false, message: "Failed to sign up for topic. Topic may be full or already signed up." }
  end
end

# Business logic for dropping a topic
def self.drop_topic_for_student(user_id, topic_id)
  team_id = get_team_participants(user_id)
  # ... (find topic, team, signed_up_team record) ...
  return { success: false, message: "Team is not signed up for this topic" } unless signed_up_team

  # Delegate drop logic to ProjectTopic
  project_topic.drop_team(team)

  { success: true, message: "Successfully dropped topic!", available_slots: project_topic.available_slots }
end

# Helper to find and drop existing signups before signing up for a new one
private
def self.drop_existing_signups_for_team(team_id)
  existing_signups = where(team_id: team_id)
  existing_signups.each do |signup|
    signup.project_topic.drop_team(signup.team) # Use ProjectTopic's drop method
  end
end

Frontend Improvements

Topic signup page for students

Topics_signup_page_for_students

Admin page to CRUD Assignment Topics

Caption

Database Migrations

  • Renamed `sign_up_topics` table to `project_topics`.
  • Updated `signed_up_teams` table: renamed `sign_up_topic_id` foreign key to `project_topic_id` and updated index name.
  • Added `allow_bookmarks` boolean column to `assignments` table.
  • Renamed `teams_users` join table to `teams_participants`.

Testing

RSpec Tests (Backend)

  • ProjectTopic Model (`project_topic_spec.rb`): Added extensive tests covering:
   * `sign_team_up`: Confirmed vs. waitlisted based on `max_choosers`, removal from other waitlists.
   * `drop_team`: Team removal, promotion of earliest waitlisted team.
   * `available_slots`, `slot_available?`: Correct calculations before/after signups/drops.
   * `confirmed_teams`, `waitlisted_teams`: Correct retrieval and ordering.
   * Validations: Presence of `topic_name`, non-negative `max_choosers` (including zero).
  • SignedUpTeam Model (`signed_up_team_spec.rb`): Added tests covering:
   * Validations: Presence of topic/team, uniqueness scope.
   * Scopes: `:confirmed`, `:waitlisted`.
   * Class methods: Signup logic delegation, removal logic, participant/topic finding.
   * Waitlisting behavior when topic is full.
  • Updates: Modified existing tests (e.g., `due_date_spec.rb`) to reference `ProjectTopic`.

Manual Tests (UI)

  • Instructor Flow: Verified assignment editing via `/assignments/edit/:id`, topic creation/editing/deletion/import via modals, toggling `allow_bookmarks`, dropping teams from topics.
  • Student Flow: Verified topic viewing via `/student_tasks/:assignmentId`, selecting/deselecting topics (including automatic drop of previous), bookmarking (when enabled), correct display of slots and waitlists, disabled selection for full topics.

Impact Analysis

Summary of Changes

  • Backend: Major refactoring of topic-related models and controllers, database schema changes, new API endpoints, updated dependencies. Approximately [Count Lines] lines added/modified across [Count Files] files (primarily models, controllers, specs, migrations).
  • Frontend: New assignment editing page with tabbed UI, new student task page for topic signup, reusable `TopicsTable` component, new routes. Approximately [Count Lines] lines added/modified across [Count Files] files (primarily in `src/pages/Assignments`, `src/pages/StudentTasks`, `src/components`).

Achievements

  • ✓ Successfully refactored `SignUpTopic` to `ProjectTopic` for improved clarity.
  • ✓ Centralized business logic in models, resulting in cleaner controllers.
  • ✓ Implemented a modern, tabbed interface for assignment editing.
  • ✓ Created a dedicated, streamlined UI for student topic signup.
  • ✓ Added topic bookmarking functionality.
  • ✓ Enhanced API endpoints for better data provision to the frontend.
  • ✓ Updated key dependencies (Rails 8).
  • ✓ Added comprehensive RSpec tests for new model logic.

Future Work

  • Frontend Bookmark Persistence: Implement API calls to save/load student topic bookmarks instead of using local state.
  • Partner Ad Feature: Fully implement the "Apply to partner ad" functionality hinted at in `TopicsTab`.
  • Complete Assignment Tabs: Implement the remaining tabs in `AssignmentEditPage` (Rubrics, Review Strategy, Due Dates, Etc.).
  • Refine General Tab/AssignmentEditor: Decide whether general assignment settings remain in `AssignmentEditor` modal or move entirely to a "General" tab within `AssignmentEditPage`.
  • Error Handling (Frontend): Enhance user feedback for API errors during topic operations (create/edit/delete/signup/drop).
  • Authorization Checks: Ensure robust authorization checks are in place for all new/modified backend endpoints.

Conclusion

This project successfully refactored the core topic management and signup system in Expertiza, replacing `SignUpTopic` with `ProjectTopic` and significantly improving the underlying logic by moving it into the models. New frontend interfaces provide a much-improved experience for both instructors managing topics and students signing up for them. The addition of features like topic bookmarking and a streamlined student signup flow enhances usability. The changes establish a more maintainable and understandable codebase for future development.

References

  1. Expertiza on GitHub
  2. The live Expertiza website
  3. Expertiza project documentation wiki
  4. Ruby on Rails Guides
  5. React Documentation
  6. Rspec Documentation