CSC/ECE 517 Fall 2023 - E2360. View for Results of Bidding (Phase2)

From Expertiza_Wiki
Jump to navigation Jump to search

Motivation

The bidding view is needed because we don’t have a way of finding out whether the bidding algorithm is running correctly. This project is to add a bidding view to show how the topics are assigned to those teams who bid.


Work Plan

Based on the project's objectives and feedback received from our mentor, we have identified specific areas for improvement:

1) Refactor variable names within methods such as "bidding_details" and variables like "Topic name" and "Bidding teams" to enhance clarity and specificity.

2) Adjust capitalization for certain components, ensuring that only the first word is capitalized for consistency.

3) Introduce calculations to determine percentages based on additional data, such as the total number of teams and the number of teams for each priority, within the "lottery_controller."

4) Conduct thorough testing of the modified controller and view to ensure optimal functionality.



To add functionality for showing the percentages of the number of teams that got their #1, #2, and #3 choices. We can follow these high-level steps:

Lottery_Controller:

i) Compute the Total Number of Teams: Before we can compute percentages, we need to know the total number of teams that participated in the bidding process. This will be our denominator in the percentage calculation.

ii) Compute the Number of Teams for Each Priority: From our `bidding_details` method, we already have counts for how many teams bid on each topic as their #1, #2, and #3 choice (`@count1`, `@count2`, `@count3`). However, these counts are currently organized by topic. We would need to modify this to count how many teams got their #1, #2, and #3 choices as their assigned topic.

iii) Compute Percentages: Once we have the number of teams that got their #1, #2, and #3 choices, we can compute the percentages by dividing these numbers by the total number of teams and multiplying by 100.

Bidding_detail view:

i) Update the View and Display Percentages: Above the table in our previous view, we can add a section to display these percentages.

ii) Format the Display: We will use a bold heading for each percentage type and format the number to a fixed number of decimal places.

iii) Ensure Responsiveness: Make sure the display of these percentages adjusts well to different screen sizes, maintaining readability and layout consistency.

Code Effort

Change in app/controllers/lottery_controller.rb

method 1: bidding_table_for_topics

This method generates a bidding table for each topic within an assignment. It performs several operations to gather and organize data related to topics, bids, and teams. Here's a breakdown of what the code does:

1. Fetch Assignment:

  • It retrieves the assignment with the specified params[:id] from the database and stores it in the @assignment instance variable.

2. Fetch Topics:

  • It retrieves all sign-up topics associated with the assignment and stores them in the @topics instance variable.

3. Fetch Bids and Assigned Teams:

For each topic in @topics, it:

  • Retrieves all bids associated with that topic and stores them in a hash, where the key is the topic's ID and the value is an array of bids with their associated teams and priorities.
  • Retrieves teams that are not waitlisted for the topic and stores them in a hash, again using the topic's ID as the key and an array of teams as the value.

4. Priority Count Calculation:

  • It dynamically computes and updates the counts for each bid priority level (1, 2, and 3) for each topic. These counts are stored in instance variables named @count1, @count2, and @count3. These instance variables are hashes where the keys are topic IDs, and the values are counts for each priority level.

5. Computes Total Teams:

  • It determines the total number of teams across all topics in the assignment by counting distinct team IDs among all non-waitlisted teams for the specified topics.

6. Computes Priority Counts and Percentages:

  • It calls two helper methods, compute_priority_counts and compute_percentages, to compute the total counts of teams at each priority level and the corresponding percentages. The results are stored in the @priority_counts and @percentages instance variables, respectively.
# Prepares data for displaying the bidding details for each topic within an assignment.
  # It computes the number of bids for each priority (1, 2, 3) per topic and also computes
  # the overall percentages of teams that received their first, second, and third choice.
  def bidding_table_for_topics
    @assignment = Assignment.find(params[:id])
    # Fetch all topics for the assignment
    @topics = @assignment.sign_up_topics
    # Fetch all bids for these topics
    @bids_by_topic = {}
    @assigned_teams_by_topic = {} # This will store the assigned teams for each topic
    @topics.each do |topic|
      # Assuming bids are stored with a topic_id, and each bid has a team associated with it
      @bids_by_topic[topic.id] = Bid.where(topic_id: topic.id).map { |bid| { team: bid.team, priority: bid.priority } }
      # Fetch teams that are not waitlisted for this topic
      @assigned_teams_by_topic[topic.id] = SignedUpTeam.where(topic_id: topic.id, is_waitlisted: false).map(&:team)      
      # Dynamically initializing and updating @count1, @count2, and @count3
      (1..3).each do |priority|
        instance_variable_set("@count#{priority}", Hash.new(0)) unless instance_variable_defined?("@count#{priority}")
        instance_variable_get("@count#{priority}")[topic.id] = @bids_by_topic[topic.id].count { |bid| bid[:priority] == priority }
      end
    end
    # Computes the total number of teams and percentages after fetching bid details
    topic_ids = SignUpTopic.where(assignment_id: @assignment.id).pluck(:id)
    @total_teams = SignedUpTeam.where(topic_id: topic_ids).distinct.count(:team_id)
    @priority_counts = compute_priority_counts(@assigned_teams_by_topic, @bids_by_topic)
    @percentages = compute_percentages(@priority_counts, @total_teams)


  end

method 2: compute_priority_counts

1. Parameters

  • assigned_teams_by_topic (Hash): A hash mapping each topic to an array of assigned teams.
  • bids_by_topic (Hash): A hash mapping each topic to an array of bid objects, where each bid object is a hash containing team and priority information.

2. Functionality

  • Initial Setup: Initializes a hash priority_counts with keys 1, 2, and 3, each having an initial value of 0. These keys represent the priority levels, and their values count the number of teams assigned to each level.
  • Iterating Over Topics and Teams: Iterates over each topic and its associated teams found in assigned_teams_by_topic. For each team in a topic, the method searches bids_by_topic[topic_id] to find a bid made by the team.
  • Updating Priority Counts: If a bid is found, the method increments the count in priority_counts corresponding to the priority level in the bid. This process effectively tallies the number of teams assigned to each priority level.

3. Returns

  • priority_counts (Hash): A hash with keys 1, 2, and 3, where each key's value represents the count of teams assigned to that respective priority level.
# Computes the count of assigned teams for each priority level (1, 2, 3) across all topics.
  # It checks each team associated with a topic and determines if the team's bid matches
  # one of the priority levels, incrementing the respective count if so.
  def compute_priority_counts(assigned_teams_by_topic, bids_by_topic)
    priority_counts = { 1 => 0, 2 => 0, 3 => 0 }
    assigned_teams_by_topic.each do |topic_id, teams|
      teams.each do |team|
        bid_info = bids_by_topic[topic_id].find { |bid| bid[:team] == team }
        priority_counts[bid_info[:priority]] += 1 if bid_info
      end
    end
    priority_counts
  end

method 3: compute_percentages

  • This method computes the percentages of teams that received their first, second, and third choice based on the counts of teams at each priority level.
# Computes the percentages of teams that received their first, second, and third choice
  # based on the counts of teams at each priority level.
  def compute_percentages(priority_counts, total_teams)
    priority_counts.transform_values { |count| (count.to_f / total_teams * 100).round(2) }
  end


Change in app/views/lottery/bidding_table_for_topics.html.erb


Components:

1. Percentage Display Section

  • This section is styled with a border, padding, and a background color to make it visually distinct.
  • It contains a subtitle (h4) describing the content: "Percentage of Teams Getting Their Choices."
  • A list (ul) displays the percentages of teams that received their 1st, 2nd, and 3rd choice. These percentages are pulled dynamically from @percentages array.
  • The colors of the percentage values vary based on the choice priority (green for #1, orange for #2, and red for #3).

Custom CSS Styles

2. Bidding Details Table

  • A table with headers for Topic ID, Topic Name, Bidding Teams, and bids.
  • The table body (<tbody>) dynamically generates rows for each topic using a loop (<% @topics.each do |topic| %>).
  • For each topic, the table displays its name, ID, bidding teams, and the number of bids for each choice.
  • Teams are marked with the .assigned-team class if they are assigned to the topic.
  • Bid counts are displayed, with a placeholder ("–") if the count is zero.
<table class="table table-bordered table-hover">
  <thead>
    <tr>
      <th>Topic id</th>
      <th>Topic name</th>
      <th>Bidding teams</th>
      <th>#1 bids</th>
      <th>#2 bids</th>
      <th>#3 bids</th>
    </tr>
  </thead>
  <tbody>
    <% @topics.each do |topic| %>
      <tr>
        <td><%= topic.id %></td>
        <td><%= topic.topic_name %></td>
        <td class="teams-column">
          <% @bids_by_topic[topic.id].each do |bid_info| %>
            <% team = bid_info[:team] %>
            <% assigned_team = @assigned_teams_by_topic[topic.id].include?(team) %>
            <span class="<%= assigned_team ? 'assigned-team' : '' %>">
              <%= team.name %> (#<%= bid_info[:priority] %>)
            </span>
          <% end %>
        </td>
        <td class="bids-column">
          <% if @count1[topic.id] != 0%>
            <%= @count1[topic.id] %>
          <%else%>
            –
          <%end%>
        </td>
        <td class="bids-column">
          <% if @count2[topic.id] != 0%>
            <%= @count2[topic.id] %>
          <%else%>
            –
          <%end%>
        </td>
        <td class="bids-column">
          <% if @count3[topic.id] != 0%>
            <%= @count3[topic.id] %>
          <%else%>
            –
          <%end%>
        </td>
      </tr>
    <% end %>
  </tbody>
</table>

Testing

1. Test for Populating Bids and Assigned Teams:

  • This test ensures that the bidding_table_for_each_topic method correctly populates bids and assigned teams for each topic.
  • It checks if @bids_by_topic is populated correctly, verifying the length of bids for specific topics.
  • It tests @assigned_teams_by_topic, ensuring it handles cases where topics have no teams.

    it 'populates bids and assigned teams for each topic, handling topics with no teams' do
        controller.bidding_table_for_each_topic
        # Check if @bids_by_topic is populated correctly
        expect(controller.instance_variable_get(:@bids_by_topic)[topic1.id].length).to eq(1)
        expect(controller.instance_variable_get(:@bids_by_topic)[topic2.id].length).to eq(1)
        # Check if @assigned_teams_by_topic is populated correctly, allowing for no teams
        assigned_teams_topic1 = controller.instance_variable_get(:@assigned_teams_by_topic)[topic1.id]
        if assigned_teams_topic1
            expect(assigned_teams_topic1.length).to(satisfy { |value| (value == 0) || (value == 1) })
        else
            expect(assigned_teams_topic1).to be_nil
        end
        # Check the counts of bids for each priority level
        # Ensure to adjust these based on what is set up in your test data
        expect(controller.instance_variable_get(:@count1)[topic1.id]).to eq(1)
        # assuming there is one bid with priority 1 for topic1
        expect(controller.instance_variable_get(:@count2)[topic1.id]).to eq(0)
        # assuming there are no bids with priority 2 for topic1
        expect(controller.instance_variable_get(:@count3)[topic1.id]).to eq(0)
        # assuming there are no bids with priority 3 for topic1
    end

2. Test for Fetching All Topics:

  • This test verifies that all topics for the assignment are fetched and stored in @topics.
    it 'fetches all topics for the assignment' do
        controller.bidding_table_for_each_topic
        expect(controller.instance_variable_get(:@topics)).to match_array([topic1, topic2, topic3, topic4])
    end

3. Test for Computing Priority Counts:

  • This test checks the calculation of priority counts for each topic.
  • It compares the computed priority counts against an expected set of values.
  • It verifies the priority count is computing the values precisely for topics.
    it 'correctly calculates priority counts for each topic' do
        controller.bidding_table_for_each_topic
  
        # Assuming you have a way to know the expected counts for each priority
        expected_priority_counts = { 1 => 0, 
                                     2 => 0, 
                                     3 => 0 }
  
        priority_counts = controller.instance_variable_get(:@priority_counts)
        expect(priority_counts).to eq(expected_priority_counts)
    end

4. Test for Computing Percentages for Teams Getting Their Choices:

  • This test ensures that the method correctly computes the percentages of teams getting their preferred choices.
  • It handles special cases where the expected percentages are not a number (NaN).
    it 'correctly calculates percentages for teams getting their choices' do
        controller.bidding_table_for_each_topic
  
        # Assuming you have a way to know the expected percentages
        expected_percentages = { 1 => Float::NAN, 
                                 2 => Float::NAN, 
                                 3 => Float::NAN }
  
        percentages = controller.instance_variable_get(:@percentages)
        expect(percentages[1].nan?).to be true if expected_percentages[1].nan?
        expect(percentages[2].nan?).to be true if expected_percentages[2].nan?
        expect(percentages[3].nan?).to be true if expected_percentages[3].nan?
    end

Rspec Testing app/views/lottery/bidding_table_for_each_topic.html.erb

Relevant Links

Github Repository: https://github.com/Shreshth-Malik/expertiza

Demo Video: https://www.youtube.com/watch?v=pGmHyqLj7PM

Pull Request: https://github.com/expertiza/expertiza/pull/2659

Testing Credentials

username: instructor6

password: password

Team

Mentor

Edward F. Gehringer (efg@ncsu.edu)

Members

Richard Li (rli14@ncsu.edu)

Shreshth Malik (smalik4@ncsu.edu)

Shuai Chen (schen76@ncsu.edu)