<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.expertiza.ncsu.edu/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Juwujar</id>
	<title>Expertiza_Wiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.expertiza.ncsu.edu/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Juwujar"/>
	<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=Special:Contributions/Juwujar"/>
	<updated>2026-08-08T09:17:41Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.41.0</generator>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2508._Reimplement_bidding-algorithm_web_service&amp;diff=161169</id>
		<title>CSC/ECE 517 Fall 2024 - E2508. Reimplement bidding-algorithm web service</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2508._Reimplement_bidding-algorithm_web_service&amp;diff=161169"/>
		<updated>2025-03-03T23:22:29Z</updated>

		<summary type="html">&lt;p&gt;Juwujar: Created page with &amp;quot;Adding text for now.&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Adding text for now.&lt;/div&gt;</summary>
		<author><name>Juwujar</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025&amp;diff=161168</id>
		<title>CSC/ECE 517 Spring 2025</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025&amp;diff=161168"/>
		<updated>2025-03-03T23:21:59Z</updated>

		<summary type="html">&lt;p&gt;Juwujar: Created page with &amp;quot;* CSC/ECE 517 Fall 2024 - E2508. Reimplement bidding-algorithm web service&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;* [[CSC/ECE 517 Fall 2024 - E2508. Reimplement bidding-algorithm web service]]&lt;/div&gt;</summary>
		<author><name>Juwujar</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=MainPage&amp;diff=161167</id>
		<title>MainPage</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=MainPage&amp;diff=161167"/>
		<updated>2025-03-03T23:20:42Z</updated>

		<summary type="html">&lt;p&gt;Juwujar: /* Expertiza */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Expertiza==&lt;br /&gt;
* [[Expertiza documentation]]&lt;br /&gt;
&lt;br /&gt;
* [[CSC/ECE 517 Summer 2008]]&lt;br /&gt;
* [[CSC/ECE 517 Fall 2010]]&lt;br /&gt;
* [[CSC/ECE 517 Fall 2011]]&lt;br /&gt;
* [[CSC/ECE 517 Fall 2012]]&lt;br /&gt;
* [[CSC/ECE 517 Fall 2013]]&lt;br /&gt;
* [[CSC/ECE 517 Fall 2014]]&lt;br /&gt;
* [[CSC/ECE 517 Fall 2015]]&lt;br /&gt;
* [[CSC/ECE 517 Fall 2016]]&lt;br /&gt;
* [[CSC/ECE 517 Spring 2014]]&lt;br /&gt;
* [[CSC/ECE 517 Spring 2015]]&lt;br /&gt;
* [[CSC/ECE 517 Spring 2016]]&lt;br /&gt;
* [[CSC/ECE 517 Spring 2017]]&lt;br /&gt;
* [[CSC/ECE 517 Fall 2017]]&lt;br /&gt;
* [[CSC/Independent Study Spring 2018]]&lt;br /&gt;
* [[CSC/ECE 517 Spring 2018]]&lt;br /&gt;
* [[CSC/ECE 517 Fall 2018]]&lt;br /&gt;
* [[CSC/ECE 517 Spring 2019]]&lt;br /&gt;
* [[CSC/ECE 517 Fall 2019]]&lt;br /&gt;
* [[CSC/ECE 517 Spring 2020]]&lt;br /&gt;
* [[CSC/ECE 517 Summer 2020]]&lt;br /&gt;
* [[CSC/ECE 517 Fall 2020]]&lt;br /&gt;
* [[CSC/ECE 517 Spring 2021]]&lt;br /&gt;
* [[CSC/ECE 517 Fall 2021]]&lt;br /&gt;
* [[CSC/ECE 517 Spring 2022]]&lt;br /&gt;
* [[CSC/ECE 517 Fall 2022]]&lt;br /&gt;
* [[CSC/ECE 517 Spring 2023]]&lt;br /&gt;
* [[CSC/ECE 517 Fall 2023]]&lt;br /&gt;
* [[CSC/ECE 517 Spring 2024]]&lt;br /&gt;
* [[CSC/ECE 517 Fall 2024]]&lt;br /&gt;
* [[CSC/ECE 517 Spring 2025]]&lt;br /&gt;
* [[CSC 630 Summer 2024]]&lt;br /&gt;
* [[CSC 456 Spring 2011|CSC 456 Spring 2012]]&lt;br /&gt;
* [[ECE 633]]&lt;br /&gt;
* [[KCU]]&lt;br /&gt;
* [[Progress reports]]&lt;br /&gt;
* [[ReactJs Frontend]]&lt;br /&gt;
* [[Front-End/Back-End]]&lt;br /&gt;
&lt;br /&gt;
==Application Behavior==&lt;br /&gt;
* [[Grading]]&lt;br /&gt;
&lt;br /&gt;
==Metaprogramming==&lt;br /&gt;
* [[CSC/ECE_517_Spring_2013/ch1b_1k_hf|Lecture on Metaprogramming]]&lt;br /&gt;
&lt;br /&gt;
==Development==&lt;br /&gt;
&lt;br /&gt;
''Expertiza now has a Java dependency, so the machine you are using to develop Expertiza on should have the JVM installed.''&lt;br /&gt;
&lt;br /&gt;
* [[Setting Up a Development Machine]]&lt;br /&gt;
* [[Creating a Linux Development Environment for Expertiza - Installation Guide]]&lt;br /&gt;
* [[Using git and github for projects]]&lt;br /&gt;
* [[Using heroku to deploy your projects]]&lt;br /&gt;
* [[How to Begin a Project from the Current Expertiza Repository]]&lt;br /&gt;
* [[Git]]&lt;br /&gt;
* [[How to Change a User's Password on a Development Machine]]&lt;br /&gt;
* [[Debugging Rails]]&lt;br /&gt;
* [http://rajanalwan.com/ui_guidelines/ Design Template]&lt;br /&gt;
&lt;br /&gt;
==Production==&lt;br /&gt;
* [[Deploying to Production]]&lt;br /&gt;
* [[Downloading Production Data]]&lt;br /&gt;
* [[Accessing the Production Server]]&lt;br /&gt;
&lt;br /&gt;
==Testing==&lt;br /&gt;
* [[Using Cucumber with Expertiza]]&lt;br /&gt;
* [[Rails Testing Overview]]&lt;br /&gt;
* [[Expertiza Continuous Integration]]&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
* [[Object-Oriented Design and Programming]]&lt;/div&gt;</summary>
		<author><name>Juwujar</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=MainPage&amp;diff=161166</id>
		<title>MainPage</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=MainPage&amp;diff=161166"/>
		<updated>2025-03-03T23:18:17Z</updated>

		<summary type="html">&lt;p&gt;Juwujar: /* Expertiza */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Expertiza==&lt;br /&gt;
* [[Expertiza documentation]]&lt;br /&gt;
&lt;br /&gt;
* [[CSC/ECE 517 Summer 2008]]&lt;br /&gt;
* [[CSC/ECE 517 Fall 2010]]&lt;br /&gt;
* [[CSC/ECE 517 Fall 2011]]&lt;br /&gt;
* [[CSC/ECE 517 Fall 2012]]&lt;br /&gt;
* [[CSC/ECE 517 Fall 2013]]&lt;br /&gt;
* [[CSC/ECE 517 Fall 2014]]&lt;br /&gt;
* [[CSC/ECE 517 Fall 2015]]&lt;br /&gt;
* [[CSC/ECE 517 Fall 2016]]&lt;br /&gt;
* [[CSC/ECE 517 Spring 2014]]&lt;br /&gt;
* [[CSC/ECE 517 Spring 2015]]&lt;br /&gt;
* [[CSC/ECE 517 Spring 2016]]&lt;br /&gt;
* [[CSC/ECE 517 Spring 2017]]&lt;br /&gt;
* [[CSC/ECE 517 Fall 2017]]&lt;br /&gt;
* [[CSC/Independent Study Spring 2018]]&lt;br /&gt;
* [[CSC/ECE 517 Spring 2018]]&lt;br /&gt;
* [[CSC/ECE 517 Fall 2018]]&lt;br /&gt;
* [[CSC/ECE 517 Spring 2019]]&lt;br /&gt;
* [[CSC/ECE 517 Fall 2019]]&lt;br /&gt;
* [[CSC/ECE 517 Spring 2020]]&lt;br /&gt;
* [[CSC/ECE 517 Summer 2020]]&lt;br /&gt;
* [[CSC/ECE 517 Fall 2020]]&lt;br /&gt;
* [[CSC/ECE 517 Spring 2021]]&lt;br /&gt;
* [[CSC/ECE 517 Fall 2021]]&lt;br /&gt;
* [[CSC/ECE 517 Spring 2022]]&lt;br /&gt;
* [[CSC/ECE 517 Fall 2022]]&lt;br /&gt;
* [[CSC/ECE 517 Spring 2023]]&lt;br /&gt;
* [[CSC/ECE 517 Fall 2023]]&lt;br /&gt;
* [[CSC/ECE 517 Spring 2024]]&lt;br /&gt;
* [[CSC/ECE 517 Fall 2024]]&lt;br /&gt;
* [[CSC 630 Summer 2024]]&lt;br /&gt;
* [[CSC/ECE 517 Spring 2025]]&lt;br /&gt;
* [[CSC 456 Spring 2011|CSC 456 Spring 2012]]&lt;br /&gt;
* [[ECE 633]]&lt;br /&gt;
* [[KCU]]&lt;br /&gt;
* [[Progress reports]]&lt;br /&gt;
* [[ReactJs Frontend]]&lt;br /&gt;
* [[Front-End/Back-End]]&lt;br /&gt;
&lt;br /&gt;
==Application Behavior==&lt;br /&gt;
* [[Grading]]&lt;br /&gt;
&lt;br /&gt;
==Metaprogramming==&lt;br /&gt;
* [[CSC/ECE_517_Spring_2013/ch1b_1k_hf|Lecture on Metaprogramming]]&lt;br /&gt;
&lt;br /&gt;
==Development==&lt;br /&gt;
&lt;br /&gt;
''Expertiza now has a Java dependency, so the machine you are using to develop Expertiza on should have the JVM installed.''&lt;br /&gt;
&lt;br /&gt;
* [[Setting Up a Development Machine]]&lt;br /&gt;
* [[Creating a Linux Development Environment for Expertiza - Installation Guide]]&lt;br /&gt;
* [[Using git and github for projects]]&lt;br /&gt;
* [[Using heroku to deploy your projects]]&lt;br /&gt;
* [[How to Begin a Project from the Current Expertiza Repository]]&lt;br /&gt;
* [[Git]]&lt;br /&gt;
* [[How to Change a User's Password on a Development Machine]]&lt;br /&gt;
* [[Debugging Rails]]&lt;br /&gt;
* [http://rajanalwan.com/ui_guidelines/ Design Template]&lt;br /&gt;
&lt;br /&gt;
==Production==&lt;br /&gt;
* [[Deploying to Production]]&lt;br /&gt;
* [[Downloading Production Data]]&lt;br /&gt;
* [[Accessing the Production Server]]&lt;br /&gt;
&lt;br /&gt;
==Testing==&lt;br /&gt;
* [[Using Cucumber with Expertiza]]&lt;br /&gt;
* [[Rails Testing Overview]]&lt;br /&gt;
* [[Expertiza Continuous Integration]]&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
* [[Object-Oriented Design and Programming]]&lt;/div&gt;</summary>
		<author><name>Juwujar</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=161057</id>
		<title>CSC/ECE 517 Fall 2024 - E2485. Allow reviewers to bid on what to review</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=161057"/>
		<updated>2024-12-12T06:27:20Z</updated>

		<summary type="html">&lt;p&gt;Juwujar: /* Project Overview */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
This project will fix the defects and improve the functionality in Expertiza of the Reviewer Bidding feature. It will make the functionality smoother, more reliable, and align with Expertiza's collaborative learning objectives. &lt;br /&gt;
&lt;br /&gt;
The Review Bidding feature lets reviewers focus on topics they know best or care about most. If someone feels qualified or interested in a topic, they can bid to review submissions in that area. This helps ensure reviews are insightful and enjoyable for the reviewer while benefiting the other participants and the overall process.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iii.-problem-statements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Problem Statements ==&lt;br /&gt;
&lt;br /&gt;
Assigning reviews to users on Expertiza involves a complex bidding algorithm that requires optimization before production deployment. Below are key areas we plan to address as per the project requirements:&lt;br /&gt;
* Code Refactoring and Best Practices&lt;br /&gt;
** '''Utilize Authentication Utilities''': Modify the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; function in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to use existing authentication utilities, enhancing code reusability and adhering to the Single Responsibility Principle (SRP) and Separation of Concerns (SoC).&lt;br /&gt;
** '''Eliminate Code Duplication''': Refactor &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; to comply with the Don't Repeat Yourself (DRY) principle.&lt;br /&gt;
** '''Rename Functions for Clarity''': Change &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; to better reflect its purpose.&lt;br /&gt;
** '''Refactor Methods in '''&amp;lt;code&amp;gt;review_bid.rb&amp;lt;/code&amp;gt;''':''': Convert class methods to instance methods or relocate them to helper classes for improved code organization.&lt;br /&gt;
* Functionality Enhancements&lt;br /&gt;
** '''Handle Missing Bids Gracefully''': Adjust the bidding algorithm to accommodate students who do not bid, allowing them to select from the remaining options.&lt;br /&gt;
** '''Support Both Individual and Team Assignments''': Ensure the code functions correctly for participant and team-based assignments when signing up for topics.&lt;br /&gt;
* User Interface Improvements&lt;br /&gt;
** '''Increase Transparency in the Bidding Process''': Display messages indicating how many students are eligible to bid, how many have submitted bids, and the deadline.&lt;br /&gt;
** '''Accurate Bid Status Indicators''': Ensure topic colors change correctly based on the number of outstanding bids, providing clear visual feedback to users.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Exhaustive Edge Case Testing''': Conduct thorough testing for edge cases not previously covered, adding additional test cases as needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iv.-design-goals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Design Goals ==&lt;br /&gt;
&lt;br /&gt;
While addressing the existing defects and enhancing the Reviewer Bidding feature, we plan to adhere to the following design guidelines:&lt;br /&gt;
&lt;br /&gt;
* Code Quality and Maintainability&lt;br /&gt;
** '''Improve Code Structure: '''Implement refactoring strategies to enhance code readability and maintainability, ensuring adherence to best practices like the DRY principle.&lt;br /&gt;
** '''Standardize Authentication Logic: '''Utilize a centralized authentication utility to promote consistency and reusability across the application.&lt;br /&gt;
* System Stability and Flexibility&lt;br /&gt;
** '''Enhance Algorithm Robustness: '''Modify the bidding algorithm to handle scenarios where students do not bid, ensuring the system remains stable under all conditions.&lt;br /&gt;
** '''Ensure Assignment Versatility: '''Design the system to seamlessly support both individual and team assignments without additional configuration.&lt;br /&gt;
* User Experience Enhancements&lt;br /&gt;
** '''Increase Bidding Transparency: '''Develop user interface elements that clearly display bidding status, deadlines, and participation metrics.&lt;br /&gt;
** '''Implement Dynamic Visual Feedback: '''Use real-time visual cues, such as color changes, to inform users about the bidding status instantly.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Develop Comprehensive Test Suites: '''Create extensive automated tests covering edge cases and critical functionalities to guarantee reliable system behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;v.-uml-diagram&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== UML Diagram ==&lt;br /&gt;
&lt;br /&gt;
Below is a visual representation of the models associated with the review bids functionality, depicting the data structures and their relationships.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml class diagram2.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi.-implementation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
'''ReviewBid: '''The &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; manages the bidding process, where participants indicate their preferences for reviewing different topics or assignments. This helps distribute review tasks fairly based on participants' interests.&lt;br /&gt;
&lt;br /&gt;
'''Assignment: '''The &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model represents team or individual assignments in Expertiza. It stores all the essential information required to keep the assignment process organized, like managing participants, setting due dates, forming teams, and overseeing the review process.&lt;br /&gt;
&lt;br /&gt;
'''SignUpTopic: '''The &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt; model identifies specific topics or areas within an assignment for which participants can sign up. It manages the availability of these topics, tracks bids, coordinates team sign-ups, and assigns topics based on both bids and participant availability. &lt;br /&gt;
&lt;br /&gt;
'''Participant: '''The &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model is about the individual users who participate in an assignment. This includes students, instructors, teaching assistants, or other relevant roles. This model is responsible for managing key user information, monitoring interactions with the assignment, keeping track of team memberships, and recording things like badges and review grades.&lt;br /&gt;
&lt;br /&gt;
== Implementation ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1.-decoupling-authorization-logic-from-reviewbidscontroller&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Decoupling Authorization Logic from &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| '''&amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; needs to use the authentication utilities'''&lt;br /&gt;
&lt;br /&gt;
The function &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; should use the available authentication utilities to support the Single Responsibility Principle and Separation of Concerns and to support code reusability.&lt;br /&gt;
&lt;br /&gt;
| '''Authentication Consistency:''' Remove authentication logic in the controller and call logic in the authentication utility class instead to ensure consistent use of the same logic across the application, foster reusability, and eliminate DRY.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
==== Authorization Flow Before Refactoring ====&lt;br /&gt;
The sequence diagram depicts the authorization process within the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;. When a user invokes an action, the controller checks if the action is allowed by verifying the user's role through the &amp;lt;code&amp;gt;current_role_name()&amp;lt;/code&amp;gt; method in the &amp;lt;code&amp;gt;ApplicationController&amp;lt;/code&amp;gt;. Depending on the action and the user's role, the controller either authorizes the user to proceed or denies access, ultimately executing the action logic if authorized and responding to the user.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 1.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We simplified the &amp;lt;code&amp;gt;action_allowed?&amp;lt;/code&amp;gt; method by decoupling authorization logic from the controller to use helper methods in the &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt; module. The &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; should not handle authentication, which is a violation of SRP. &lt;br /&gt;
&lt;br /&gt;
Note: We also made similar updates to the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; methods for &amp;lt;code&amp;gt;SignUpController&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;vertical-align:top;&amp;quot; | Original Code !! style=&amp;quot;vertical-align:top;&amp;quot; | Refactored Code&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index'&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator',&lt;br /&gt;
     'Student'].include? current_role_name and&lt;br /&gt;
        ((%w[list].include? action_name) ? are_needed_authorizations_present?(params[:id], &amp;quot;participant&amp;quot;, &amp;quot;reader&amp;quot;, &amp;quot;submitter&amp;quot;, &amp;quot;reviewer&amp;quot;) : true)&lt;br /&gt;
  else&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator'].include? current_role_name&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index', 'list'&lt;br /&gt;
    current_user_has_student_privileges? &amp;amp;&amp;amp; list_authorization_check&lt;br /&gt;
  else&lt;br /&gt;
    current_user_has_ta_privileges?&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; action ===&lt;br /&gt;
Previously, the &amp;lt;code&amp;gt;ReviewBidsController's index&amp;lt;/code&amp;gt; action was overloaded with responsibilities—it handled everything from locating and validating assignments to fetching review mappings and counting completed reviews. This all-in-one approach made the controller action messy. &lt;br /&gt;
&lt;br /&gt;
We have moved the business logic from the controller action and into dedicated service objects. With this change, we introduced two key service objects: &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; focuses on retrieving the participant along with their assignments, while the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; takes on the task of managing review mappings and calculating review-related stats, like how many reviews have been completed. &lt;br /&gt;
&lt;br /&gt;
This separation of responsibilities aligns with the Single Responsibility Principle. The logic for fetching assignments, retrieving review mappings, and counting reviews is now more flexible and reusable throughout the application. For example, we used methods in the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; in both the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; and the &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;, helping to avoid code duplication.The article [https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial Rails Service Objects Tutorial] served as a guide on when and how to use service objects in Rails.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
  return unless current_user_id?(@participant.user_id)&lt;br /&gt;
  @assignment = @participant.assignment&lt;br /&gt;
  @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
  # Finding how many reviews have been completed&lt;br /&gt;
  @num_reviews_completed = 0&lt;br /&gt;
  @review_mappings.each do |map|&lt;br /&gt;
    @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # render view for completing reviews after review bidding has been completed&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @assignment = participant_service.assignment&lt;br /&gt;
  unless @assignment.is_a?(Assignment)&lt;br /&gt;
    flash[:error] = 'Assignment not found.'&lt;br /&gt;
    redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  @review_mappings = review_service.review_mappings&lt;br /&gt;
  @num_reviews_completed = review_service.review_counts[:completed]&lt;br /&gt;
&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Optimize &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Action functionality ===&lt;br /&gt;
In the earlier version of the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, the server only needed a couple of key pieces of information: an array of topic IDs (params[:topic]) and the participant ID (params[:id}). From there, it would figure out the assignment_id by using the first topic as a reference. This is risky because the &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt; associated with the topic may not be a valid &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
With the new implementation, we structured the parameters to include the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt;, an array of topic IDs, and &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;. This change ensures that the server always receives complete and reliable information, preventing issues caused by missing or inconsistent data. It also removes the need for the server to perform unnecessary lookups, as in the case of &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;, allowing the controller to focus on receiving requests and rendering appropriate responses.&lt;br /&gt;
&lt;br /&gt;
While these updates have cleaned up the set_priority action, they still contain a lot of business logic, like validations and priority updates. To make the code even more maintainable, there’s room for further refactoring—perhaps moving some of this logic into a service class or model method to streamline the controller even more.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | Old Code&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    if params[:topic].nil?&lt;br /&gt;
      ReviewBid.where(participant_id: params[:id]).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      participant = AssignmentParticipant.find_by(id: params[:id])&lt;br /&gt;
      assignment_id = SignUpTopic.find(params[:topic].first).assignment.id&lt;br /&gt;
      @bids = ReviewBid.where(participant_id: params[:id])&lt;br /&gt;
      signed_up_topics = ReviewBid.where(participant_id: params[:id]).map(&amp;amp;:signuptopic_id)&lt;br /&gt;
      signed_up_topics -= params[:topic].map(&amp;amp;:to_i)&lt;br /&gt;
      signed_up_topics.each do |topic|&lt;br /&gt;
        ReviewBid.where(signuptopic_id: topic, participant_id: params[:id]).destroy_all&lt;br /&gt;
      end&lt;br /&gt;
      params[:topic].each_with_index do |topic_id, index|&lt;br /&gt;
        bid_existence = ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id])&lt;br /&gt;
        if bid_existence.empty?&lt;br /&gt;
          ReviewBid.create(priority: index + 1, signuptopic_id: topic_id, participant_id: params[:id], assignment_id: assignment_id)&lt;br /&gt;
        else&lt;br /&gt;
          ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id]).update_all(priority: index + 1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to action: 'show', assignment_id: params[:assignment_id], id: params[:id]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    participant_id = params[:participant_id].to_i&lt;br /&gt;
    selected_topic_ids = params[:topic]&amp;amp;.map(&amp;amp;:to_i) || []&lt;br /&gt;
    assignment_id = params[:assignment_id].to_i&lt;br /&gt;
&lt;br /&gt;
    unless current_user_can_modify_participant?(participant_id)&lt;br /&gt;
      flash[:error] = 'You are not authorized to modify these bids.'&lt;br /&gt;
      respond_to do |format|&lt;br /&gt;
        format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
        format.json { render json: { status: 'unauthorized' }, status: :unauthorized }&lt;br /&gt;
      end&lt;br /&gt;
      return&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    if selected_topic_ids.empty?&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      assignment = Assignment.find_by(id: assignment_id)&lt;br /&gt;
      if assignment.nil?&lt;br /&gt;
        flash[:error] = &amp;quot;Invalid assignment.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_assignment' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      unless SignUpTopic.where(id: selected_topic_ids, assignment_id: assignment_id).count == selected_topic_ids.size&lt;br /&gt;
        flash[:error] = &amp;quot;One or more selected topics are invalid.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_topics' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).where.not(signuptopic_id: selected_topic_ids).destroy_all&lt;br /&gt;
&lt;br /&gt;
      selected_topic_ids.each_with_index do |topic_id, index|&lt;br /&gt;
        bid = ReviewBid.find_or_initialize_by(signuptopic_id: topic_id, participant_id: participant_id)&lt;br /&gt;
        bid.priority = index + 1&lt;br /&gt;
        bid.assignment_id = assignment_id&lt;br /&gt;
        bid.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_to action: 'show', assignment_id: assignment_id, id: participant_id, notice: 'Review bids updated successfully.' }&lt;br /&gt;
      format.json { render json: { status: 'success' }, status: :ok }&lt;br /&gt;
    end&lt;br /&gt;
  rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
    flash[:error] = &amp;quot;Failed to update priorities: #{e.message}&amp;quot;&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
      format.json { render json: { status: 'error', message: e.message }, status: :unprocessable_entity }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Client Side Changes to Support &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Changes ====&lt;br /&gt;
We refactored the client-side code to ensure it passed the correct information from the view to the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action. On the client side, we now grab the topicIDs and send them as &amp;lt;code&amp;gt;selected_topic_ids&amp;lt;/code&amp;gt;, allowing the controller to figure out which topics were selected and in what order.  &lt;br /&gt;
&lt;br /&gt;
When working with jQuery, AJAX is awesome because it lets us update the server without reloading the page, making the user experience smooth and responsive. When we pull the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, we can grab it along with other data, like the list of topics, and include it as a single request. This way, we don’t have to rely on the URL to know which participant we’re working with, which makes the code cleaner.&lt;br /&gt;
&lt;br /&gt;
We also added an update handler so that any time the order of the selected topic changes in the view, information is sent to the appropriate controller action. This ensures that the controller and the view remain in sync, with real-time updates, and includes a mechanism for providing feedback if something goes wrong.&lt;br /&gt;
&lt;br /&gt;
Note: One interesting aspect of the application was the mix of JavaScript and CoffeeScript code. It wasn't clear why both types of code were used.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery -&amp;gt;&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: -&amp;gt;&lt;br /&gt;
      $.post($(this).data('update-url'), $(this).sortable('serialize'))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery(document).ready ($) -&amp;gt;&lt;br /&gt;
  sendUpdatedOrder = -&amp;gt;&lt;br /&gt;
    sortedIDs = $(this).sortable('toArray')&lt;br /&gt;
&lt;br /&gt;
    topicIDs = sortedIDs.map (id) -&amp;gt;&lt;br /&gt;
      id.replace 'topic_', ''&lt;br /&gt;
&lt;br /&gt;
    participant_id = $(this).data('participant-id')&lt;br /&gt;
&lt;br /&gt;
    assignment_id = new URL($(this).data('update-url'), window.location.origin).searchParams.get('assignment_id')&lt;br /&gt;
&lt;br /&gt;
    $.ajax&lt;br /&gt;
      type: 'POST'&lt;br /&gt;
      url: $(this).data('update-url')&lt;br /&gt;
      data:&lt;br /&gt;
        topic: topicIDs&lt;br /&gt;
        participant_id: participant_id&lt;br /&gt;
        assignment_id: assignment_id&lt;br /&gt;
      success: (response) -&amp;gt;&lt;br /&gt;
        console.log 'Selections updated successfully.'&lt;br /&gt;
      error: (xhr, status, error) -&amp;gt;&lt;br /&gt;
        alert 'An error occurred while saving your selections. Please try again.'&lt;br /&gt;
        console.error error&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: sendUpdatedOrder&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-1.-signupsheetcontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; Updates ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; method renders the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; action However, the view is associated with the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # provides variables for reviewing page located at views/review_bids/others_work.html.erb&lt;br /&gt;
  def index&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
    return unless current_user_id?(@participant.user_id)&lt;br /&gt;
&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
    # Finding how many reviews have been completed&lt;br /&gt;
    @num_reviews_completed = 0&lt;br /&gt;
    @review_mappings.each do |map|&lt;br /&gt;
      @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # render view for completing reviews after review bidding has been completed&lt;br /&gt;
    render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
  end  &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-2.-reviewbidsotherswork-view-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;review_bids_others_work.html.erb&amp;lt;/code&amp;gt; View Updates ====&lt;br /&gt;
The view is technically part of the SignUpSheetController, but it's being rendered by the ReviewBidsController, which initially threw us off. After chatting with the professor, we decided not to change this view. He indicated that the plan is to reuse these views for both assignment and review bidding.&lt;br /&gt;
&lt;br /&gt;
We suggest building the assignment bidding and review bidding features separately first to ensure each one works independently. Once that’s done, we can clean things up by combining any shared functionality. This could include creating shared views or even introducing an abstract model to handle both types of bidding more efficiently. It could also mean replacing the ReviewBidsController with something more generic, like a BidsController, to keep things reusable and easier to maintain down the line.&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Rename &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; method ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; should be &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt;&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:''' Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The primary activity here is to refactor the method name &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; and fix the hardcoded URL. However, the match topic bidding algorithm no longer works since Heroku is no longer free, per the professor. The reference link below has detailed information about the web service that we can leverage to gain that understanding.&lt;br /&gt;
&lt;br /&gt;
Reference Link for Webservice Details: [https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Updated Implementation ====&lt;br /&gt;
The original requirement was to rename the run_bidding_algorithm method to assign_reviewers, but after digging into the code, we realized that renaming it didn’t make sense. The method runs the bidding algorithm to match topics, and there’s already a separate method to handle assigning review topics.&lt;br /&gt;
&lt;br /&gt;
However, we improved the logic in the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; method (which calls the private &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt;) by introducing a separate &amp;lt;code&amp;gt;BiddingAlgorithmService&amp;lt;/code&amp;gt; object. This keeps the business logic out of the controller action, which makes things cleaner and more maintainable.&lt;br /&gt;
&lt;br /&gt;
==== review_bids_controller ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def run_bidding_algorithm&lt;br /&gt;
    review_bid = ReviewBid.new&lt;br /&gt;
    bidding_data = review_bid.bidding_data&lt;br /&gt;
    matched_topics = BiddingAlgorithmService.new(bidding_data).run&lt;br /&gt;
    raise ArgumentError, 'Failed to assign reviewers. Please try again later.' unless matched_topics&lt;br /&gt;
&lt;br /&gt;
    matched_topics&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== bidding_algorithm_service ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# frozen_string_literal: true&lt;br /&gt;
&lt;br /&gt;
# The `BiddingAlgorithmService` run the bid assignment algorithm&lt;br /&gt;
# Sends student IDs, topic IDs, student preferences, and timestamps to the web service&lt;br /&gt;
# The web service returns the matched assignments in the JSON response body&lt;br /&gt;
class BiddingAlgorithmService&lt;br /&gt;
  SERVICE_URL = 'http://app-csc517.herokuapp.com/match_topics'.freeze&lt;br /&gt;
&lt;br /&gt;
  # MOCK_DATA is based on the documentation detailing how the webservice behaves.&lt;br /&gt;
  # Reference: https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review#Webservice&lt;br /&gt;
  MOCK_DATA = {&lt;br /&gt;
    36239 =&amp;gt; [3970, 3972, 3975],&lt;br /&gt;
    36240 =&amp;gt; [3973, 3974, 3972],&lt;br /&gt;
    36241 =&amp;gt; [3969, 3971, 3972],&lt;br /&gt;
    36242 =&amp;gt; [3969, 3971, 3973],&lt;br /&gt;
    36243 =&amp;gt; [3969, 3970, 3971]&lt;br /&gt;
  }.freeze&lt;br /&gt;
&lt;br /&gt;
  def initialize(bidding_data)&lt;br /&gt;
    @bidding_data = bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def run&lt;br /&gt;
    return MOCK_DATA if Rails.application.config.use_mock_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
    perform_request&lt;br /&gt;
  rescue RestClient::ExceptionWithResponse, JSON::ParserError&lt;br /&gt;
    false&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def perform_request&lt;br /&gt;
    response = RestClient.post(&lt;br /&gt;
      SERVICE_URL,&lt;br /&gt;
      @bidding_data.to_json,&lt;br /&gt;
      content_type: :json,&lt;br /&gt;
      accept: :json&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    validate_response(response)&lt;br /&gt;
    JSON.parse(response.body)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def validate_response(response)&lt;br /&gt;
    raise 'Invalid response format' unless response.headers[:content_type] &amp;amp;&amp;amp; response.headers[:content_type].include?('application/json')&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Resolve Functionality Issues for Non Bidders ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Currently it doesn't work if some student does not bid. In this case, algorithm needs to be fixed to ignore anyone who didn’t bid, and let them choose from what’s left over.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to do several things within this controller. We will set a default state for non-bidders that we can point to to give them options to select topics to review from the leftovers. We will consolidate &amp;lt;code&amp;gt;@sign_up_topics&amp;lt;/code&amp;gt; to resolve the DRY violation. Lastly, we will remove business logic from the show method and push it to a new helper to conform to the SRP.&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # Additional method&lt;br /&gt;
&lt;br /&gt;
  # Provides variables for review bidding page&lt;br /&gt;
  def show&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id].to_i)&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @sign_up_topics = SignUpTopic.where(assignment_id: @assignment.id, private_to: nil)&lt;br /&gt;
    my_topic = SignedUpTeam.topic_id(@participant.parent_id, @participant.user_id)&lt;br /&gt;
    @sign_up_topics -= SignUpTopic.where(assignment_id: @assignment.id, id: my_topic)&lt;br /&gt;
    @num_participants = AssignmentParticipant.where(parent_id: @assignment.id).count&lt;br /&gt;
    @selected_topics = nil # This is used to list the topics assigned to review (i.e., select == assigned)&lt;br /&gt;
    @bids = ReviewBid.where(participant_id: @participant, assignment_id: @assignment.id)&lt;br /&gt;
    signed_up_topics = []&lt;br /&gt;
    &lt;br /&gt;
    @bids.each do |bid|&lt;br /&gt;
      sign_up_topic = SignUpTopic.find_by(id: bid.signuptopic_id)&lt;br /&gt;
      signed_up_topics &amp;lt;&amp;lt; sign_up_topic if sign_up_topic&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    signed_up_topics &amp;amp;= @sign_up_topics&lt;br /&gt;
    @sign_up_topics -= signed_up_topics&lt;br /&gt;
    @bids = signed_up_topics&lt;br /&gt;
    @num_of_topics = @sign_up_topics.size&lt;br /&gt;
    @assigned_review_maps = []&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: @assignment.id, reviewer_id: @participant.id).each do |review_map|&lt;br /&gt;
      @assigned_review_maps &amp;lt;&amp;lt; review_map&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Explicitly render view since it's in the sign up sheet views&lt;br /&gt;
    render 'sign_up_sheet/review_bids_show'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Additional methods&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-2.-reviewbidshelper-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
 # Assigns bidding topics to reviewers&lt;br /&gt;
  def assign_bidding&lt;br /&gt;
      assignment = validate_assignment(params[:assignment_id])&lt;br /&gt;
&lt;br /&gt;
      reviewers = validate_reviewers(assignment.id)&lt;br /&gt;
      reviewer_ids = reviewers.map(&amp;amp;:id)&lt;br /&gt;
&lt;br /&gt;
      matched_topics = run_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
      if matched_topics.blank?&lt;br /&gt;
        flash[:alert] = 'Topic or assignment is missing'&lt;br /&gt;
        redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      leftover_topics = find_leftover_topics(assignment.id, matched_topics)&lt;br /&gt;
      assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.new.assign_review_topics(matched_topics)&lt;br /&gt;
      assignment.update!(can_choose_topic_to_review: false)&lt;br /&gt;
&lt;br /&gt;
      flash[:notice] = 'Reviewers were successfully assigned to topics.'&lt;br /&gt;
      redirect_back fallback_location: root_path&lt;br /&gt;
    rescue ArgumentError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ArgumentError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: e.message&lt;br /&gt;
    rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::RecordInvalid: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue ActiveRecord::ActiveRecordError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::ActiveRecordError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue StandardError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;StandardError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers. Please try again later.'&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  #service methods here ...&lt;br /&gt;
&lt;br /&gt;
  def find_leftover_topics(assignment_id, matched_topics)&lt;br /&gt;
    all_topic_ids = SignUpTopic.where(assignment_id: assignment_id).pluck(:id)&lt;br /&gt;
    assigned_topic_ids = matched_topics.map { |match| match[:topic_id] }&lt;br /&gt;
&lt;br /&gt;
    # Calculate leftover topics&lt;br /&gt;
    all_topic_ids - assigned_topic_ids&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
    return if leftover_topics.blank?&lt;br /&gt;
&lt;br /&gt;
    # Find non-bidders by excluding already matched reviewers&lt;br /&gt;
    non_bidders = reviewer_ids - matched_topics.keys&lt;br /&gt;
&lt;br /&gt;
    # Assign leftover topics to non-bidders in a round-robin fashion&lt;br /&gt;
    non_bidders.each_with_index do |reviewer_id, index|&lt;br /&gt;
      topic_id = leftover_topics[index % leftover_topics.length]&lt;br /&gt;
      ReviewBid.create(priority: 1, signuptopic_id: topic_id, participant_id: reviewer_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirm Functionality for Single-person Teams ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Make sure your code works on individual assignments, i.e., assignments where participants sign up for topics instead of teams. In this case, a team is created for each participant when they sign up. So the code should work for assignments to which either individuals or teams submit.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to make an update to focus on topic rather than team or have it where teams are updated based on topic id.We will probably utilize sign up topic or a similar object to accomplish this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-5-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bid Status Messaging ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| To make the functionality more intuitive, include a message to say how many students are eligible to submit bids, how many have submitted their bids, when the deadline for submitting bids is.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
The logic for these messages will go into the helper file. It is effectively the same reasoning as our migration of other business/functionality code from the controller to the helper to conform to the SRP. It will go hand in hand with the SignUpTopic portion of the code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-6-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Why are the methods in review_bid.rb class methods? Can we change them to instance methods or move it to helpers?&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-1.-reviewbid-model-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model Updates ====&lt;br /&gt;
Since the current class methods are tied to &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model instances, we will change them to instance methods. There is no need to move them to helpers.&lt;br /&gt;
&lt;br /&gt;
===== Review Bid Model Flow Before Refactoring =====&lt;br /&gt;
This sequence diagram describes how the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; interacts with the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model in assigning review topics to reviewers according to their bids. Once the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; action is initiated by the user, the controller gathers the IDs of reviewers and calls &amp;lt;code&amp;gt;ReviewBid.bidding_data&amp;lt;/code&amp;gt; to gather bidding information. That is sent out to an external bidding algorithm, and the matched topics that come back are used by &amp;lt;code&amp;gt;ReviewBid.assign_review_topics&amp;lt;/code&amp;gt; to create &amp;lt;code&amp;gt;ReviewResponseMap&amp;lt;/code&amp;gt; entries, assigning topics to reviewers for the assignment.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
  belongs_to :assignment, class_name: 'Assignment'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  def self.bidding_data(assignment_id, reviewer_ids)&lt;br /&gt;
    # create basic hash and set basic hash data&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'users' =&amp;gt; {}, 'max_accepted_proposals' =&amp;gt; [] }&lt;br /&gt;
    bidding_data['tid'] = SignUpTopic.where(assignment_id: assignment_id).ids&lt;br /&gt;
    bidding_data['max_accepted_proposals'] = Assignment.where(id: assignment_id).pluck(:num_reviews_allowed).first&lt;br /&gt;
&lt;br /&gt;
    # loop through reviewer_ids to get reviewer specific bidding data&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      bidding_data['users'][reviewer_id] = reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # assigns topics to reviews as matched by the webservice algorithm&lt;br /&gt;
  def self.assign_review_topics(assignment_id, reviewer_ids, matched_topics, _min_num_reviews = 2)&lt;br /&gt;
    # if review response map already created, delete it&lt;br /&gt;
    if ReviewResponseMap.where(reviewed_object_id: assignment_id)&lt;br /&gt;
      ReviewResponseMap.where(reviewed_object_id: assignment_id).destroy_all&lt;br /&gt;
    end&lt;br /&gt;
    # loop through reviewer_ids to assign reviews to each reviewer&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      topics_to_assign = matched_topics[reviewer_id.to_s]&lt;br /&gt;
      topics_to_assign.each do |topic|&lt;br /&gt;
        assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method to assign a single topic to a reviewer&lt;br /&gt;
  def self.assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic).pluck(:team_id).first&lt;br /&gt;
    team_to_review.nil? ? [] : ReviewResponseMap.create(reviewed_object_id: assignment_id, reviewer_id: reviewer_id, reviewee_id: team_to_review, type: 'ReviewResponseMap')&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method for getting individual reviewer_ids bidding data&lt;br /&gt;
  # returns user's bidding data hash&lt;br /&gt;
  def self.reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    reviewer_user_id = AssignmentParticipant.find(reviewer_id).user_id&lt;br /&gt;
    self_topic = SignedUpTeam.topic_id(assignment_id, reviewer_user_id)&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'otid' =&amp;gt; self_topic, 'priority' =&amp;gt; [], 'time' =&amp;gt; [] }&lt;br /&gt;
    bids = ReviewBid.where(participant_id: reviewer_id)&lt;br /&gt;
&lt;br /&gt;
    # loop through each bid for a topic to get specific data&lt;br /&gt;
    bids.each do |bid|&lt;br /&gt;
      bidding_data['tid'] &amp;lt;&amp;lt; bid.signuptopic_id&lt;br /&gt;
      bidding_data['priority'] &amp;lt;&amp;lt; bid.priority&lt;br /&gt;
      bidding_data['time'] &amp;lt;&amp;lt; bid.updated_at&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  # Instance method to get bidding data for this review bid&lt;br /&gt;
  def bidding_data&lt;br /&gt;
    {&lt;br /&gt;
      'tid' =&amp;gt; topic_ids,&lt;br /&gt;
      'otid' =&amp;gt; self_topic,&lt;br /&gt;
      'priority' =&amp;gt; priorities,&lt;br /&gt;
      'time' =&amp;gt; updated_times&lt;br /&gt;
    }&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # bid object to limit db touches for similar data acquisitions&lt;br /&gt;
  def bids&lt;br /&gt;
    @bids ||= ReviewBid.where(participant_id: participant_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def topic_ids&lt;br /&gt;
    bids.pluck(:signuptopic_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def priorities&lt;br /&gt;
    bids.pluck(:priority)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def updated_times&lt;br /&gt;
    bids.pluck(:updated_at)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self_topic&lt;br /&gt;
    return nil unless topic &amp;amp;&amp;amp; topic.assignment_id&lt;br /&gt;
&lt;br /&gt;
    SignedUpTeam.topic_id(topic.assignment_id, participant.user_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign topics to reviews&lt;br /&gt;
  def assign_review_topics(matched_topics)&lt;br /&gt;
    validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: topic.assignment_id).destroy_all&lt;br /&gt;
&lt;br /&gt;
    matched_topics.each do |reviewer_id, topics|&lt;br /&gt;
      Array(topics).each { |topic_id| assign_topic_to_reviewer(reviewer_id, topic_id) }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign a single topic to a reviewer&lt;br /&gt;
  def assign_topic_to_reviewer(reviewer_id, topic_id)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic_id).pluck(:team_id).first&lt;br /&gt;
    return if team_to_review.nil?&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.create(&lt;br /&gt;
      reviewed_object_id: topic.assignment_id,&lt;br /&gt;
      reviewer_id: reviewer_id,&lt;br /&gt;
      reviewee_id: team_to_review,&lt;br /&gt;
      type: 'ReviewResponseMap'&lt;br /&gt;
    )&lt;br /&gt;
    # error handling&lt;br /&gt;
  rescue StandardError =&amp;gt; e&lt;br /&gt;
    Rails.logger.error(&amp;quot;Failed to assign topic #{topic_id} to reviewer #{reviewer_id}: #{e.message}&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
    raise ArgumentError, 'Topic or assignment is missing' if topic.nil? || topic.assignment_id.nil?&lt;br /&gt;
    raise ArgumentError, 'Matched topics must be a Hash' unless matched_topics.is_a?(Hash)&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
Once we changed them to instance methods, we modified their use in the controller code to instantiate the object first.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-3.-schema.db-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;Schema.db&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We also need to remove the foreign key reference to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model since the &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model already has access to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;, and that is the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; we should be referencing. This will also involve modifying the database to remove the foreign key reference to the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Steps required:&lt;br /&gt;
&lt;br /&gt;
# Review the records in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table in the database to see if there are any records and if those records have values for &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;&lt;br /&gt;
# Create a migration file using the command: &amp;lt;code&amp;gt;rails generate migration RemoveAssignmentFromReviewBids&amp;lt;/code&amp;gt;&lt;br /&gt;
# In the generated file, indicate the change that needs to be made using the change method and migration columns &amp;lt;code&amp;gt;remove_foreign_key&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;remove_column&amp;lt;/code&amp;gt;. Migration should be straightforward since there is no cascading behavior (i.e., &amp;lt;code&amp;gt;dependent:destroy&amp;lt;/code&amp;gt;) to worry about.&lt;br /&gt;
# Once comfortable with the updates to the generated file, run the migration using the &amp;lt;code&amp;gt;rails db:migrate&amp;lt;/code&amp;gt; command.&lt;br /&gt;
# Run a query in the DB to ensure no orphaned records are in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table after the updates.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-4.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implement Edge Case Testing ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| In the previous implementation wiki, there are edge cases which are not exhaustively tested. Should test those edge cases thoroughly and add more edge case testing - link&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span class=&amp;quot;mark&amp;quot;&amp;gt;TODO&amp;lt;/span&amp;gt;:We just got initial test results back. We still need to figure out why tests are failing and what may be slipping through the cracks.&lt;br /&gt;
&lt;br /&gt;
[[File:Test expertiza.png|411x459px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-8-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design Patterns and Principles ===&lt;br /&gt;
Below are the design patterns and principles we plan to use to address the problem statements outlined in the project requirements:&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
** Controller Responsibility&lt;br /&gt;
*** To address the issue of authentication logic residing in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, we will refactor the code to ensure the controller solely handles request and response flows.&lt;br /&gt;
*** Authentication logic will be moved to dedicated methods in &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt;, making the codebase easier to maintain and test.&lt;br /&gt;
*** We will move this logic into helper methods or models, ensuring each component has a single responsibility and improving code readability.&lt;br /&gt;
&lt;br /&gt;
* Separation of Concerns (SoC)&lt;br /&gt;
** By relocating authorization logic out of the controller, we separate authentication from request handling.&lt;br /&gt;
** Adhering to SoC enhances readability and maintainability by allowing each part of the application to focus on its specific role.&lt;br /&gt;
&lt;br /&gt;
* Don't Repeat Yourself (DRY)&lt;br /&gt;
** We will inspect the existing implementation for code duplication, particularly in the controller actions.&lt;br /&gt;
** Any repetitive code will be refactored or removed to reduce redundancy and improve clarity.&lt;br /&gt;
&lt;br /&gt;
* Dependency Inversion Principle (DIP)&lt;br /&gt;
** We plan to update class methods to instance methods, allowing the controller to depend on abstractions rather than concrete implementations.&lt;br /&gt;
&lt;br /&gt;
** Clear and intentional naming during refactoring will enhance code clarity and maintainability.&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
The Excel spreadsheet below details our test plan for testing the code we changed or created to ensure code coverage. The process involved reviewing the code and determining all the possible scenarios that needed to be covered based on what the method was doing, then creating test cases accordingly.&lt;br /&gt;
&lt;br /&gt;
* [https://docs.google.com/spreadsheets/d/1iHzOdSwlTtxOtf5VsNdwOaCP8NzYMmiJ/edit?usp=drive_link Open Test Matrix Spreadsheet]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/review_bids_controller_spec.rb ReviewBidsController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/sign_up_sheet_controller_spec.rb SignUpSheetController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/models/review_bid_spec.rb ReviewBid model updated test cases]&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-1.-mentor&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Ed Gehringer&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-2.-members&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Members ===&lt;br /&gt;
&lt;br /&gt;
* Gavin Teague&lt;br /&gt;
* Janice Uwujaren&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xi.-links&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
Pull Request:&lt;br /&gt;
[https://github.com/expertiza/expertiza/pull/2901 Expertiza Pull Request #2901]&lt;br /&gt;
&lt;br /&gt;
Demo Video:&lt;br /&gt;
[https://youtu.be/TjYkx6SSC0Y E2485 Review Bidding Feature Demo Video]&lt;br /&gt;
&lt;br /&gt;
Previous Wiki:  &lt;br /&gt;
[https://wiki.expertiza.ncsu.edu/index.php/CSC/ECE_517_Fall_2021_-_E2151._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;Link&amp;lt;/u&amp;gt;]  &lt;br /&gt;
&lt;br /&gt;
Reference Link for Webservice Details:  &lt;br /&gt;
[https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
Guidance on Using Service Objects in Rails:&lt;br /&gt;
[https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial Rails Service Objects Tutorial]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xii.-references&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/document/d/1LHVacylQmB15I6VcYUA5nuC7GRRU9szHzZhjlPcE-PA/edit?tab=t.0#heading=h.u0oycoj6e0ev &amp;lt;u&amp;gt;Project Instructions&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;appendix-a-uml-class-diagram-plantuml-code-for-review-bids&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Juwujar</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=161056</id>
		<title>CSC/ECE 517 Fall 2024 - E2485. Allow reviewers to bid on what to review</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=161056"/>
		<updated>2024-12-12T06:19:31Z</updated>

		<summary type="html">&lt;p&gt;Juwujar: /* Links */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
This project will fix the defects and improve the functionality in Expertiza of the Reviewer Bidding feature. It will make the functionality smoother, more reliable, and align with Expertiza's collaborative learning objectives. &lt;br /&gt;
&lt;br /&gt;
The Review Bidding feature lets reviewers focus on topics they know best or care about most. If someone feels exceptionally qualified or interested in a topic, they can bid to review submissions in that area. This helps ensure reviews are insightful and enjoyable for the reviewer while benefiting the other participants and the overall process.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iii.-problem-statements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Problem Statements ==&lt;br /&gt;
&lt;br /&gt;
Assigning reviews to users on Expertiza involves a complex bidding algorithm that requires optimization before production deployment. Below are key areas we plan to address as per the project requirements:&lt;br /&gt;
* Code Refactoring and Best Practices&lt;br /&gt;
** '''Utilize Authentication Utilities''': Modify the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; function in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to use existing authentication utilities, enhancing code reusability and adhering to the Single Responsibility Principle (SRP) and Separation of Concerns (SoC).&lt;br /&gt;
** '''Eliminate Code Duplication''': Refactor &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; to comply with the Don't Repeat Yourself (DRY) principle.&lt;br /&gt;
** '''Rename Functions for Clarity''': Change &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; to better reflect its purpose.&lt;br /&gt;
** '''Refactor Methods in '''&amp;lt;code&amp;gt;review_bid.rb&amp;lt;/code&amp;gt;''':''': Convert class methods to instance methods or relocate them to helper classes for improved code organization.&lt;br /&gt;
* Functionality Enhancements&lt;br /&gt;
** '''Handle Missing Bids Gracefully''': Adjust the bidding algorithm to accommodate students who do not bid, allowing them to select from the remaining options.&lt;br /&gt;
** '''Support Both Individual and Team Assignments''': Ensure the code functions correctly for participant and team-based assignments when signing up for topics.&lt;br /&gt;
* User Interface Improvements&lt;br /&gt;
** '''Increase Transparency in the Bidding Process''': Display messages indicating how many students are eligible to bid, how many have submitted bids, and the deadline.&lt;br /&gt;
** '''Accurate Bid Status Indicators''': Ensure topic colors change correctly based on the number of outstanding bids, providing clear visual feedback to users.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Exhaustive Edge Case Testing''': Conduct thorough testing for edge cases not previously covered, adding additional test cases as needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iv.-design-goals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Design Goals ==&lt;br /&gt;
&lt;br /&gt;
While addressing the existing defects and enhancing the Reviewer Bidding feature, we plan to adhere to the following design guidelines:&lt;br /&gt;
&lt;br /&gt;
* Code Quality and Maintainability&lt;br /&gt;
** '''Improve Code Structure: '''Implement refactoring strategies to enhance code readability and maintainability, ensuring adherence to best practices like the DRY principle.&lt;br /&gt;
** '''Standardize Authentication Logic: '''Utilize a centralized authentication utility to promote consistency and reusability across the application.&lt;br /&gt;
* System Stability and Flexibility&lt;br /&gt;
** '''Enhance Algorithm Robustness: '''Modify the bidding algorithm to handle scenarios where students do not bid, ensuring the system remains stable under all conditions.&lt;br /&gt;
** '''Ensure Assignment Versatility: '''Design the system to seamlessly support both individual and team assignments without additional configuration.&lt;br /&gt;
* User Experience Enhancements&lt;br /&gt;
** '''Increase Bidding Transparency: '''Develop user interface elements that clearly display bidding status, deadlines, and participation metrics.&lt;br /&gt;
** '''Implement Dynamic Visual Feedback: '''Use real-time visual cues, such as color changes, to inform users about the bidding status instantly.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Develop Comprehensive Test Suites: '''Create extensive automated tests covering edge cases and critical functionalities to guarantee reliable system behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;v.-uml-diagram&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== UML Diagram ==&lt;br /&gt;
&lt;br /&gt;
Below is a visual representation of the models associated with the review bids functionality, depicting the data structures and their relationships.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml class diagram2.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi.-implementation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
'''ReviewBid: '''The &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; manages the bidding process, where participants indicate their preferences for reviewing different topics or assignments. This helps distribute review tasks fairly based on participants' interests.&lt;br /&gt;
&lt;br /&gt;
'''Assignment: '''The &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model represents team or individual assignments in Expertiza. It stores all the essential information required to keep the assignment process organized, like managing participants, setting due dates, forming teams, and overseeing the review process.&lt;br /&gt;
&lt;br /&gt;
'''SignUpTopic: '''The &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt; model identifies specific topics or areas within an assignment for which participants can sign up. It manages the availability of these topics, tracks bids, coordinates team sign-ups, and assigns topics based on both bids and participant availability. &lt;br /&gt;
&lt;br /&gt;
'''Participant: '''The &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model is about the individual users who participate in an assignment. This includes students, instructors, teaching assistants, or other relevant roles. This model is responsible for managing key user information, monitoring interactions with the assignment, keeping track of team memberships, and recording things like badges and review grades.&lt;br /&gt;
&lt;br /&gt;
== Implementation ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1.-decoupling-authorization-logic-from-reviewbidscontroller&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Decoupling Authorization Logic from &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| '''&amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; needs to use the authentication utilities'''&lt;br /&gt;
&lt;br /&gt;
The function &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; should use the available authentication utilities to support the Single Responsibility Principle and Separation of Concerns and to support code reusability.&lt;br /&gt;
&lt;br /&gt;
| '''Authentication Consistency:''' Remove authentication logic in the controller and call logic in the authentication utility class instead to ensure consistent use of the same logic across the application, foster reusability, and eliminate DRY.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
==== Authorization Flow Before Refactoring ====&lt;br /&gt;
The sequence diagram depicts the authorization process within the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;. When a user invokes an action, the controller checks if the action is allowed by verifying the user's role through the &amp;lt;code&amp;gt;current_role_name()&amp;lt;/code&amp;gt; method in the &amp;lt;code&amp;gt;ApplicationController&amp;lt;/code&amp;gt;. Depending on the action and the user's role, the controller either authorizes the user to proceed or denies access, ultimately executing the action logic if authorized and responding to the user.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 1.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We simplified the &amp;lt;code&amp;gt;action_allowed?&amp;lt;/code&amp;gt; method by decoupling authorization logic from the controller to use helper methods in the &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt; module. The &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; should not handle authentication, which is a violation of SRP. &lt;br /&gt;
&lt;br /&gt;
Note: We also made similar updates to the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; methods for &amp;lt;code&amp;gt;SignUpController&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;vertical-align:top;&amp;quot; | Original Code !! style=&amp;quot;vertical-align:top;&amp;quot; | Refactored Code&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index'&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator',&lt;br /&gt;
     'Student'].include? current_role_name and&lt;br /&gt;
        ((%w[list].include? action_name) ? are_needed_authorizations_present?(params[:id], &amp;quot;participant&amp;quot;, &amp;quot;reader&amp;quot;, &amp;quot;submitter&amp;quot;, &amp;quot;reviewer&amp;quot;) : true)&lt;br /&gt;
  else&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator'].include? current_role_name&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index', 'list'&lt;br /&gt;
    current_user_has_student_privileges? &amp;amp;&amp;amp; list_authorization_check&lt;br /&gt;
  else&lt;br /&gt;
    current_user_has_ta_privileges?&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; action ===&lt;br /&gt;
Previously, the &amp;lt;code&amp;gt;ReviewBidsController's index&amp;lt;/code&amp;gt; action was overloaded with responsibilities—it handled everything from locating and validating assignments to fetching review mappings and counting completed reviews. This all-in-one approach made the controller action messy. &lt;br /&gt;
&lt;br /&gt;
We have moved the business logic from the controller action and into dedicated service objects. With this change, we introduced two key service objects: &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; focuses on retrieving the participant along with their assignments, while the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; takes on the task of managing review mappings and calculating review-related stats, like how many reviews have been completed. &lt;br /&gt;
&lt;br /&gt;
This separation of responsibilities aligns with the Single Responsibility Principle. The logic for fetching assignments, retrieving review mappings, and counting reviews is now more flexible and reusable throughout the application. For example, we used methods in the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; in both the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; and the &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;, helping to avoid code duplication.The article [https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial Rails Service Objects Tutorial] served as a guide on when and how to use service objects in Rails.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
  return unless current_user_id?(@participant.user_id)&lt;br /&gt;
  @assignment = @participant.assignment&lt;br /&gt;
  @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
  # Finding how many reviews have been completed&lt;br /&gt;
  @num_reviews_completed = 0&lt;br /&gt;
  @review_mappings.each do |map|&lt;br /&gt;
    @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # render view for completing reviews after review bidding has been completed&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @assignment = participant_service.assignment&lt;br /&gt;
  unless @assignment.is_a?(Assignment)&lt;br /&gt;
    flash[:error] = 'Assignment not found.'&lt;br /&gt;
    redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  @review_mappings = review_service.review_mappings&lt;br /&gt;
  @num_reviews_completed = review_service.review_counts[:completed]&lt;br /&gt;
&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Optimize &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Action functionality ===&lt;br /&gt;
In the earlier version of the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, the server only needed a couple of key pieces of information: an array of topic IDs (params[:topic]) and the participant ID (params[:id}). From there, it would figure out the assignment_id by using the first topic as a reference. This is risky because the &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt; associated with the topic may not be a valid &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
With the new implementation, we structured the parameters to include the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt;, an array of topic IDs, and &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;. This change ensures that the server always receives complete and reliable information, preventing issues caused by missing or inconsistent data. It also removes the need for the server to perform unnecessary lookups, as in the case of &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;, allowing the controller to focus on receiving requests and rendering appropriate responses.&lt;br /&gt;
&lt;br /&gt;
While these updates have cleaned up the set_priority action, they still contain a lot of business logic, like validations and priority updates. To make the code even more maintainable, there’s room for further refactoring—perhaps moving some of this logic into a service class or model method to streamline the controller even more.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | Old Code&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    if params[:topic].nil?&lt;br /&gt;
      ReviewBid.where(participant_id: params[:id]).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      participant = AssignmentParticipant.find_by(id: params[:id])&lt;br /&gt;
      assignment_id = SignUpTopic.find(params[:topic].first).assignment.id&lt;br /&gt;
      @bids = ReviewBid.where(participant_id: params[:id])&lt;br /&gt;
      signed_up_topics = ReviewBid.where(participant_id: params[:id]).map(&amp;amp;:signuptopic_id)&lt;br /&gt;
      signed_up_topics -= params[:topic].map(&amp;amp;:to_i)&lt;br /&gt;
      signed_up_topics.each do |topic|&lt;br /&gt;
        ReviewBid.where(signuptopic_id: topic, participant_id: params[:id]).destroy_all&lt;br /&gt;
      end&lt;br /&gt;
      params[:topic].each_with_index do |topic_id, index|&lt;br /&gt;
        bid_existence = ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id])&lt;br /&gt;
        if bid_existence.empty?&lt;br /&gt;
          ReviewBid.create(priority: index + 1, signuptopic_id: topic_id, participant_id: params[:id], assignment_id: assignment_id)&lt;br /&gt;
        else&lt;br /&gt;
          ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id]).update_all(priority: index + 1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to action: 'show', assignment_id: params[:assignment_id], id: params[:id]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    participant_id = params[:participant_id].to_i&lt;br /&gt;
    selected_topic_ids = params[:topic]&amp;amp;.map(&amp;amp;:to_i) || []&lt;br /&gt;
    assignment_id = params[:assignment_id].to_i&lt;br /&gt;
&lt;br /&gt;
    unless current_user_can_modify_participant?(participant_id)&lt;br /&gt;
      flash[:error] = 'You are not authorized to modify these bids.'&lt;br /&gt;
      respond_to do |format|&lt;br /&gt;
        format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
        format.json { render json: { status: 'unauthorized' }, status: :unauthorized }&lt;br /&gt;
      end&lt;br /&gt;
      return&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    if selected_topic_ids.empty?&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      assignment = Assignment.find_by(id: assignment_id)&lt;br /&gt;
      if assignment.nil?&lt;br /&gt;
        flash[:error] = &amp;quot;Invalid assignment.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_assignment' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      unless SignUpTopic.where(id: selected_topic_ids, assignment_id: assignment_id).count == selected_topic_ids.size&lt;br /&gt;
        flash[:error] = &amp;quot;One or more selected topics are invalid.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_topics' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).where.not(signuptopic_id: selected_topic_ids).destroy_all&lt;br /&gt;
&lt;br /&gt;
      selected_topic_ids.each_with_index do |topic_id, index|&lt;br /&gt;
        bid = ReviewBid.find_or_initialize_by(signuptopic_id: topic_id, participant_id: participant_id)&lt;br /&gt;
        bid.priority = index + 1&lt;br /&gt;
        bid.assignment_id = assignment_id&lt;br /&gt;
        bid.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_to action: 'show', assignment_id: assignment_id, id: participant_id, notice: 'Review bids updated successfully.' }&lt;br /&gt;
      format.json { render json: { status: 'success' }, status: :ok }&lt;br /&gt;
    end&lt;br /&gt;
  rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
    flash[:error] = &amp;quot;Failed to update priorities: #{e.message}&amp;quot;&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
      format.json { render json: { status: 'error', message: e.message }, status: :unprocessable_entity }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Client Side Changes to Support &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Changes ====&lt;br /&gt;
We refactored the client-side code to ensure it passed the correct information from the view to the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action. On the client side, we now grab the topicIDs and send them as &amp;lt;code&amp;gt;selected_topic_ids&amp;lt;/code&amp;gt;, allowing the controller to figure out which topics were selected and in what order.  &lt;br /&gt;
&lt;br /&gt;
When working with jQuery, AJAX is awesome because it lets us update the server without reloading the page, making the user experience smooth and responsive. When we pull the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, we can grab it along with other data, like the list of topics, and include it as a single request. This way, we don’t have to rely on the URL to know which participant we’re working with, which makes the code cleaner.&lt;br /&gt;
&lt;br /&gt;
We also added an update handler so that any time the order of the selected topic changes in the view, information is sent to the appropriate controller action. This ensures that the controller and the view remain in sync, with real-time updates, and includes a mechanism for providing feedback if something goes wrong.&lt;br /&gt;
&lt;br /&gt;
Note: One interesting aspect of the application was the mix of JavaScript and CoffeeScript code. It wasn't clear why both types of code were used.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery -&amp;gt;&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: -&amp;gt;&lt;br /&gt;
      $.post($(this).data('update-url'), $(this).sortable('serialize'))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery(document).ready ($) -&amp;gt;&lt;br /&gt;
  sendUpdatedOrder = -&amp;gt;&lt;br /&gt;
    sortedIDs = $(this).sortable('toArray')&lt;br /&gt;
&lt;br /&gt;
    topicIDs = sortedIDs.map (id) -&amp;gt;&lt;br /&gt;
      id.replace 'topic_', ''&lt;br /&gt;
&lt;br /&gt;
    participant_id = $(this).data('participant-id')&lt;br /&gt;
&lt;br /&gt;
    assignment_id = new URL($(this).data('update-url'), window.location.origin).searchParams.get('assignment_id')&lt;br /&gt;
&lt;br /&gt;
    $.ajax&lt;br /&gt;
      type: 'POST'&lt;br /&gt;
      url: $(this).data('update-url')&lt;br /&gt;
      data:&lt;br /&gt;
        topic: topicIDs&lt;br /&gt;
        participant_id: participant_id&lt;br /&gt;
        assignment_id: assignment_id&lt;br /&gt;
      success: (response) -&amp;gt;&lt;br /&gt;
        console.log 'Selections updated successfully.'&lt;br /&gt;
      error: (xhr, status, error) -&amp;gt;&lt;br /&gt;
        alert 'An error occurred while saving your selections. Please try again.'&lt;br /&gt;
        console.error error&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: sendUpdatedOrder&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-1.-signupsheetcontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; Updates ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; method renders the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; action However, the view is associated with the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # provides variables for reviewing page located at views/review_bids/others_work.html.erb&lt;br /&gt;
  def index&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
    return unless current_user_id?(@participant.user_id)&lt;br /&gt;
&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
    # Finding how many reviews have been completed&lt;br /&gt;
    @num_reviews_completed = 0&lt;br /&gt;
    @review_mappings.each do |map|&lt;br /&gt;
      @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # render view for completing reviews after review bidding has been completed&lt;br /&gt;
    render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
  end  &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-2.-reviewbidsotherswork-view-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;review_bids_others_work.html.erb&amp;lt;/code&amp;gt; View Updates ====&lt;br /&gt;
The view is technically part of the SignUpSheetController, but it's being rendered by the ReviewBidsController, which initially threw us off. After chatting with the professor, we decided not to change this view. He indicated that the plan is to reuse these views for both assignment and review bidding.&lt;br /&gt;
&lt;br /&gt;
We suggest building the assignment bidding and review bidding features separately first to ensure each one works independently. Once that’s done, we can clean things up by combining any shared functionality. This could include creating shared views or even introducing an abstract model to handle both types of bidding more efficiently. It could also mean replacing the ReviewBidsController with something more generic, like a BidsController, to keep things reusable and easier to maintain down the line.&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Rename &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; method ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; should be &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt;&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:''' Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The primary activity here is to refactor the method name &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; and fix the hardcoded URL. However, the match topic bidding algorithm no longer works since Heroku is no longer free, per the professor. The reference link below has detailed information about the web service that we can leverage to gain that understanding.&lt;br /&gt;
&lt;br /&gt;
Reference Link for Webservice Details: [https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Updated Implementation ====&lt;br /&gt;
The original requirement was to rename the run_bidding_algorithm method to assign_reviewers, but after digging into the code, we realized that renaming it didn’t make sense. The method runs the bidding algorithm to match topics, and there’s already a separate method to handle assigning review topics.&lt;br /&gt;
&lt;br /&gt;
However, we improved the logic in the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; method (which calls the private &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt;) by introducing a separate &amp;lt;code&amp;gt;BiddingAlgorithmService&amp;lt;/code&amp;gt; object. This keeps the business logic out of the controller action, which makes things cleaner and more maintainable.&lt;br /&gt;
&lt;br /&gt;
==== review_bids_controller ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def run_bidding_algorithm&lt;br /&gt;
    review_bid = ReviewBid.new&lt;br /&gt;
    bidding_data = review_bid.bidding_data&lt;br /&gt;
    matched_topics = BiddingAlgorithmService.new(bidding_data).run&lt;br /&gt;
    raise ArgumentError, 'Failed to assign reviewers. Please try again later.' unless matched_topics&lt;br /&gt;
&lt;br /&gt;
    matched_topics&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== bidding_algorithm_service ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# frozen_string_literal: true&lt;br /&gt;
&lt;br /&gt;
# The `BiddingAlgorithmService` run the bid assignment algorithm&lt;br /&gt;
# Sends student IDs, topic IDs, student preferences, and timestamps to the web service&lt;br /&gt;
# The web service returns the matched assignments in the JSON response body&lt;br /&gt;
class BiddingAlgorithmService&lt;br /&gt;
  SERVICE_URL = 'http://app-csc517.herokuapp.com/match_topics'.freeze&lt;br /&gt;
&lt;br /&gt;
  # MOCK_DATA is based on the documentation detailing how the webservice behaves.&lt;br /&gt;
  # Reference: https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review#Webservice&lt;br /&gt;
  MOCK_DATA = {&lt;br /&gt;
    36239 =&amp;gt; [3970, 3972, 3975],&lt;br /&gt;
    36240 =&amp;gt; [3973, 3974, 3972],&lt;br /&gt;
    36241 =&amp;gt; [3969, 3971, 3972],&lt;br /&gt;
    36242 =&amp;gt; [3969, 3971, 3973],&lt;br /&gt;
    36243 =&amp;gt; [3969, 3970, 3971]&lt;br /&gt;
  }.freeze&lt;br /&gt;
&lt;br /&gt;
  def initialize(bidding_data)&lt;br /&gt;
    @bidding_data = bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def run&lt;br /&gt;
    return MOCK_DATA if Rails.application.config.use_mock_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
    perform_request&lt;br /&gt;
  rescue RestClient::ExceptionWithResponse, JSON::ParserError&lt;br /&gt;
    false&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def perform_request&lt;br /&gt;
    response = RestClient.post(&lt;br /&gt;
      SERVICE_URL,&lt;br /&gt;
      @bidding_data.to_json,&lt;br /&gt;
      content_type: :json,&lt;br /&gt;
      accept: :json&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    validate_response(response)&lt;br /&gt;
    JSON.parse(response.body)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def validate_response(response)&lt;br /&gt;
    raise 'Invalid response format' unless response.headers[:content_type] &amp;amp;&amp;amp; response.headers[:content_type].include?('application/json')&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Resolve Functionality Issues for Non Bidders ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Currently it doesn't work if some student does not bid. In this case, algorithm needs to be fixed to ignore anyone who didn’t bid, and let them choose from what’s left over.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to do several things within this controller. We will set a default state for non-bidders that we can point to to give them options to select topics to review from the leftovers. We will consolidate &amp;lt;code&amp;gt;@sign_up_topics&amp;lt;/code&amp;gt; to resolve the DRY violation. Lastly, we will remove business logic from the show method and push it to a new helper to conform to the SRP.&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # Additional method&lt;br /&gt;
&lt;br /&gt;
  # Provides variables for review bidding page&lt;br /&gt;
  def show&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id].to_i)&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @sign_up_topics = SignUpTopic.where(assignment_id: @assignment.id, private_to: nil)&lt;br /&gt;
    my_topic = SignedUpTeam.topic_id(@participant.parent_id, @participant.user_id)&lt;br /&gt;
    @sign_up_topics -= SignUpTopic.where(assignment_id: @assignment.id, id: my_topic)&lt;br /&gt;
    @num_participants = AssignmentParticipant.where(parent_id: @assignment.id).count&lt;br /&gt;
    @selected_topics = nil # This is used to list the topics assigned to review (i.e., select == assigned)&lt;br /&gt;
    @bids = ReviewBid.where(participant_id: @participant, assignment_id: @assignment.id)&lt;br /&gt;
    signed_up_topics = []&lt;br /&gt;
    &lt;br /&gt;
    @bids.each do |bid|&lt;br /&gt;
      sign_up_topic = SignUpTopic.find_by(id: bid.signuptopic_id)&lt;br /&gt;
      signed_up_topics &amp;lt;&amp;lt; sign_up_topic if sign_up_topic&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    signed_up_topics &amp;amp;= @sign_up_topics&lt;br /&gt;
    @sign_up_topics -= signed_up_topics&lt;br /&gt;
    @bids = signed_up_topics&lt;br /&gt;
    @num_of_topics = @sign_up_topics.size&lt;br /&gt;
    @assigned_review_maps = []&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: @assignment.id, reviewer_id: @participant.id).each do |review_map|&lt;br /&gt;
      @assigned_review_maps &amp;lt;&amp;lt; review_map&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Explicitly render view since it's in the sign up sheet views&lt;br /&gt;
    render 'sign_up_sheet/review_bids_show'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Additional methods&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-2.-reviewbidshelper-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
 # Assigns bidding topics to reviewers&lt;br /&gt;
  def assign_bidding&lt;br /&gt;
      assignment = validate_assignment(params[:assignment_id])&lt;br /&gt;
&lt;br /&gt;
      reviewers = validate_reviewers(assignment.id)&lt;br /&gt;
      reviewer_ids = reviewers.map(&amp;amp;:id)&lt;br /&gt;
&lt;br /&gt;
      matched_topics = run_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
      if matched_topics.blank?&lt;br /&gt;
        flash[:alert] = 'Topic or assignment is missing'&lt;br /&gt;
        redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      leftover_topics = find_leftover_topics(assignment.id, matched_topics)&lt;br /&gt;
      assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.new.assign_review_topics(matched_topics)&lt;br /&gt;
      assignment.update!(can_choose_topic_to_review: false)&lt;br /&gt;
&lt;br /&gt;
      flash[:notice] = 'Reviewers were successfully assigned to topics.'&lt;br /&gt;
      redirect_back fallback_location: root_path&lt;br /&gt;
    rescue ArgumentError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ArgumentError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: e.message&lt;br /&gt;
    rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::RecordInvalid: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue ActiveRecord::ActiveRecordError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::ActiveRecordError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue StandardError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;StandardError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers. Please try again later.'&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  #service methods here ...&lt;br /&gt;
&lt;br /&gt;
  def find_leftover_topics(assignment_id, matched_topics)&lt;br /&gt;
    all_topic_ids = SignUpTopic.where(assignment_id: assignment_id).pluck(:id)&lt;br /&gt;
    assigned_topic_ids = matched_topics.map { |match| match[:topic_id] }&lt;br /&gt;
&lt;br /&gt;
    # Calculate leftover topics&lt;br /&gt;
    all_topic_ids - assigned_topic_ids&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
    return if leftover_topics.blank?&lt;br /&gt;
&lt;br /&gt;
    # Find non-bidders by excluding already matched reviewers&lt;br /&gt;
    non_bidders = reviewer_ids - matched_topics.keys&lt;br /&gt;
&lt;br /&gt;
    # Assign leftover topics to non-bidders in a round-robin fashion&lt;br /&gt;
    non_bidders.each_with_index do |reviewer_id, index|&lt;br /&gt;
      topic_id = leftover_topics[index % leftover_topics.length]&lt;br /&gt;
      ReviewBid.create(priority: 1, signuptopic_id: topic_id, participant_id: reviewer_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirm Functionality for Single-person Teams ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Make sure your code works on individual assignments, i.e., assignments where participants sign up for topics instead of teams. In this case, a team is created for each participant when they sign up. So the code should work for assignments to which either individuals or teams submit.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to make an update to focus on topic rather than team or have it where teams are updated based on topic id.We will probably utilize sign up topic or a similar object to accomplish this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-5-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bid Status Messaging ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| To make the functionality more intuitive, include a message to say how many students are eligible to submit bids, how many have submitted their bids, when the deadline for submitting bids is.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
The logic for these messages will go into the helper file. It is effectively the same reasoning as our migration of other business/functionality code from the controller to the helper to conform to the SRP. It will go hand in hand with the SignUpTopic portion of the code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-6-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Why are the methods in review_bid.rb class methods? Can we change them to instance methods or move it to helpers?&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-1.-reviewbid-model-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model Updates ====&lt;br /&gt;
Since the current class methods are tied to &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model instances, we will change them to instance methods. There is no need to move them to helpers.&lt;br /&gt;
&lt;br /&gt;
===== Review Bid Model Flow Before Refactoring =====&lt;br /&gt;
This sequence diagram describes how the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; interacts with the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model in assigning review topics to reviewers according to their bids. Once the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; action is initiated by the user, the controller gathers the IDs of reviewers and calls &amp;lt;code&amp;gt;ReviewBid.bidding_data&amp;lt;/code&amp;gt; to gather bidding information. That is sent out to an external bidding algorithm, and the matched topics that come back are used by &amp;lt;code&amp;gt;ReviewBid.assign_review_topics&amp;lt;/code&amp;gt; to create &amp;lt;code&amp;gt;ReviewResponseMap&amp;lt;/code&amp;gt; entries, assigning topics to reviewers for the assignment.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
  belongs_to :assignment, class_name: 'Assignment'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  def self.bidding_data(assignment_id, reviewer_ids)&lt;br /&gt;
    # create basic hash and set basic hash data&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'users' =&amp;gt; {}, 'max_accepted_proposals' =&amp;gt; [] }&lt;br /&gt;
    bidding_data['tid'] = SignUpTopic.where(assignment_id: assignment_id).ids&lt;br /&gt;
    bidding_data['max_accepted_proposals'] = Assignment.where(id: assignment_id).pluck(:num_reviews_allowed).first&lt;br /&gt;
&lt;br /&gt;
    # loop through reviewer_ids to get reviewer specific bidding data&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      bidding_data['users'][reviewer_id] = reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # assigns topics to reviews as matched by the webservice algorithm&lt;br /&gt;
  def self.assign_review_topics(assignment_id, reviewer_ids, matched_topics, _min_num_reviews = 2)&lt;br /&gt;
    # if review response map already created, delete it&lt;br /&gt;
    if ReviewResponseMap.where(reviewed_object_id: assignment_id)&lt;br /&gt;
      ReviewResponseMap.where(reviewed_object_id: assignment_id).destroy_all&lt;br /&gt;
    end&lt;br /&gt;
    # loop through reviewer_ids to assign reviews to each reviewer&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      topics_to_assign = matched_topics[reviewer_id.to_s]&lt;br /&gt;
      topics_to_assign.each do |topic|&lt;br /&gt;
        assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method to assign a single topic to a reviewer&lt;br /&gt;
  def self.assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic).pluck(:team_id).first&lt;br /&gt;
    team_to_review.nil? ? [] : ReviewResponseMap.create(reviewed_object_id: assignment_id, reviewer_id: reviewer_id, reviewee_id: team_to_review, type: 'ReviewResponseMap')&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method for getting individual reviewer_ids bidding data&lt;br /&gt;
  # returns user's bidding data hash&lt;br /&gt;
  def self.reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    reviewer_user_id = AssignmentParticipant.find(reviewer_id).user_id&lt;br /&gt;
    self_topic = SignedUpTeam.topic_id(assignment_id, reviewer_user_id)&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'otid' =&amp;gt; self_topic, 'priority' =&amp;gt; [], 'time' =&amp;gt; [] }&lt;br /&gt;
    bids = ReviewBid.where(participant_id: reviewer_id)&lt;br /&gt;
&lt;br /&gt;
    # loop through each bid for a topic to get specific data&lt;br /&gt;
    bids.each do |bid|&lt;br /&gt;
      bidding_data['tid'] &amp;lt;&amp;lt; bid.signuptopic_id&lt;br /&gt;
      bidding_data['priority'] &amp;lt;&amp;lt; bid.priority&lt;br /&gt;
      bidding_data['time'] &amp;lt;&amp;lt; bid.updated_at&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  # Instance method to get bidding data for this review bid&lt;br /&gt;
  def bidding_data&lt;br /&gt;
    {&lt;br /&gt;
      'tid' =&amp;gt; topic_ids,&lt;br /&gt;
      'otid' =&amp;gt; self_topic,&lt;br /&gt;
      'priority' =&amp;gt; priorities,&lt;br /&gt;
      'time' =&amp;gt; updated_times&lt;br /&gt;
    }&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # bid object to limit db touches for similar data acquisitions&lt;br /&gt;
  def bids&lt;br /&gt;
    @bids ||= ReviewBid.where(participant_id: participant_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def topic_ids&lt;br /&gt;
    bids.pluck(:signuptopic_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def priorities&lt;br /&gt;
    bids.pluck(:priority)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def updated_times&lt;br /&gt;
    bids.pluck(:updated_at)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self_topic&lt;br /&gt;
    return nil unless topic &amp;amp;&amp;amp; topic.assignment_id&lt;br /&gt;
&lt;br /&gt;
    SignedUpTeam.topic_id(topic.assignment_id, participant.user_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign topics to reviews&lt;br /&gt;
  def assign_review_topics(matched_topics)&lt;br /&gt;
    validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: topic.assignment_id).destroy_all&lt;br /&gt;
&lt;br /&gt;
    matched_topics.each do |reviewer_id, topics|&lt;br /&gt;
      Array(topics).each { |topic_id| assign_topic_to_reviewer(reviewer_id, topic_id) }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign a single topic to a reviewer&lt;br /&gt;
  def assign_topic_to_reviewer(reviewer_id, topic_id)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic_id).pluck(:team_id).first&lt;br /&gt;
    return if team_to_review.nil?&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.create(&lt;br /&gt;
      reviewed_object_id: topic.assignment_id,&lt;br /&gt;
      reviewer_id: reviewer_id,&lt;br /&gt;
      reviewee_id: team_to_review,&lt;br /&gt;
      type: 'ReviewResponseMap'&lt;br /&gt;
    )&lt;br /&gt;
    # error handling&lt;br /&gt;
  rescue StandardError =&amp;gt; e&lt;br /&gt;
    Rails.logger.error(&amp;quot;Failed to assign topic #{topic_id} to reviewer #{reviewer_id}: #{e.message}&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
    raise ArgumentError, 'Topic or assignment is missing' if topic.nil? || topic.assignment_id.nil?&lt;br /&gt;
    raise ArgumentError, 'Matched topics must be a Hash' unless matched_topics.is_a?(Hash)&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
Once we changed them to instance methods, we modified their use in the controller code to instantiate the object first.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-3.-schema.db-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;Schema.db&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We also need to remove the foreign key reference to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model since the &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model already has access to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;, and that is the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; we should be referencing. This will also involve modifying the database to remove the foreign key reference to the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Steps required:&lt;br /&gt;
&lt;br /&gt;
# Review the records in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table in the database to see if there are any records and if those records have values for &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;&lt;br /&gt;
# Create a migration file using the command: &amp;lt;code&amp;gt;rails generate migration RemoveAssignmentFromReviewBids&amp;lt;/code&amp;gt;&lt;br /&gt;
# In the generated file, indicate the change that needs to be made using the change method and migration columns &amp;lt;code&amp;gt;remove_foreign_key&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;remove_column&amp;lt;/code&amp;gt;. Migration should be straightforward since there is no cascading behavior (i.e., &amp;lt;code&amp;gt;dependent:destroy&amp;lt;/code&amp;gt;) to worry about.&lt;br /&gt;
# Once comfortable with the updates to the generated file, run the migration using the &amp;lt;code&amp;gt;rails db:migrate&amp;lt;/code&amp;gt; command.&lt;br /&gt;
# Run a query in the DB to ensure no orphaned records are in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table after the updates.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-4.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implement Edge Case Testing ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| In the previous implementation wiki, there are edge cases which are not exhaustively tested. Should test those edge cases thoroughly and add more edge case testing - link&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span class=&amp;quot;mark&amp;quot;&amp;gt;TODO&amp;lt;/span&amp;gt;:We just got initial test results back. We still need to figure out why tests are failing and what may be slipping through the cracks.&lt;br /&gt;
&lt;br /&gt;
[[File:Test expertiza.png|411x459px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-8-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design Patterns and Principles ===&lt;br /&gt;
Below are the design patterns and principles we plan to use to address the problem statements outlined in the project requirements:&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
** Controller Responsibility&lt;br /&gt;
*** To address the issue of authentication logic residing in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, we will refactor the code to ensure the controller solely handles request and response flows.&lt;br /&gt;
*** Authentication logic will be moved to dedicated methods in &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt;, making the codebase easier to maintain and test.&lt;br /&gt;
*** We will move this logic into helper methods or models, ensuring each component has a single responsibility and improving code readability.&lt;br /&gt;
&lt;br /&gt;
* Separation of Concerns (SoC)&lt;br /&gt;
** By relocating authorization logic out of the controller, we separate authentication from request handling.&lt;br /&gt;
** Adhering to SoC enhances readability and maintainability by allowing each part of the application to focus on its specific role.&lt;br /&gt;
&lt;br /&gt;
* Don't Repeat Yourself (DRY)&lt;br /&gt;
** We will inspect the existing implementation for code duplication, particularly in the controller actions.&lt;br /&gt;
** Any repetitive code will be refactored or removed to reduce redundancy and improve clarity.&lt;br /&gt;
&lt;br /&gt;
* Dependency Inversion Principle (DIP)&lt;br /&gt;
** We plan to update class methods to instance methods, allowing the controller to depend on abstractions rather than concrete implementations.&lt;br /&gt;
&lt;br /&gt;
** Clear and intentional naming during refactoring will enhance code clarity and maintainability.&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
The Excel spreadsheet below details our test plan for testing the code we changed or created to ensure code coverage. The process involved reviewing the code and determining all the possible scenarios that needed to be covered based on what the method was doing, then creating test cases accordingly.&lt;br /&gt;
&lt;br /&gt;
* [https://docs.google.com/spreadsheets/d/1iHzOdSwlTtxOtf5VsNdwOaCP8NzYMmiJ/edit?usp=drive_link Open Test Matrix Spreadsheet]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/review_bids_controller_spec.rb ReviewBidsController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/sign_up_sheet_controller_spec.rb SignUpSheetController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/models/review_bid_spec.rb ReviewBid model updated test cases]&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-1.-mentor&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Ed Gehringer&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-2.-members&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Members ===&lt;br /&gt;
&lt;br /&gt;
* Gavin Teague&lt;br /&gt;
* Janice Uwujaren&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xi.-links&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
Pull Request:&lt;br /&gt;
[https://github.com/expertiza/expertiza/pull/2901 Expertiza Pull Request #2901]&lt;br /&gt;
&lt;br /&gt;
Demo Video:&lt;br /&gt;
[https://youtu.be/TjYkx6SSC0Y E2485 Review Bidding Feature Demo Video]&lt;br /&gt;
&lt;br /&gt;
Previous Wiki:  &lt;br /&gt;
[https://wiki.expertiza.ncsu.edu/index.php/CSC/ECE_517_Fall_2021_-_E2151._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;Link&amp;lt;/u&amp;gt;]  &lt;br /&gt;
&lt;br /&gt;
Reference Link for Webservice Details:  &lt;br /&gt;
[https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
Guidance on Using Service Objects in Rails:&lt;br /&gt;
[https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial Rails Service Objects Tutorial]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xii.-references&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/document/d/1LHVacylQmB15I6VcYUA5nuC7GRRU9szHzZhjlPcE-PA/edit?tab=t.0#heading=h.u0oycoj6e0ev &amp;lt;u&amp;gt;Project Instructions&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;appendix-a-uml-class-diagram-plantuml-code-for-review-bids&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Juwujar</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=161055</id>
		<title>CSC/ECE 517 Fall 2024 - E2485. Allow reviewers to bid on what to review</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=161055"/>
		<updated>2024-12-12T06:16:19Z</updated>

		<summary type="html">&lt;p&gt;Juwujar: /* Files Modified/Added */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
This project will fix the defects and improve the functionality in Expertiza of the Reviewer Bidding feature. It will make the functionality smoother, more reliable, and align with Expertiza's collaborative learning objectives. &lt;br /&gt;
&lt;br /&gt;
The Review Bidding feature lets reviewers focus on topics they know best or care about most. If someone feels exceptionally qualified or interested in a topic, they can bid to review submissions in that area. This helps ensure reviews are insightful and enjoyable for the reviewer while benefiting the other participants and the overall process.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iii.-problem-statements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Problem Statements ==&lt;br /&gt;
&lt;br /&gt;
Assigning reviews to users on Expertiza involves a complex bidding algorithm that requires optimization before production deployment. Below are key areas we plan to address as per the project requirements:&lt;br /&gt;
* Code Refactoring and Best Practices&lt;br /&gt;
** '''Utilize Authentication Utilities''': Modify the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; function in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to use existing authentication utilities, enhancing code reusability and adhering to the Single Responsibility Principle (SRP) and Separation of Concerns (SoC).&lt;br /&gt;
** '''Eliminate Code Duplication''': Refactor &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; to comply with the Don't Repeat Yourself (DRY) principle.&lt;br /&gt;
** '''Rename Functions for Clarity''': Change &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; to better reflect its purpose.&lt;br /&gt;
** '''Refactor Methods in '''&amp;lt;code&amp;gt;review_bid.rb&amp;lt;/code&amp;gt;''':''': Convert class methods to instance methods or relocate them to helper classes for improved code organization.&lt;br /&gt;
* Functionality Enhancements&lt;br /&gt;
** '''Handle Missing Bids Gracefully''': Adjust the bidding algorithm to accommodate students who do not bid, allowing them to select from the remaining options.&lt;br /&gt;
** '''Support Both Individual and Team Assignments''': Ensure the code functions correctly for participant and team-based assignments when signing up for topics.&lt;br /&gt;
* User Interface Improvements&lt;br /&gt;
** '''Increase Transparency in the Bidding Process''': Display messages indicating how many students are eligible to bid, how many have submitted bids, and the deadline.&lt;br /&gt;
** '''Accurate Bid Status Indicators''': Ensure topic colors change correctly based on the number of outstanding bids, providing clear visual feedback to users.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Exhaustive Edge Case Testing''': Conduct thorough testing for edge cases not previously covered, adding additional test cases as needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iv.-design-goals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Design Goals ==&lt;br /&gt;
&lt;br /&gt;
While addressing the existing defects and enhancing the Reviewer Bidding feature, we plan to adhere to the following design guidelines:&lt;br /&gt;
&lt;br /&gt;
* Code Quality and Maintainability&lt;br /&gt;
** '''Improve Code Structure: '''Implement refactoring strategies to enhance code readability and maintainability, ensuring adherence to best practices like the DRY principle.&lt;br /&gt;
** '''Standardize Authentication Logic: '''Utilize a centralized authentication utility to promote consistency and reusability across the application.&lt;br /&gt;
* System Stability and Flexibility&lt;br /&gt;
** '''Enhance Algorithm Robustness: '''Modify the bidding algorithm to handle scenarios where students do not bid, ensuring the system remains stable under all conditions.&lt;br /&gt;
** '''Ensure Assignment Versatility: '''Design the system to seamlessly support both individual and team assignments without additional configuration.&lt;br /&gt;
* User Experience Enhancements&lt;br /&gt;
** '''Increase Bidding Transparency: '''Develop user interface elements that clearly display bidding status, deadlines, and participation metrics.&lt;br /&gt;
** '''Implement Dynamic Visual Feedback: '''Use real-time visual cues, such as color changes, to inform users about the bidding status instantly.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Develop Comprehensive Test Suites: '''Create extensive automated tests covering edge cases and critical functionalities to guarantee reliable system behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;v.-uml-diagram&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== UML Diagram ==&lt;br /&gt;
&lt;br /&gt;
Below is a visual representation of the models associated with the review bids functionality, depicting the data structures and their relationships.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml class diagram2.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi.-implementation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
'''ReviewBid: '''The &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; manages the bidding process, where participants indicate their preferences for reviewing different topics or assignments. This helps distribute review tasks fairly based on participants' interests.&lt;br /&gt;
&lt;br /&gt;
'''Assignment: '''The &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model represents team or individual assignments in Expertiza. It stores all the essential information required to keep the assignment process organized, like managing participants, setting due dates, forming teams, and overseeing the review process.&lt;br /&gt;
&lt;br /&gt;
'''SignUpTopic: '''The &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt; model identifies specific topics or areas within an assignment for which participants can sign up. It manages the availability of these topics, tracks bids, coordinates team sign-ups, and assigns topics based on both bids and participant availability. &lt;br /&gt;
&lt;br /&gt;
'''Participant: '''The &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model is about the individual users who participate in an assignment. This includes students, instructors, teaching assistants, or other relevant roles. This model is responsible for managing key user information, monitoring interactions with the assignment, keeping track of team memberships, and recording things like badges and review grades.&lt;br /&gt;
&lt;br /&gt;
== Implementation ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1.-decoupling-authorization-logic-from-reviewbidscontroller&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Decoupling Authorization Logic from &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| '''&amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; needs to use the authentication utilities'''&lt;br /&gt;
&lt;br /&gt;
The function &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; should use the available authentication utilities to support the Single Responsibility Principle and Separation of Concerns and to support code reusability.&lt;br /&gt;
&lt;br /&gt;
| '''Authentication Consistency:''' Remove authentication logic in the controller and call logic in the authentication utility class instead to ensure consistent use of the same logic across the application, foster reusability, and eliminate DRY.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
==== Authorization Flow Before Refactoring ====&lt;br /&gt;
The sequence diagram depicts the authorization process within the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;. When a user invokes an action, the controller checks if the action is allowed by verifying the user's role through the &amp;lt;code&amp;gt;current_role_name()&amp;lt;/code&amp;gt; method in the &amp;lt;code&amp;gt;ApplicationController&amp;lt;/code&amp;gt;. Depending on the action and the user's role, the controller either authorizes the user to proceed or denies access, ultimately executing the action logic if authorized and responding to the user.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 1.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We simplified the &amp;lt;code&amp;gt;action_allowed?&amp;lt;/code&amp;gt; method by decoupling authorization logic from the controller to use helper methods in the &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt; module. The &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; should not handle authentication, which is a violation of SRP. &lt;br /&gt;
&lt;br /&gt;
Note: We also made similar updates to the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; methods for &amp;lt;code&amp;gt;SignUpController&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;vertical-align:top;&amp;quot; | Original Code !! style=&amp;quot;vertical-align:top;&amp;quot; | Refactored Code&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index'&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator',&lt;br /&gt;
     'Student'].include? current_role_name and&lt;br /&gt;
        ((%w[list].include? action_name) ? are_needed_authorizations_present?(params[:id], &amp;quot;participant&amp;quot;, &amp;quot;reader&amp;quot;, &amp;quot;submitter&amp;quot;, &amp;quot;reviewer&amp;quot;) : true)&lt;br /&gt;
  else&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator'].include? current_role_name&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index', 'list'&lt;br /&gt;
    current_user_has_student_privileges? &amp;amp;&amp;amp; list_authorization_check&lt;br /&gt;
  else&lt;br /&gt;
    current_user_has_ta_privileges?&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; action ===&lt;br /&gt;
Previously, the &amp;lt;code&amp;gt;ReviewBidsController's index&amp;lt;/code&amp;gt; action was overloaded with responsibilities—it handled everything from locating and validating assignments to fetching review mappings and counting completed reviews. This all-in-one approach made the controller action messy. &lt;br /&gt;
&lt;br /&gt;
We have moved the business logic from the controller action and into dedicated service objects. With this change, we introduced two key service objects: &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; focuses on retrieving the participant along with their assignments, while the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; takes on the task of managing review mappings and calculating review-related stats, like how many reviews have been completed. &lt;br /&gt;
&lt;br /&gt;
This separation of responsibilities aligns with the Single Responsibility Principle. The logic for fetching assignments, retrieving review mappings, and counting reviews is now more flexible and reusable throughout the application. For example, we used methods in the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; in both the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; and the &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;, helping to avoid code duplication.The article [https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial Rails Service Objects Tutorial] served as a guide on when and how to use service objects in Rails.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
  return unless current_user_id?(@participant.user_id)&lt;br /&gt;
  @assignment = @participant.assignment&lt;br /&gt;
  @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
  # Finding how many reviews have been completed&lt;br /&gt;
  @num_reviews_completed = 0&lt;br /&gt;
  @review_mappings.each do |map|&lt;br /&gt;
    @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # render view for completing reviews after review bidding has been completed&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @assignment = participant_service.assignment&lt;br /&gt;
  unless @assignment.is_a?(Assignment)&lt;br /&gt;
    flash[:error] = 'Assignment not found.'&lt;br /&gt;
    redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  @review_mappings = review_service.review_mappings&lt;br /&gt;
  @num_reviews_completed = review_service.review_counts[:completed]&lt;br /&gt;
&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Optimize &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Action functionality ===&lt;br /&gt;
In the earlier version of the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, the server only needed a couple of key pieces of information: an array of topic IDs (params[:topic]) and the participant ID (params[:id}). From there, it would figure out the assignment_id by using the first topic as a reference. This is risky because the &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt; associated with the topic may not be a valid &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
With the new implementation, we structured the parameters to include the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt;, an array of topic IDs, and &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;. This change ensures that the server always receives complete and reliable information, preventing issues caused by missing or inconsistent data. It also removes the need for the server to perform unnecessary lookups, as in the case of &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;, allowing the controller to focus on receiving requests and rendering appropriate responses.&lt;br /&gt;
&lt;br /&gt;
While these updates have cleaned up the set_priority action, they still contain a lot of business logic, like validations and priority updates. To make the code even more maintainable, there’s room for further refactoring—perhaps moving some of this logic into a service class or model method to streamline the controller even more.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | Old Code&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    if params[:topic].nil?&lt;br /&gt;
      ReviewBid.where(participant_id: params[:id]).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      participant = AssignmentParticipant.find_by(id: params[:id])&lt;br /&gt;
      assignment_id = SignUpTopic.find(params[:topic].first).assignment.id&lt;br /&gt;
      @bids = ReviewBid.where(participant_id: params[:id])&lt;br /&gt;
      signed_up_topics = ReviewBid.where(participant_id: params[:id]).map(&amp;amp;:signuptopic_id)&lt;br /&gt;
      signed_up_topics -= params[:topic].map(&amp;amp;:to_i)&lt;br /&gt;
      signed_up_topics.each do |topic|&lt;br /&gt;
        ReviewBid.where(signuptopic_id: topic, participant_id: params[:id]).destroy_all&lt;br /&gt;
      end&lt;br /&gt;
      params[:topic].each_with_index do |topic_id, index|&lt;br /&gt;
        bid_existence = ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id])&lt;br /&gt;
        if bid_existence.empty?&lt;br /&gt;
          ReviewBid.create(priority: index + 1, signuptopic_id: topic_id, participant_id: params[:id], assignment_id: assignment_id)&lt;br /&gt;
        else&lt;br /&gt;
          ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id]).update_all(priority: index + 1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to action: 'show', assignment_id: params[:assignment_id], id: params[:id]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    participant_id = params[:participant_id].to_i&lt;br /&gt;
    selected_topic_ids = params[:topic]&amp;amp;.map(&amp;amp;:to_i) || []&lt;br /&gt;
    assignment_id = params[:assignment_id].to_i&lt;br /&gt;
&lt;br /&gt;
    unless current_user_can_modify_participant?(participant_id)&lt;br /&gt;
      flash[:error] = 'You are not authorized to modify these bids.'&lt;br /&gt;
      respond_to do |format|&lt;br /&gt;
        format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
        format.json { render json: { status: 'unauthorized' }, status: :unauthorized }&lt;br /&gt;
      end&lt;br /&gt;
      return&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    if selected_topic_ids.empty?&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      assignment = Assignment.find_by(id: assignment_id)&lt;br /&gt;
      if assignment.nil?&lt;br /&gt;
        flash[:error] = &amp;quot;Invalid assignment.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_assignment' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      unless SignUpTopic.where(id: selected_topic_ids, assignment_id: assignment_id).count == selected_topic_ids.size&lt;br /&gt;
        flash[:error] = &amp;quot;One or more selected topics are invalid.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_topics' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).where.not(signuptopic_id: selected_topic_ids).destroy_all&lt;br /&gt;
&lt;br /&gt;
      selected_topic_ids.each_with_index do |topic_id, index|&lt;br /&gt;
        bid = ReviewBid.find_or_initialize_by(signuptopic_id: topic_id, participant_id: participant_id)&lt;br /&gt;
        bid.priority = index + 1&lt;br /&gt;
        bid.assignment_id = assignment_id&lt;br /&gt;
        bid.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_to action: 'show', assignment_id: assignment_id, id: participant_id, notice: 'Review bids updated successfully.' }&lt;br /&gt;
      format.json { render json: { status: 'success' }, status: :ok }&lt;br /&gt;
    end&lt;br /&gt;
  rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
    flash[:error] = &amp;quot;Failed to update priorities: #{e.message}&amp;quot;&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
      format.json { render json: { status: 'error', message: e.message }, status: :unprocessable_entity }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Client Side Changes to Support &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Changes ====&lt;br /&gt;
We refactored the client-side code to ensure it passed the correct information from the view to the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action. On the client side, we now grab the topicIDs and send them as &amp;lt;code&amp;gt;selected_topic_ids&amp;lt;/code&amp;gt;, allowing the controller to figure out which topics were selected and in what order.  &lt;br /&gt;
&lt;br /&gt;
When working with jQuery, AJAX is awesome because it lets us update the server without reloading the page, making the user experience smooth and responsive. When we pull the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, we can grab it along with other data, like the list of topics, and include it as a single request. This way, we don’t have to rely on the URL to know which participant we’re working with, which makes the code cleaner.&lt;br /&gt;
&lt;br /&gt;
We also added an update handler so that any time the order of the selected topic changes in the view, information is sent to the appropriate controller action. This ensures that the controller and the view remain in sync, with real-time updates, and includes a mechanism for providing feedback if something goes wrong.&lt;br /&gt;
&lt;br /&gt;
Note: One interesting aspect of the application was the mix of JavaScript and CoffeeScript code. It wasn't clear why both types of code were used.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery -&amp;gt;&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: -&amp;gt;&lt;br /&gt;
      $.post($(this).data('update-url'), $(this).sortable('serialize'))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery(document).ready ($) -&amp;gt;&lt;br /&gt;
  sendUpdatedOrder = -&amp;gt;&lt;br /&gt;
    sortedIDs = $(this).sortable('toArray')&lt;br /&gt;
&lt;br /&gt;
    topicIDs = sortedIDs.map (id) -&amp;gt;&lt;br /&gt;
      id.replace 'topic_', ''&lt;br /&gt;
&lt;br /&gt;
    participant_id = $(this).data('participant-id')&lt;br /&gt;
&lt;br /&gt;
    assignment_id = new URL($(this).data('update-url'), window.location.origin).searchParams.get('assignment_id')&lt;br /&gt;
&lt;br /&gt;
    $.ajax&lt;br /&gt;
      type: 'POST'&lt;br /&gt;
      url: $(this).data('update-url')&lt;br /&gt;
      data:&lt;br /&gt;
        topic: topicIDs&lt;br /&gt;
        participant_id: participant_id&lt;br /&gt;
        assignment_id: assignment_id&lt;br /&gt;
      success: (response) -&amp;gt;&lt;br /&gt;
        console.log 'Selections updated successfully.'&lt;br /&gt;
      error: (xhr, status, error) -&amp;gt;&lt;br /&gt;
        alert 'An error occurred while saving your selections. Please try again.'&lt;br /&gt;
        console.error error&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: sendUpdatedOrder&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-1.-signupsheetcontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; Updates ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; method renders the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; action However, the view is associated with the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # provides variables for reviewing page located at views/review_bids/others_work.html.erb&lt;br /&gt;
  def index&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
    return unless current_user_id?(@participant.user_id)&lt;br /&gt;
&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
    # Finding how many reviews have been completed&lt;br /&gt;
    @num_reviews_completed = 0&lt;br /&gt;
    @review_mappings.each do |map|&lt;br /&gt;
      @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # render view for completing reviews after review bidding has been completed&lt;br /&gt;
    render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
  end  &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-2.-reviewbidsotherswork-view-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;review_bids_others_work.html.erb&amp;lt;/code&amp;gt; View Updates ====&lt;br /&gt;
The view is technically part of the SignUpSheetController, but it's being rendered by the ReviewBidsController, which initially threw us off. After chatting with the professor, we decided not to change this view. He indicated that the plan is to reuse these views for both assignment and review bidding.&lt;br /&gt;
&lt;br /&gt;
We suggest building the assignment bidding and review bidding features separately first to ensure each one works independently. Once that’s done, we can clean things up by combining any shared functionality. This could include creating shared views or even introducing an abstract model to handle both types of bidding more efficiently. It could also mean replacing the ReviewBidsController with something more generic, like a BidsController, to keep things reusable and easier to maintain down the line.&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Rename &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; method ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; should be &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt;&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:''' Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The primary activity here is to refactor the method name &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; and fix the hardcoded URL. However, the match topic bidding algorithm no longer works since Heroku is no longer free, per the professor. The reference link below has detailed information about the web service that we can leverage to gain that understanding.&lt;br /&gt;
&lt;br /&gt;
Reference Link for Webservice Details: [https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Updated Implementation ====&lt;br /&gt;
The original requirement was to rename the run_bidding_algorithm method to assign_reviewers, but after digging into the code, we realized that renaming it didn’t make sense. The method runs the bidding algorithm to match topics, and there’s already a separate method to handle assigning review topics.&lt;br /&gt;
&lt;br /&gt;
However, we improved the logic in the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; method (which calls the private &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt;) by introducing a separate &amp;lt;code&amp;gt;BiddingAlgorithmService&amp;lt;/code&amp;gt; object. This keeps the business logic out of the controller action, which makes things cleaner and more maintainable.&lt;br /&gt;
&lt;br /&gt;
==== review_bids_controller ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def run_bidding_algorithm&lt;br /&gt;
    review_bid = ReviewBid.new&lt;br /&gt;
    bidding_data = review_bid.bidding_data&lt;br /&gt;
    matched_topics = BiddingAlgorithmService.new(bidding_data).run&lt;br /&gt;
    raise ArgumentError, 'Failed to assign reviewers. Please try again later.' unless matched_topics&lt;br /&gt;
&lt;br /&gt;
    matched_topics&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== bidding_algorithm_service ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# frozen_string_literal: true&lt;br /&gt;
&lt;br /&gt;
# The `BiddingAlgorithmService` run the bid assignment algorithm&lt;br /&gt;
# Sends student IDs, topic IDs, student preferences, and timestamps to the web service&lt;br /&gt;
# The web service returns the matched assignments in the JSON response body&lt;br /&gt;
class BiddingAlgorithmService&lt;br /&gt;
  SERVICE_URL = 'http://app-csc517.herokuapp.com/match_topics'.freeze&lt;br /&gt;
&lt;br /&gt;
  # MOCK_DATA is based on the documentation detailing how the webservice behaves.&lt;br /&gt;
  # Reference: https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review#Webservice&lt;br /&gt;
  MOCK_DATA = {&lt;br /&gt;
    36239 =&amp;gt; [3970, 3972, 3975],&lt;br /&gt;
    36240 =&amp;gt; [3973, 3974, 3972],&lt;br /&gt;
    36241 =&amp;gt; [3969, 3971, 3972],&lt;br /&gt;
    36242 =&amp;gt; [3969, 3971, 3973],&lt;br /&gt;
    36243 =&amp;gt; [3969, 3970, 3971]&lt;br /&gt;
  }.freeze&lt;br /&gt;
&lt;br /&gt;
  def initialize(bidding_data)&lt;br /&gt;
    @bidding_data = bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def run&lt;br /&gt;
    return MOCK_DATA if Rails.application.config.use_mock_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
    perform_request&lt;br /&gt;
  rescue RestClient::ExceptionWithResponse, JSON::ParserError&lt;br /&gt;
    false&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def perform_request&lt;br /&gt;
    response = RestClient.post(&lt;br /&gt;
      SERVICE_URL,&lt;br /&gt;
      @bidding_data.to_json,&lt;br /&gt;
      content_type: :json,&lt;br /&gt;
      accept: :json&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    validate_response(response)&lt;br /&gt;
    JSON.parse(response.body)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def validate_response(response)&lt;br /&gt;
    raise 'Invalid response format' unless response.headers[:content_type] &amp;amp;&amp;amp; response.headers[:content_type].include?('application/json')&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Resolve Functionality Issues for Non Bidders ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Currently it doesn't work if some student does not bid. In this case, algorithm needs to be fixed to ignore anyone who didn’t bid, and let them choose from what’s left over.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to do several things within this controller. We will set a default state for non-bidders that we can point to to give them options to select topics to review from the leftovers. We will consolidate &amp;lt;code&amp;gt;@sign_up_topics&amp;lt;/code&amp;gt; to resolve the DRY violation. Lastly, we will remove business logic from the show method and push it to a new helper to conform to the SRP.&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # Additional method&lt;br /&gt;
&lt;br /&gt;
  # Provides variables for review bidding page&lt;br /&gt;
  def show&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id].to_i)&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @sign_up_topics = SignUpTopic.where(assignment_id: @assignment.id, private_to: nil)&lt;br /&gt;
    my_topic = SignedUpTeam.topic_id(@participant.parent_id, @participant.user_id)&lt;br /&gt;
    @sign_up_topics -= SignUpTopic.where(assignment_id: @assignment.id, id: my_topic)&lt;br /&gt;
    @num_participants = AssignmentParticipant.where(parent_id: @assignment.id).count&lt;br /&gt;
    @selected_topics = nil # This is used to list the topics assigned to review (i.e., select == assigned)&lt;br /&gt;
    @bids = ReviewBid.where(participant_id: @participant, assignment_id: @assignment.id)&lt;br /&gt;
    signed_up_topics = []&lt;br /&gt;
    &lt;br /&gt;
    @bids.each do |bid|&lt;br /&gt;
      sign_up_topic = SignUpTopic.find_by(id: bid.signuptopic_id)&lt;br /&gt;
      signed_up_topics &amp;lt;&amp;lt; sign_up_topic if sign_up_topic&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    signed_up_topics &amp;amp;= @sign_up_topics&lt;br /&gt;
    @sign_up_topics -= signed_up_topics&lt;br /&gt;
    @bids = signed_up_topics&lt;br /&gt;
    @num_of_topics = @sign_up_topics.size&lt;br /&gt;
    @assigned_review_maps = []&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: @assignment.id, reviewer_id: @participant.id).each do |review_map|&lt;br /&gt;
      @assigned_review_maps &amp;lt;&amp;lt; review_map&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Explicitly render view since it's in the sign up sheet views&lt;br /&gt;
    render 'sign_up_sheet/review_bids_show'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Additional methods&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-2.-reviewbidshelper-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
 # Assigns bidding topics to reviewers&lt;br /&gt;
  def assign_bidding&lt;br /&gt;
      assignment = validate_assignment(params[:assignment_id])&lt;br /&gt;
&lt;br /&gt;
      reviewers = validate_reviewers(assignment.id)&lt;br /&gt;
      reviewer_ids = reviewers.map(&amp;amp;:id)&lt;br /&gt;
&lt;br /&gt;
      matched_topics = run_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
      if matched_topics.blank?&lt;br /&gt;
        flash[:alert] = 'Topic or assignment is missing'&lt;br /&gt;
        redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      leftover_topics = find_leftover_topics(assignment.id, matched_topics)&lt;br /&gt;
      assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.new.assign_review_topics(matched_topics)&lt;br /&gt;
      assignment.update!(can_choose_topic_to_review: false)&lt;br /&gt;
&lt;br /&gt;
      flash[:notice] = 'Reviewers were successfully assigned to topics.'&lt;br /&gt;
      redirect_back fallback_location: root_path&lt;br /&gt;
    rescue ArgumentError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ArgumentError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: e.message&lt;br /&gt;
    rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::RecordInvalid: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue ActiveRecord::ActiveRecordError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::ActiveRecordError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue StandardError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;StandardError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers. Please try again later.'&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  #service methods here ...&lt;br /&gt;
&lt;br /&gt;
  def find_leftover_topics(assignment_id, matched_topics)&lt;br /&gt;
    all_topic_ids = SignUpTopic.where(assignment_id: assignment_id).pluck(:id)&lt;br /&gt;
    assigned_topic_ids = matched_topics.map { |match| match[:topic_id] }&lt;br /&gt;
&lt;br /&gt;
    # Calculate leftover topics&lt;br /&gt;
    all_topic_ids - assigned_topic_ids&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
    return if leftover_topics.blank?&lt;br /&gt;
&lt;br /&gt;
    # Find non-bidders by excluding already matched reviewers&lt;br /&gt;
    non_bidders = reviewer_ids - matched_topics.keys&lt;br /&gt;
&lt;br /&gt;
    # Assign leftover topics to non-bidders in a round-robin fashion&lt;br /&gt;
    non_bidders.each_with_index do |reviewer_id, index|&lt;br /&gt;
      topic_id = leftover_topics[index % leftover_topics.length]&lt;br /&gt;
      ReviewBid.create(priority: 1, signuptopic_id: topic_id, participant_id: reviewer_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirm Functionality for Single-person Teams ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Make sure your code works on individual assignments, i.e., assignments where participants sign up for topics instead of teams. In this case, a team is created for each participant when they sign up. So the code should work for assignments to which either individuals or teams submit.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to make an update to focus on topic rather than team or have it where teams are updated based on topic id.We will probably utilize sign up topic or a similar object to accomplish this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-5-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bid Status Messaging ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| To make the functionality more intuitive, include a message to say how many students are eligible to submit bids, how many have submitted their bids, when the deadline for submitting bids is.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
The logic for these messages will go into the helper file. It is effectively the same reasoning as our migration of other business/functionality code from the controller to the helper to conform to the SRP. It will go hand in hand with the SignUpTopic portion of the code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-6-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Why are the methods in review_bid.rb class methods? Can we change them to instance methods or move it to helpers?&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-1.-reviewbid-model-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model Updates ====&lt;br /&gt;
Since the current class methods are tied to &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model instances, we will change them to instance methods. There is no need to move them to helpers.&lt;br /&gt;
&lt;br /&gt;
===== Review Bid Model Flow Before Refactoring =====&lt;br /&gt;
This sequence diagram describes how the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; interacts with the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model in assigning review topics to reviewers according to their bids. Once the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; action is initiated by the user, the controller gathers the IDs of reviewers and calls &amp;lt;code&amp;gt;ReviewBid.bidding_data&amp;lt;/code&amp;gt; to gather bidding information. That is sent out to an external bidding algorithm, and the matched topics that come back are used by &amp;lt;code&amp;gt;ReviewBid.assign_review_topics&amp;lt;/code&amp;gt; to create &amp;lt;code&amp;gt;ReviewResponseMap&amp;lt;/code&amp;gt; entries, assigning topics to reviewers for the assignment.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
  belongs_to :assignment, class_name: 'Assignment'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  def self.bidding_data(assignment_id, reviewer_ids)&lt;br /&gt;
    # create basic hash and set basic hash data&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'users' =&amp;gt; {}, 'max_accepted_proposals' =&amp;gt; [] }&lt;br /&gt;
    bidding_data['tid'] = SignUpTopic.where(assignment_id: assignment_id).ids&lt;br /&gt;
    bidding_data['max_accepted_proposals'] = Assignment.where(id: assignment_id).pluck(:num_reviews_allowed).first&lt;br /&gt;
&lt;br /&gt;
    # loop through reviewer_ids to get reviewer specific bidding data&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      bidding_data['users'][reviewer_id] = reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # assigns topics to reviews as matched by the webservice algorithm&lt;br /&gt;
  def self.assign_review_topics(assignment_id, reviewer_ids, matched_topics, _min_num_reviews = 2)&lt;br /&gt;
    # if review response map already created, delete it&lt;br /&gt;
    if ReviewResponseMap.where(reviewed_object_id: assignment_id)&lt;br /&gt;
      ReviewResponseMap.where(reviewed_object_id: assignment_id).destroy_all&lt;br /&gt;
    end&lt;br /&gt;
    # loop through reviewer_ids to assign reviews to each reviewer&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      topics_to_assign = matched_topics[reviewer_id.to_s]&lt;br /&gt;
      topics_to_assign.each do |topic|&lt;br /&gt;
        assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method to assign a single topic to a reviewer&lt;br /&gt;
  def self.assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic).pluck(:team_id).first&lt;br /&gt;
    team_to_review.nil? ? [] : ReviewResponseMap.create(reviewed_object_id: assignment_id, reviewer_id: reviewer_id, reviewee_id: team_to_review, type: 'ReviewResponseMap')&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method for getting individual reviewer_ids bidding data&lt;br /&gt;
  # returns user's bidding data hash&lt;br /&gt;
  def self.reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    reviewer_user_id = AssignmentParticipant.find(reviewer_id).user_id&lt;br /&gt;
    self_topic = SignedUpTeam.topic_id(assignment_id, reviewer_user_id)&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'otid' =&amp;gt; self_topic, 'priority' =&amp;gt; [], 'time' =&amp;gt; [] }&lt;br /&gt;
    bids = ReviewBid.where(participant_id: reviewer_id)&lt;br /&gt;
&lt;br /&gt;
    # loop through each bid for a topic to get specific data&lt;br /&gt;
    bids.each do |bid|&lt;br /&gt;
      bidding_data['tid'] &amp;lt;&amp;lt; bid.signuptopic_id&lt;br /&gt;
      bidding_data['priority'] &amp;lt;&amp;lt; bid.priority&lt;br /&gt;
      bidding_data['time'] &amp;lt;&amp;lt; bid.updated_at&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  # Instance method to get bidding data for this review bid&lt;br /&gt;
  def bidding_data&lt;br /&gt;
    {&lt;br /&gt;
      'tid' =&amp;gt; topic_ids,&lt;br /&gt;
      'otid' =&amp;gt; self_topic,&lt;br /&gt;
      'priority' =&amp;gt; priorities,&lt;br /&gt;
      'time' =&amp;gt; updated_times&lt;br /&gt;
    }&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # bid object to limit db touches for similar data acquisitions&lt;br /&gt;
  def bids&lt;br /&gt;
    @bids ||= ReviewBid.where(participant_id: participant_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def topic_ids&lt;br /&gt;
    bids.pluck(:signuptopic_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def priorities&lt;br /&gt;
    bids.pluck(:priority)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def updated_times&lt;br /&gt;
    bids.pluck(:updated_at)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self_topic&lt;br /&gt;
    return nil unless topic &amp;amp;&amp;amp; topic.assignment_id&lt;br /&gt;
&lt;br /&gt;
    SignedUpTeam.topic_id(topic.assignment_id, participant.user_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign topics to reviews&lt;br /&gt;
  def assign_review_topics(matched_topics)&lt;br /&gt;
    validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: topic.assignment_id).destroy_all&lt;br /&gt;
&lt;br /&gt;
    matched_topics.each do |reviewer_id, topics|&lt;br /&gt;
      Array(topics).each { |topic_id| assign_topic_to_reviewer(reviewer_id, topic_id) }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign a single topic to a reviewer&lt;br /&gt;
  def assign_topic_to_reviewer(reviewer_id, topic_id)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic_id).pluck(:team_id).first&lt;br /&gt;
    return if team_to_review.nil?&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.create(&lt;br /&gt;
      reviewed_object_id: topic.assignment_id,&lt;br /&gt;
      reviewer_id: reviewer_id,&lt;br /&gt;
      reviewee_id: team_to_review,&lt;br /&gt;
      type: 'ReviewResponseMap'&lt;br /&gt;
    )&lt;br /&gt;
    # error handling&lt;br /&gt;
  rescue StandardError =&amp;gt; e&lt;br /&gt;
    Rails.logger.error(&amp;quot;Failed to assign topic #{topic_id} to reviewer #{reviewer_id}: #{e.message}&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
    raise ArgumentError, 'Topic or assignment is missing' if topic.nil? || topic.assignment_id.nil?&lt;br /&gt;
    raise ArgumentError, 'Matched topics must be a Hash' unless matched_topics.is_a?(Hash)&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
Once we changed them to instance methods, we modified their use in the controller code to instantiate the object first.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-3.-schema.db-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;Schema.db&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We also need to remove the foreign key reference to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model since the &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model already has access to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;, and that is the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; we should be referencing. This will also involve modifying the database to remove the foreign key reference to the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Steps required:&lt;br /&gt;
&lt;br /&gt;
# Review the records in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table in the database to see if there are any records and if those records have values for &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;&lt;br /&gt;
# Create a migration file using the command: &amp;lt;code&amp;gt;rails generate migration RemoveAssignmentFromReviewBids&amp;lt;/code&amp;gt;&lt;br /&gt;
# In the generated file, indicate the change that needs to be made using the change method and migration columns &amp;lt;code&amp;gt;remove_foreign_key&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;remove_column&amp;lt;/code&amp;gt;. Migration should be straightforward since there is no cascading behavior (i.e., &amp;lt;code&amp;gt;dependent:destroy&amp;lt;/code&amp;gt;) to worry about.&lt;br /&gt;
# Once comfortable with the updates to the generated file, run the migration using the &amp;lt;code&amp;gt;rails db:migrate&amp;lt;/code&amp;gt; command.&lt;br /&gt;
# Run a query in the DB to ensure no orphaned records are in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table after the updates.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-4.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implement Edge Case Testing ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| In the previous implementation wiki, there are edge cases which are not exhaustively tested. Should test those edge cases thoroughly and add more edge case testing - link&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span class=&amp;quot;mark&amp;quot;&amp;gt;TODO&amp;lt;/span&amp;gt;:We just got initial test results back. We still need to figure out why tests are failing and what may be slipping through the cracks.&lt;br /&gt;
&lt;br /&gt;
[[File:Test expertiza.png|411x459px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-8-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design Patterns and Principles ===&lt;br /&gt;
Below are the design patterns and principles we plan to use to address the problem statements outlined in the project requirements:&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
** Controller Responsibility&lt;br /&gt;
*** To address the issue of authentication logic residing in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, we will refactor the code to ensure the controller solely handles request and response flows.&lt;br /&gt;
*** Authentication logic will be moved to dedicated methods in &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt;, making the codebase easier to maintain and test.&lt;br /&gt;
*** We will move this logic into helper methods or models, ensuring each component has a single responsibility and improving code readability.&lt;br /&gt;
&lt;br /&gt;
* Separation of Concerns (SoC)&lt;br /&gt;
** By relocating authorization logic out of the controller, we separate authentication from request handling.&lt;br /&gt;
** Adhering to SoC enhances readability and maintainability by allowing each part of the application to focus on its specific role.&lt;br /&gt;
&lt;br /&gt;
* Don't Repeat Yourself (DRY)&lt;br /&gt;
** We will inspect the existing implementation for code duplication, particularly in the controller actions.&lt;br /&gt;
** Any repetitive code will be refactored or removed to reduce redundancy and improve clarity.&lt;br /&gt;
&lt;br /&gt;
* Dependency Inversion Principle (DIP)&lt;br /&gt;
** We plan to update class methods to instance methods, allowing the controller to depend on abstractions rather than concrete implementations.&lt;br /&gt;
&lt;br /&gt;
** Clear and intentional naming during refactoring will enhance code clarity and maintainability.&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
The Excel spreadsheet below details our test plan for testing the code we changed or created to ensure code coverage. The process involved reviewing the code and determining all the possible scenarios that needed to be covered based on what the method was doing, then creating test cases accordingly.&lt;br /&gt;
&lt;br /&gt;
* [https://docs.google.com/spreadsheets/d/1iHzOdSwlTtxOtf5VsNdwOaCP8NzYMmiJ/edit?usp=drive_link Open Test Matrix Spreadsheet]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/review_bids_controller_spec.rb ReviewBidsController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/sign_up_sheet_controller_spec.rb SignUpSheetController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/models/review_bid_spec.rb ReviewBid model updated test cases]&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-1.-mentor&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Ed Gehringer&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-2.-members&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Members ===&lt;br /&gt;
&lt;br /&gt;
* Gavin Teague&lt;br /&gt;
* Janice Uwujaren&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xi.-links&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
Previous Wiki:  &lt;br /&gt;
[https://wiki.expertiza.ncsu.edu/index.php/CSC/ECE_517_Fall_2021_-_E2151._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;Link&amp;lt;/u&amp;gt;]  &lt;br /&gt;
&lt;br /&gt;
Reference Link for Webservice Details:  &lt;br /&gt;
[https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
Guidance on Using Service Objects in Rails:&lt;br /&gt;
[https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial Rails Service Objects Tutorial]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xii.-references&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/document/d/1LHVacylQmB15I6VcYUA5nuC7GRRU9szHzZhjlPcE-PA/edit?tab=t.0#heading=h.u0oycoj6e0ev &amp;lt;u&amp;gt;Project Instructions&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;appendix-a-uml-class-diagram-plantuml-code-for-review-bids&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Juwujar</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=161054</id>
		<title>CSC/ECE 517 Fall 2024 - E2485. Allow reviewers to bid on what to review</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=161054"/>
		<updated>2024-12-12T06:14:58Z</updated>

		<summary type="html">&lt;p&gt;Juwujar: /* Design Patterns and Principles */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
This project will fix the defects and improve the functionality in Expertiza of the Reviewer Bidding feature. It will make the functionality smoother, more reliable, and align with Expertiza's collaborative learning objectives. &lt;br /&gt;
&lt;br /&gt;
The Review Bidding feature lets reviewers focus on topics they know best or care about most. If someone feels exceptionally qualified or interested in a topic, they can bid to review submissions in that area. This helps ensure reviews are insightful and enjoyable for the reviewer while benefiting the other participants and the overall process.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iii.-problem-statements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Problem Statements ==&lt;br /&gt;
&lt;br /&gt;
Assigning reviews to users on Expertiza involves a complex bidding algorithm that requires optimization before production deployment. Below are key areas we plan to address as per the project requirements:&lt;br /&gt;
* Code Refactoring and Best Practices&lt;br /&gt;
** '''Utilize Authentication Utilities''': Modify the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; function in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to use existing authentication utilities, enhancing code reusability and adhering to the Single Responsibility Principle (SRP) and Separation of Concerns (SoC).&lt;br /&gt;
** '''Eliminate Code Duplication''': Refactor &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; to comply with the Don't Repeat Yourself (DRY) principle.&lt;br /&gt;
** '''Rename Functions for Clarity''': Change &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; to better reflect its purpose.&lt;br /&gt;
** '''Refactor Methods in '''&amp;lt;code&amp;gt;review_bid.rb&amp;lt;/code&amp;gt;''':''': Convert class methods to instance methods or relocate them to helper classes for improved code organization.&lt;br /&gt;
* Functionality Enhancements&lt;br /&gt;
** '''Handle Missing Bids Gracefully''': Adjust the bidding algorithm to accommodate students who do not bid, allowing them to select from the remaining options.&lt;br /&gt;
** '''Support Both Individual and Team Assignments''': Ensure the code functions correctly for participant and team-based assignments when signing up for topics.&lt;br /&gt;
* User Interface Improvements&lt;br /&gt;
** '''Increase Transparency in the Bidding Process''': Display messages indicating how many students are eligible to bid, how many have submitted bids, and the deadline.&lt;br /&gt;
** '''Accurate Bid Status Indicators''': Ensure topic colors change correctly based on the number of outstanding bids, providing clear visual feedback to users.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Exhaustive Edge Case Testing''': Conduct thorough testing for edge cases not previously covered, adding additional test cases as needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iv.-design-goals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Design Goals ==&lt;br /&gt;
&lt;br /&gt;
While addressing the existing defects and enhancing the Reviewer Bidding feature, we plan to adhere to the following design guidelines:&lt;br /&gt;
&lt;br /&gt;
* Code Quality and Maintainability&lt;br /&gt;
** '''Improve Code Structure: '''Implement refactoring strategies to enhance code readability and maintainability, ensuring adherence to best practices like the DRY principle.&lt;br /&gt;
** '''Standardize Authentication Logic: '''Utilize a centralized authentication utility to promote consistency and reusability across the application.&lt;br /&gt;
* System Stability and Flexibility&lt;br /&gt;
** '''Enhance Algorithm Robustness: '''Modify the bidding algorithm to handle scenarios where students do not bid, ensuring the system remains stable under all conditions.&lt;br /&gt;
** '''Ensure Assignment Versatility: '''Design the system to seamlessly support both individual and team assignments without additional configuration.&lt;br /&gt;
* User Experience Enhancements&lt;br /&gt;
** '''Increase Bidding Transparency: '''Develop user interface elements that clearly display bidding status, deadlines, and participation metrics.&lt;br /&gt;
** '''Implement Dynamic Visual Feedback: '''Use real-time visual cues, such as color changes, to inform users about the bidding status instantly.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Develop Comprehensive Test Suites: '''Create extensive automated tests covering edge cases and critical functionalities to guarantee reliable system behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;v.-uml-diagram&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== UML Diagram ==&lt;br /&gt;
&lt;br /&gt;
Below is a visual representation of the models associated with the review bids functionality, depicting the data structures and their relationships.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml class diagram2.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi.-implementation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
'''ReviewBid: '''The &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; manages the bidding process, where participants indicate their preferences for reviewing different topics or assignments. This helps distribute review tasks fairly based on participants' interests.&lt;br /&gt;
&lt;br /&gt;
'''Assignment: '''The &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model represents team or individual assignments in Expertiza. It stores all the essential information required to keep the assignment process organized, like managing participants, setting due dates, forming teams, and overseeing the review process.&lt;br /&gt;
&lt;br /&gt;
'''SignUpTopic: '''The &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt; model identifies specific topics or areas within an assignment for which participants can sign up. It manages the availability of these topics, tracks bids, coordinates team sign-ups, and assigns topics based on both bids and participant availability. &lt;br /&gt;
&lt;br /&gt;
'''Participant: '''The &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model is about the individual users who participate in an assignment. This includes students, instructors, teaching assistants, or other relevant roles. This model is responsible for managing key user information, monitoring interactions with the assignment, keeping track of team memberships, and recording things like badges and review grades.&lt;br /&gt;
&lt;br /&gt;
== Implementation ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1.-decoupling-authorization-logic-from-reviewbidscontroller&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Decoupling Authorization Logic from &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| '''&amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; needs to use the authentication utilities'''&lt;br /&gt;
&lt;br /&gt;
The function &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; should use the available authentication utilities to support the Single Responsibility Principle and Separation of Concerns and to support code reusability.&lt;br /&gt;
&lt;br /&gt;
| '''Authentication Consistency:''' Remove authentication logic in the controller and call logic in the authentication utility class instead to ensure consistent use of the same logic across the application, foster reusability, and eliminate DRY.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
==== Authorization Flow Before Refactoring ====&lt;br /&gt;
The sequence diagram depicts the authorization process within the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;. When a user invokes an action, the controller checks if the action is allowed by verifying the user's role through the &amp;lt;code&amp;gt;current_role_name()&amp;lt;/code&amp;gt; method in the &amp;lt;code&amp;gt;ApplicationController&amp;lt;/code&amp;gt;. Depending on the action and the user's role, the controller either authorizes the user to proceed or denies access, ultimately executing the action logic if authorized and responding to the user.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 1.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We simplified the &amp;lt;code&amp;gt;action_allowed?&amp;lt;/code&amp;gt; method by decoupling authorization logic from the controller to use helper methods in the &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt; module. The &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; should not handle authentication, which is a violation of SRP. &lt;br /&gt;
&lt;br /&gt;
Note: We also made similar updates to the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; methods for &amp;lt;code&amp;gt;SignUpController&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;vertical-align:top;&amp;quot; | Original Code !! style=&amp;quot;vertical-align:top;&amp;quot; | Refactored Code&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index'&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator',&lt;br /&gt;
     'Student'].include? current_role_name and&lt;br /&gt;
        ((%w[list].include? action_name) ? are_needed_authorizations_present?(params[:id], &amp;quot;participant&amp;quot;, &amp;quot;reader&amp;quot;, &amp;quot;submitter&amp;quot;, &amp;quot;reviewer&amp;quot;) : true)&lt;br /&gt;
  else&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator'].include? current_role_name&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index', 'list'&lt;br /&gt;
    current_user_has_student_privileges? &amp;amp;&amp;amp; list_authorization_check&lt;br /&gt;
  else&lt;br /&gt;
    current_user_has_ta_privileges?&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; action ===&lt;br /&gt;
Previously, the &amp;lt;code&amp;gt;ReviewBidsController's index&amp;lt;/code&amp;gt; action was overloaded with responsibilities—it handled everything from locating and validating assignments to fetching review mappings and counting completed reviews. This all-in-one approach made the controller action messy. &lt;br /&gt;
&lt;br /&gt;
We have moved the business logic from the controller action and into dedicated service objects. With this change, we introduced two key service objects: &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; focuses on retrieving the participant along with their assignments, while the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; takes on the task of managing review mappings and calculating review-related stats, like how many reviews have been completed. &lt;br /&gt;
&lt;br /&gt;
This separation of responsibilities aligns with the Single Responsibility Principle. The logic for fetching assignments, retrieving review mappings, and counting reviews is now more flexible and reusable throughout the application. For example, we used methods in the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; in both the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; and the &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;, helping to avoid code duplication.The article [https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial Rails Service Objects Tutorial] served as a guide on when and how to use service objects in Rails.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
  return unless current_user_id?(@participant.user_id)&lt;br /&gt;
  @assignment = @participant.assignment&lt;br /&gt;
  @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
  # Finding how many reviews have been completed&lt;br /&gt;
  @num_reviews_completed = 0&lt;br /&gt;
  @review_mappings.each do |map|&lt;br /&gt;
    @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # render view for completing reviews after review bidding has been completed&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @assignment = participant_service.assignment&lt;br /&gt;
  unless @assignment.is_a?(Assignment)&lt;br /&gt;
    flash[:error] = 'Assignment not found.'&lt;br /&gt;
    redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  @review_mappings = review_service.review_mappings&lt;br /&gt;
  @num_reviews_completed = review_service.review_counts[:completed]&lt;br /&gt;
&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Optimize &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Action functionality ===&lt;br /&gt;
In the earlier version of the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, the server only needed a couple of key pieces of information: an array of topic IDs (params[:topic]) and the participant ID (params[:id}). From there, it would figure out the assignment_id by using the first topic as a reference. This is risky because the &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt; associated with the topic may not be a valid &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
With the new implementation, we structured the parameters to include the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt;, an array of topic IDs, and &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;. This change ensures that the server always receives complete and reliable information, preventing issues caused by missing or inconsistent data. It also removes the need for the server to perform unnecessary lookups, as in the case of &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;, allowing the controller to focus on receiving requests and rendering appropriate responses.&lt;br /&gt;
&lt;br /&gt;
While these updates have cleaned up the set_priority action, they still contain a lot of business logic, like validations and priority updates. To make the code even more maintainable, there’s room for further refactoring—perhaps moving some of this logic into a service class or model method to streamline the controller even more.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | Old Code&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    if params[:topic].nil?&lt;br /&gt;
      ReviewBid.where(participant_id: params[:id]).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      participant = AssignmentParticipant.find_by(id: params[:id])&lt;br /&gt;
      assignment_id = SignUpTopic.find(params[:topic].first).assignment.id&lt;br /&gt;
      @bids = ReviewBid.where(participant_id: params[:id])&lt;br /&gt;
      signed_up_topics = ReviewBid.where(participant_id: params[:id]).map(&amp;amp;:signuptopic_id)&lt;br /&gt;
      signed_up_topics -= params[:topic].map(&amp;amp;:to_i)&lt;br /&gt;
      signed_up_topics.each do |topic|&lt;br /&gt;
        ReviewBid.where(signuptopic_id: topic, participant_id: params[:id]).destroy_all&lt;br /&gt;
      end&lt;br /&gt;
      params[:topic].each_with_index do |topic_id, index|&lt;br /&gt;
        bid_existence = ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id])&lt;br /&gt;
        if bid_existence.empty?&lt;br /&gt;
          ReviewBid.create(priority: index + 1, signuptopic_id: topic_id, participant_id: params[:id], assignment_id: assignment_id)&lt;br /&gt;
        else&lt;br /&gt;
          ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id]).update_all(priority: index + 1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to action: 'show', assignment_id: params[:assignment_id], id: params[:id]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    participant_id = params[:participant_id].to_i&lt;br /&gt;
    selected_topic_ids = params[:topic]&amp;amp;.map(&amp;amp;:to_i) || []&lt;br /&gt;
    assignment_id = params[:assignment_id].to_i&lt;br /&gt;
&lt;br /&gt;
    unless current_user_can_modify_participant?(participant_id)&lt;br /&gt;
      flash[:error] = 'You are not authorized to modify these bids.'&lt;br /&gt;
      respond_to do |format|&lt;br /&gt;
        format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
        format.json { render json: { status: 'unauthorized' }, status: :unauthorized }&lt;br /&gt;
      end&lt;br /&gt;
      return&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    if selected_topic_ids.empty?&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      assignment = Assignment.find_by(id: assignment_id)&lt;br /&gt;
      if assignment.nil?&lt;br /&gt;
        flash[:error] = &amp;quot;Invalid assignment.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_assignment' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      unless SignUpTopic.where(id: selected_topic_ids, assignment_id: assignment_id).count == selected_topic_ids.size&lt;br /&gt;
        flash[:error] = &amp;quot;One or more selected topics are invalid.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_topics' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).where.not(signuptopic_id: selected_topic_ids).destroy_all&lt;br /&gt;
&lt;br /&gt;
      selected_topic_ids.each_with_index do |topic_id, index|&lt;br /&gt;
        bid = ReviewBid.find_or_initialize_by(signuptopic_id: topic_id, participant_id: participant_id)&lt;br /&gt;
        bid.priority = index + 1&lt;br /&gt;
        bid.assignment_id = assignment_id&lt;br /&gt;
        bid.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_to action: 'show', assignment_id: assignment_id, id: participant_id, notice: 'Review bids updated successfully.' }&lt;br /&gt;
      format.json { render json: { status: 'success' }, status: :ok }&lt;br /&gt;
    end&lt;br /&gt;
  rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
    flash[:error] = &amp;quot;Failed to update priorities: #{e.message}&amp;quot;&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
      format.json { render json: { status: 'error', message: e.message }, status: :unprocessable_entity }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Client Side Changes to Support &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Changes ====&lt;br /&gt;
We refactored the client-side code to ensure it passed the correct information from the view to the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action. On the client side, we now grab the topicIDs and send them as &amp;lt;code&amp;gt;selected_topic_ids&amp;lt;/code&amp;gt;, allowing the controller to figure out which topics were selected and in what order.  &lt;br /&gt;
&lt;br /&gt;
When working with jQuery, AJAX is awesome because it lets us update the server without reloading the page, making the user experience smooth and responsive. When we pull the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, we can grab it along with other data, like the list of topics, and include it as a single request. This way, we don’t have to rely on the URL to know which participant we’re working with, which makes the code cleaner.&lt;br /&gt;
&lt;br /&gt;
We also added an update handler so that any time the order of the selected topic changes in the view, information is sent to the appropriate controller action. This ensures that the controller and the view remain in sync, with real-time updates, and includes a mechanism for providing feedback if something goes wrong.&lt;br /&gt;
&lt;br /&gt;
Note: One interesting aspect of the application was the mix of JavaScript and CoffeeScript code. It wasn't clear why both types of code were used.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery -&amp;gt;&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: -&amp;gt;&lt;br /&gt;
      $.post($(this).data('update-url'), $(this).sortable('serialize'))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery(document).ready ($) -&amp;gt;&lt;br /&gt;
  sendUpdatedOrder = -&amp;gt;&lt;br /&gt;
    sortedIDs = $(this).sortable('toArray')&lt;br /&gt;
&lt;br /&gt;
    topicIDs = sortedIDs.map (id) -&amp;gt;&lt;br /&gt;
      id.replace 'topic_', ''&lt;br /&gt;
&lt;br /&gt;
    participant_id = $(this).data('participant-id')&lt;br /&gt;
&lt;br /&gt;
    assignment_id = new URL($(this).data('update-url'), window.location.origin).searchParams.get('assignment_id')&lt;br /&gt;
&lt;br /&gt;
    $.ajax&lt;br /&gt;
      type: 'POST'&lt;br /&gt;
      url: $(this).data('update-url')&lt;br /&gt;
      data:&lt;br /&gt;
        topic: topicIDs&lt;br /&gt;
        participant_id: participant_id&lt;br /&gt;
        assignment_id: assignment_id&lt;br /&gt;
      success: (response) -&amp;gt;&lt;br /&gt;
        console.log 'Selections updated successfully.'&lt;br /&gt;
      error: (xhr, status, error) -&amp;gt;&lt;br /&gt;
        alert 'An error occurred while saving your selections. Please try again.'&lt;br /&gt;
        console.error error&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: sendUpdatedOrder&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-1.-signupsheetcontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; Updates ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; method renders the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; action However, the view is associated with the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # provides variables for reviewing page located at views/review_bids/others_work.html.erb&lt;br /&gt;
  def index&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
    return unless current_user_id?(@participant.user_id)&lt;br /&gt;
&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
    # Finding how many reviews have been completed&lt;br /&gt;
    @num_reviews_completed = 0&lt;br /&gt;
    @review_mappings.each do |map|&lt;br /&gt;
      @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # render view for completing reviews after review bidding has been completed&lt;br /&gt;
    render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
  end  &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-2.-reviewbidsotherswork-view-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;review_bids_others_work.html.erb&amp;lt;/code&amp;gt; View Updates ====&lt;br /&gt;
The view is technically part of the SignUpSheetController, but it's being rendered by the ReviewBidsController, which initially threw us off. After chatting with the professor, we decided not to change this view. He indicated that the plan is to reuse these views for both assignment and review bidding.&lt;br /&gt;
&lt;br /&gt;
We suggest building the assignment bidding and review bidding features separately first to ensure each one works independently. Once that’s done, we can clean things up by combining any shared functionality. This could include creating shared views or even introducing an abstract model to handle both types of bidding more efficiently. It could also mean replacing the ReviewBidsController with something more generic, like a BidsController, to keep things reusable and easier to maintain down the line.&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Rename &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; method ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; should be &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt;&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:''' Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The primary activity here is to refactor the method name &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; and fix the hardcoded URL. However, the match topic bidding algorithm no longer works since Heroku is no longer free, per the professor. The reference link below has detailed information about the web service that we can leverage to gain that understanding.&lt;br /&gt;
&lt;br /&gt;
Reference Link for Webservice Details: [https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Updated Implementation ====&lt;br /&gt;
The original requirement was to rename the run_bidding_algorithm method to assign_reviewers, but after digging into the code, we realized that renaming it didn’t make sense. The method runs the bidding algorithm to match topics, and there’s already a separate method to handle assigning review topics.&lt;br /&gt;
&lt;br /&gt;
However, we improved the logic in the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; method (which calls the private &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt;) by introducing a separate &amp;lt;code&amp;gt;BiddingAlgorithmService&amp;lt;/code&amp;gt; object. This keeps the business logic out of the controller action, which makes things cleaner and more maintainable.&lt;br /&gt;
&lt;br /&gt;
==== review_bids_controller ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def run_bidding_algorithm&lt;br /&gt;
    review_bid = ReviewBid.new&lt;br /&gt;
    bidding_data = review_bid.bidding_data&lt;br /&gt;
    matched_topics = BiddingAlgorithmService.new(bidding_data).run&lt;br /&gt;
    raise ArgumentError, 'Failed to assign reviewers. Please try again later.' unless matched_topics&lt;br /&gt;
&lt;br /&gt;
    matched_topics&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== bidding_algorithm_service ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# frozen_string_literal: true&lt;br /&gt;
&lt;br /&gt;
# The `BiddingAlgorithmService` run the bid assignment algorithm&lt;br /&gt;
# Sends student IDs, topic IDs, student preferences, and timestamps to the web service&lt;br /&gt;
# The web service returns the matched assignments in the JSON response body&lt;br /&gt;
class BiddingAlgorithmService&lt;br /&gt;
  SERVICE_URL = 'http://app-csc517.herokuapp.com/match_topics'.freeze&lt;br /&gt;
&lt;br /&gt;
  # MOCK_DATA is based on the documentation detailing how the webservice behaves.&lt;br /&gt;
  # Reference: https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review#Webservice&lt;br /&gt;
  MOCK_DATA = {&lt;br /&gt;
    36239 =&amp;gt; [3970, 3972, 3975],&lt;br /&gt;
    36240 =&amp;gt; [3973, 3974, 3972],&lt;br /&gt;
    36241 =&amp;gt; [3969, 3971, 3972],&lt;br /&gt;
    36242 =&amp;gt; [3969, 3971, 3973],&lt;br /&gt;
    36243 =&amp;gt; [3969, 3970, 3971]&lt;br /&gt;
  }.freeze&lt;br /&gt;
&lt;br /&gt;
  def initialize(bidding_data)&lt;br /&gt;
    @bidding_data = bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def run&lt;br /&gt;
    return MOCK_DATA if Rails.application.config.use_mock_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
    perform_request&lt;br /&gt;
  rescue RestClient::ExceptionWithResponse, JSON::ParserError&lt;br /&gt;
    false&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def perform_request&lt;br /&gt;
    response = RestClient.post(&lt;br /&gt;
      SERVICE_URL,&lt;br /&gt;
      @bidding_data.to_json,&lt;br /&gt;
      content_type: :json,&lt;br /&gt;
      accept: :json&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    validate_response(response)&lt;br /&gt;
    JSON.parse(response.body)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def validate_response(response)&lt;br /&gt;
    raise 'Invalid response format' unless response.headers[:content_type] &amp;amp;&amp;amp; response.headers[:content_type].include?('application/json')&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Resolve Functionality Issues for Non Bidders ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Currently it doesn't work if some student does not bid. In this case, algorithm needs to be fixed to ignore anyone who didn’t bid, and let them choose from what’s left over.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to do several things within this controller. We will set a default state for non-bidders that we can point to to give them options to select topics to review from the leftovers. We will consolidate &amp;lt;code&amp;gt;@sign_up_topics&amp;lt;/code&amp;gt; to resolve the DRY violation. Lastly, we will remove business logic from the show method and push it to a new helper to conform to the SRP.&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # Additional method&lt;br /&gt;
&lt;br /&gt;
  # Provides variables for review bidding page&lt;br /&gt;
  def show&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id].to_i)&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @sign_up_topics = SignUpTopic.where(assignment_id: @assignment.id, private_to: nil)&lt;br /&gt;
    my_topic = SignedUpTeam.topic_id(@participant.parent_id, @participant.user_id)&lt;br /&gt;
    @sign_up_topics -= SignUpTopic.where(assignment_id: @assignment.id, id: my_topic)&lt;br /&gt;
    @num_participants = AssignmentParticipant.where(parent_id: @assignment.id).count&lt;br /&gt;
    @selected_topics = nil # This is used to list the topics assigned to review (i.e., select == assigned)&lt;br /&gt;
    @bids = ReviewBid.where(participant_id: @participant, assignment_id: @assignment.id)&lt;br /&gt;
    signed_up_topics = []&lt;br /&gt;
    &lt;br /&gt;
    @bids.each do |bid|&lt;br /&gt;
      sign_up_topic = SignUpTopic.find_by(id: bid.signuptopic_id)&lt;br /&gt;
      signed_up_topics &amp;lt;&amp;lt; sign_up_topic if sign_up_topic&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    signed_up_topics &amp;amp;= @sign_up_topics&lt;br /&gt;
    @sign_up_topics -= signed_up_topics&lt;br /&gt;
    @bids = signed_up_topics&lt;br /&gt;
    @num_of_topics = @sign_up_topics.size&lt;br /&gt;
    @assigned_review_maps = []&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: @assignment.id, reviewer_id: @participant.id).each do |review_map|&lt;br /&gt;
      @assigned_review_maps &amp;lt;&amp;lt; review_map&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Explicitly render view since it's in the sign up sheet views&lt;br /&gt;
    render 'sign_up_sheet/review_bids_show'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Additional methods&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-2.-reviewbidshelper-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
 # Assigns bidding topics to reviewers&lt;br /&gt;
  def assign_bidding&lt;br /&gt;
      assignment = validate_assignment(params[:assignment_id])&lt;br /&gt;
&lt;br /&gt;
      reviewers = validate_reviewers(assignment.id)&lt;br /&gt;
      reviewer_ids = reviewers.map(&amp;amp;:id)&lt;br /&gt;
&lt;br /&gt;
      matched_topics = run_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
      if matched_topics.blank?&lt;br /&gt;
        flash[:alert] = 'Topic or assignment is missing'&lt;br /&gt;
        redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      leftover_topics = find_leftover_topics(assignment.id, matched_topics)&lt;br /&gt;
      assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.new.assign_review_topics(matched_topics)&lt;br /&gt;
      assignment.update!(can_choose_topic_to_review: false)&lt;br /&gt;
&lt;br /&gt;
      flash[:notice] = 'Reviewers were successfully assigned to topics.'&lt;br /&gt;
      redirect_back fallback_location: root_path&lt;br /&gt;
    rescue ArgumentError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ArgumentError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: e.message&lt;br /&gt;
    rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::RecordInvalid: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue ActiveRecord::ActiveRecordError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::ActiveRecordError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue StandardError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;StandardError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers. Please try again later.'&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  #service methods here ...&lt;br /&gt;
&lt;br /&gt;
  def find_leftover_topics(assignment_id, matched_topics)&lt;br /&gt;
    all_topic_ids = SignUpTopic.where(assignment_id: assignment_id).pluck(:id)&lt;br /&gt;
    assigned_topic_ids = matched_topics.map { |match| match[:topic_id] }&lt;br /&gt;
&lt;br /&gt;
    # Calculate leftover topics&lt;br /&gt;
    all_topic_ids - assigned_topic_ids&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
    return if leftover_topics.blank?&lt;br /&gt;
&lt;br /&gt;
    # Find non-bidders by excluding already matched reviewers&lt;br /&gt;
    non_bidders = reviewer_ids - matched_topics.keys&lt;br /&gt;
&lt;br /&gt;
    # Assign leftover topics to non-bidders in a round-robin fashion&lt;br /&gt;
    non_bidders.each_with_index do |reviewer_id, index|&lt;br /&gt;
      topic_id = leftover_topics[index % leftover_topics.length]&lt;br /&gt;
      ReviewBid.create(priority: 1, signuptopic_id: topic_id, participant_id: reviewer_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirm Functionality for Single-person Teams ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Make sure your code works on individual assignments, i.e., assignments where participants sign up for topics instead of teams. In this case, a team is created for each participant when they sign up. So the code should work for assignments to which either individuals or teams submit.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to make an update to focus on topic rather than team or have it where teams are updated based on topic id.We will probably utilize sign up topic or a similar object to accomplish this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-5-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bid Status Messaging ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| To make the functionality more intuitive, include a message to say how many students are eligible to submit bids, how many have submitted their bids, when the deadline for submitting bids is.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
The logic for these messages will go into the helper file. It is effectively the same reasoning as our migration of other business/functionality code from the controller to the helper to conform to the SRP. It will go hand in hand with the SignUpTopic portion of the code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-6-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Why are the methods in review_bid.rb class methods? Can we change them to instance methods or move it to helpers?&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-1.-reviewbid-model-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model Updates ====&lt;br /&gt;
Since the current class methods are tied to &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model instances, we will change them to instance methods. There is no need to move them to helpers.&lt;br /&gt;
&lt;br /&gt;
===== Review Bid Model Flow Before Refactoring =====&lt;br /&gt;
This sequence diagram describes how the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; interacts with the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model in assigning review topics to reviewers according to their bids. Once the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; action is initiated by the user, the controller gathers the IDs of reviewers and calls &amp;lt;code&amp;gt;ReviewBid.bidding_data&amp;lt;/code&amp;gt; to gather bidding information. That is sent out to an external bidding algorithm, and the matched topics that come back are used by &amp;lt;code&amp;gt;ReviewBid.assign_review_topics&amp;lt;/code&amp;gt; to create &amp;lt;code&amp;gt;ReviewResponseMap&amp;lt;/code&amp;gt; entries, assigning topics to reviewers for the assignment.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
  belongs_to :assignment, class_name: 'Assignment'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  def self.bidding_data(assignment_id, reviewer_ids)&lt;br /&gt;
    # create basic hash and set basic hash data&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'users' =&amp;gt; {}, 'max_accepted_proposals' =&amp;gt; [] }&lt;br /&gt;
    bidding_data['tid'] = SignUpTopic.where(assignment_id: assignment_id).ids&lt;br /&gt;
    bidding_data['max_accepted_proposals'] = Assignment.where(id: assignment_id).pluck(:num_reviews_allowed).first&lt;br /&gt;
&lt;br /&gt;
    # loop through reviewer_ids to get reviewer specific bidding data&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      bidding_data['users'][reviewer_id] = reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # assigns topics to reviews as matched by the webservice algorithm&lt;br /&gt;
  def self.assign_review_topics(assignment_id, reviewer_ids, matched_topics, _min_num_reviews = 2)&lt;br /&gt;
    # if review response map already created, delete it&lt;br /&gt;
    if ReviewResponseMap.where(reviewed_object_id: assignment_id)&lt;br /&gt;
      ReviewResponseMap.where(reviewed_object_id: assignment_id).destroy_all&lt;br /&gt;
    end&lt;br /&gt;
    # loop through reviewer_ids to assign reviews to each reviewer&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      topics_to_assign = matched_topics[reviewer_id.to_s]&lt;br /&gt;
      topics_to_assign.each do |topic|&lt;br /&gt;
        assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method to assign a single topic to a reviewer&lt;br /&gt;
  def self.assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic).pluck(:team_id).first&lt;br /&gt;
    team_to_review.nil? ? [] : ReviewResponseMap.create(reviewed_object_id: assignment_id, reviewer_id: reviewer_id, reviewee_id: team_to_review, type: 'ReviewResponseMap')&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method for getting individual reviewer_ids bidding data&lt;br /&gt;
  # returns user's bidding data hash&lt;br /&gt;
  def self.reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    reviewer_user_id = AssignmentParticipant.find(reviewer_id).user_id&lt;br /&gt;
    self_topic = SignedUpTeam.topic_id(assignment_id, reviewer_user_id)&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'otid' =&amp;gt; self_topic, 'priority' =&amp;gt; [], 'time' =&amp;gt; [] }&lt;br /&gt;
    bids = ReviewBid.where(participant_id: reviewer_id)&lt;br /&gt;
&lt;br /&gt;
    # loop through each bid for a topic to get specific data&lt;br /&gt;
    bids.each do |bid|&lt;br /&gt;
      bidding_data['tid'] &amp;lt;&amp;lt; bid.signuptopic_id&lt;br /&gt;
      bidding_data['priority'] &amp;lt;&amp;lt; bid.priority&lt;br /&gt;
      bidding_data['time'] &amp;lt;&amp;lt; bid.updated_at&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  # Instance method to get bidding data for this review bid&lt;br /&gt;
  def bidding_data&lt;br /&gt;
    {&lt;br /&gt;
      'tid' =&amp;gt; topic_ids,&lt;br /&gt;
      'otid' =&amp;gt; self_topic,&lt;br /&gt;
      'priority' =&amp;gt; priorities,&lt;br /&gt;
      'time' =&amp;gt; updated_times&lt;br /&gt;
    }&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # bid object to limit db touches for similar data acquisitions&lt;br /&gt;
  def bids&lt;br /&gt;
    @bids ||= ReviewBid.where(participant_id: participant_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def topic_ids&lt;br /&gt;
    bids.pluck(:signuptopic_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def priorities&lt;br /&gt;
    bids.pluck(:priority)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def updated_times&lt;br /&gt;
    bids.pluck(:updated_at)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self_topic&lt;br /&gt;
    return nil unless topic &amp;amp;&amp;amp; topic.assignment_id&lt;br /&gt;
&lt;br /&gt;
    SignedUpTeam.topic_id(topic.assignment_id, participant.user_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign topics to reviews&lt;br /&gt;
  def assign_review_topics(matched_topics)&lt;br /&gt;
    validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: topic.assignment_id).destroy_all&lt;br /&gt;
&lt;br /&gt;
    matched_topics.each do |reviewer_id, topics|&lt;br /&gt;
      Array(topics).each { |topic_id| assign_topic_to_reviewer(reviewer_id, topic_id) }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign a single topic to a reviewer&lt;br /&gt;
  def assign_topic_to_reviewer(reviewer_id, topic_id)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic_id).pluck(:team_id).first&lt;br /&gt;
    return if team_to_review.nil?&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.create(&lt;br /&gt;
      reviewed_object_id: topic.assignment_id,&lt;br /&gt;
      reviewer_id: reviewer_id,&lt;br /&gt;
      reviewee_id: team_to_review,&lt;br /&gt;
      type: 'ReviewResponseMap'&lt;br /&gt;
    )&lt;br /&gt;
    # error handling&lt;br /&gt;
  rescue StandardError =&amp;gt; e&lt;br /&gt;
    Rails.logger.error(&amp;quot;Failed to assign topic #{topic_id} to reviewer #{reviewer_id}: #{e.message}&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
    raise ArgumentError, 'Topic or assignment is missing' if topic.nil? || topic.assignment_id.nil?&lt;br /&gt;
    raise ArgumentError, 'Matched topics must be a Hash' unless matched_topics.is_a?(Hash)&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
Once we changed them to instance methods, we modified their use in the controller code to instantiate the object first.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-3.-schema.db-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;Schema.db&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We also need to remove the foreign key reference to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model since the &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model already has access to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;, and that is the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; we should be referencing. This will also involve modifying the database to remove the foreign key reference to the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Steps required:&lt;br /&gt;
&lt;br /&gt;
# Review the records in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table in the database to see if there are any records and if those records have values for &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;&lt;br /&gt;
# Create a migration file using the command: &amp;lt;code&amp;gt;rails generate migration RemoveAssignmentFromReviewBids&amp;lt;/code&amp;gt;&lt;br /&gt;
# In the generated file, indicate the change that needs to be made using the change method and migration columns &amp;lt;code&amp;gt;remove_foreign_key&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;remove_column&amp;lt;/code&amp;gt;. Migration should be straightforward since there is no cascading behavior (i.e., &amp;lt;code&amp;gt;dependent:destroy&amp;lt;/code&amp;gt;) to worry about.&lt;br /&gt;
# Once comfortable with the updates to the generated file, run the migration using the &amp;lt;code&amp;gt;rails db:migrate&amp;lt;/code&amp;gt; command.&lt;br /&gt;
# Run a query in the DB to ensure no orphaned records are in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table after the updates.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-4.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implement Edge Case Testing ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| In the previous implementation wiki, there are edge cases which are not exhaustively tested. Should test those edge cases thoroughly and add more edge case testing - link&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span class=&amp;quot;mark&amp;quot;&amp;gt;TODO&amp;lt;/span&amp;gt;:We just got initial test results back. We still need to figure out why tests are failing and what may be slipping through the cracks.&lt;br /&gt;
&lt;br /&gt;
[[File:Test expertiza.png|411x459px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-8-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design Patterns and Principles ===&lt;br /&gt;
Below are the design patterns and principles we plan to use to address the problem statements outlined in the project requirements:&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
** Controller Responsibility&lt;br /&gt;
*** To address the issue of authentication logic residing in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, we will refactor the code to ensure the controller solely handles request and response flows.&lt;br /&gt;
*** Authentication logic will be moved to dedicated methods in &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt;, making the codebase easier to maintain and test.&lt;br /&gt;
*** We will move this logic into helper methods or models, ensuring each component has a single responsibility and improving code readability.&lt;br /&gt;
&lt;br /&gt;
* Separation of Concerns (SoC)&lt;br /&gt;
** By relocating authorization logic out of the controller, we separate authentication from request handling.&lt;br /&gt;
** Adhering to SoC enhances readability and maintainability by allowing each part of the application to focus on its specific role.&lt;br /&gt;
&lt;br /&gt;
* Don't Repeat Yourself (DRY)&lt;br /&gt;
** We will inspect the existing implementation for code duplication, particularly in the controller actions.&lt;br /&gt;
** Any repetitive code will be refactored or removed to reduce redundancy and improve clarity.&lt;br /&gt;
&lt;br /&gt;
* Dependency Inversion Principle (DIP)&lt;br /&gt;
** We plan to update class methods to instance methods, allowing the controller to depend on abstractions rather than concrete implementations.&lt;br /&gt;
&lt;br /&gt;
** Clear and intentional naming during refactoring will enhance code clarity and maintainability.&lt;br /&gt;
&lt;br /&gt;
== Files Modified/Added ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-1.-controllers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
review_bids_controller.rb&lt;br /&gt;
&lt;br /&gt;
signup_sheet_controller.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-2.-models&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
review_bid.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-3.-helpers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
&lt;br /&gt;
authorization_helper.rb&lt;br /&gt;
&lt;br /&gt;
Review_bids_helper.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;viii.-test-plan&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
The Excel spreadsheet below details our test plan for testing the code we changed or created to ensure code coverage. The process involved reviewing the code and determining all the possible scenarios that needed to be covered based on what the method was doing, then creating test cases accordingly.&lt;br /&gt;
&lt;br /&gt;
* [https://docs.google.com/spreadsheets/d/1iHzOdSwlTtxOtf5VsNdwOaCP8NzYMmiJ/edit?usp=drive_link Open Test Matrix Spreadsheet]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/review_bids_controller_spec.rb ReviewBidsController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/sign_up_sheet_controller_spec.rb SignUpSheetController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/models/review_bid_spec.rb ReviewBid model updated test cases]&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-1.-mentor&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Ed Gehringer&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-2.-members&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Members ===&lt;br /&gt;
&lt;br /&gt;
* Gavin Teague&lt;br /&gt;
* Janice Uwujaren&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xi.-links&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
Previous Wiki:  &lt;br /&gt;
[https://wiki.expertiza.ncsu.edu/index.php/CSC/ECE_517_Fall_2021_-_E2151._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;Link&amp;lt;/u&amp;gt;]  &lt;br /&gt;
&lt;br /&gt;
Reference Link for Webservice Details:  &lt;br /&gt;
[https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
Guidance on Using Service Objects in Rails:&lt;br /&gt;
[https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial Rails Service Objects Tutorial]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xii.-references&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/document/d/1LHVacylQmB15I6VcYUA5nuC7GRRU9szHzZhjlPcE-PA/edit?tab=t.0#heading=h.u0oycoj6e0ev &amp;lt;u&amp;gt;Project Instructions&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;appendix-a-uml-class-diagram-plantuml-code-for-review-bids&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Juwujar</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=161053</id>
		<title>CSC/ECE 517 Fall 2024 - E2485. Allow reviewers to bid on what to review</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=161053"/>
		<updated>2024-12-12T06:03:59Z</updated>

		<summary type="html">&lt;p&gt;Juwujar: /* ReviewBidsHelper Updates */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
This project will fix the defects and improve the functionality in Expertiza of the Reviewer Bidding feature. It will make the functionality smoother, more reliable, and align with Expertiza's collaborative learning objectives. &lt;br /&gt;
&lt;br /&gt;
The Review Bidding feature lets reviewers focus on topics they know best or care about most. If someone feels exceptionally qualified or interested in a topic, they can bid to review submissions in that area. This helps ensure reviews are insightful and enjoyable for the reviewer while benefiting the other participants and the overall process.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iii.-problem-statements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Problem Statements ==&lt;br /&gt;
&lt;br /&gt;
Assigning reviews to users on Expertiza involves a complex bidding algorithm that requires optimization before production deployment. Below are key areas we plan to address as per the project requirements:&lt;br /&gt;
* Code Refactoring and Best Practices&lt;br /&gt;
** '''Utilize Authentication Utilities''': Modify the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; function in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to use existing authentication utilities, enhancing code reusability and adhering to the Single Responsibility Principle (SRP) and Separation of Concerns (SoC).&lt;br /&gt;
** '''Eliminate Code Duplication''': Refactor &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; to comply with the Don't Repeat Yourself (DRY) principle.&lt;br /&gt;
** '''Rename Functions for Clarity''': Change &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; to better reflect its purpose.&lt;br /&gt;
** '''Refactor Methods in '''&amp;lt;code&amp;gt;review_bid.rb&amp;lt;/code&amp;gt;''':''': Convert class methods to instance methods or relocate them to helper classes for improved code organization.&lt;br /&gt;
* Functionality Enhancements&lt;br /&gt;
** '''Handle Missing Bids Gracefully''': Adjust the bidding algorithm to accommodate students who do not bid, allowing them to select from the remaining options.&lt;br /&gt;
** '''Support Both Individual and Team Assignments''': Ensure the code functions correctly for participant and team-based assignments when signing up for topics.&lt;br /&gt;
* User Interface Improvements&lt;br /&gt;
** '''Increase Transparency in the Bidding Process''': Display messages indicating how many students are eligible to bid, how many have submitted bids, and the deadline.&lt;br /&gt;
** '''Accurate Bid Status Indicators''': Ensure topic colors change correctly based on the number of outstanding bids, providing clear visual feedback to users.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Exhaustive Edge Case Testing''': Conduct thorough testing for edge cases not previously covered, adding additional test cases as needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iv.-design-goals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Design Goals ==&lt;br /&gt;
&lt;br /&gt;
While addressing the existing defects and enhancing the Reviewer Bidding feature, we plan to adhere to the following design guidelines:&lt;br /&gt;
&lt;br /&gt;
* Code Quality and Maintainability&lt;br /&gt;
** '''Improve Code Structure: '''Implement refactoring strategies to enhance code readability and maintainability, ensuring adherence to best practices like the DRY principle.&lt;br /&gt;
** '''Standardize Authentication Logic: '''Utilize a centralized authentication utility to promote consistency and reusability across the application.&lt;br /&gt;
* System Stability and Flexibility&lt;br /&gt;
** '''Enhance Algorithm Robustness: '''Modify the bidding algorithm to handle scenarios where students do not bid, ensuring the system remains stable under all conditions.&lt;br /&gt;
** '''Ensure Assignment Versatility: '''Design the system to seamlessly support both individual and team assignments without additional configuration.&lt;br /&gt;
* User Experience Enhancements&lt;br /&gt;
** '''Increase Bidding Transparency: '''Develop user interface elements that clearly display bidding status, deadlines, and participation metrics.&lt;br /&gt;
** '''Implement Dynamic Visual Feedback: '''Use real-time visual cues, such as color changes, to inform users about the bidding status instantly.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Develop Comprehensive Test Suites: '''Create extensive automated tests covering edge cases and critical functionalities to guarantee reliable system behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;v.-uml-diagram&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== UML Diagram ==&lt;br /&gt;
&lt;br /&gt;
Below is a visual representation of the models associated with the review bids functionality, depicting the data structures and their relationships.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml class diagram2.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi.-implementation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
'''ReviewBid: '''The &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; manages the bidding process, where participants indicate their preferences for reviewing different topics or assignments. This helps distribute review tasks fairly based on participants' interests.&lt;br /&gt;
&lt;br /&gt;
'''Assignment: '''The &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model represents team or individual assignments in Expertiza. It stores all the essential information required to keep the assignment process organized, like managing participants, setting due dates, forming teams, and overseeing the review process.&lt;br /&gt;
&lt;br /&gt;
'''SignUpTopic: '''The &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt; model identifies specific topics or areas within an assignment for which participants can sign up. It manages the availability of these topics, tracks bids, coordinates team sign-ups, and assigns topics based on both bids and participant availability. &lt;br /&gt;
&lt;br /&gt;
'''Participant: '''The &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model is about the individual users who participate in an assignment. This includes students, instructors, teaching assistants, or other relevant roles. This model is responsible for managing key user information, monitoring interactions with the assignment, keeping track of team memberships, and recording things like badges and review grades.&lt;br /&gt;
&lt;br /&gt;
== Implementation ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1.-decoupling-authorization-logic-from-reviewbidscontroller&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Decoupling Authorization Logic from &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| '''&amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; needs to use the authentication utilities'''&lt;br /&gt;
&lt;br /&gt;
The function &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; should use the available authentication utilities to support the Single Responsibility Principle and Separation of Concerns and to support code reusability.&lt;br /&gt;
&lt;br /&gt;
| '''Authentication Consistency:''' Remove authentication logic in the controller and call logic in the authentication utility class instead to ensure consistent use of the same logic across the application, foster reusability, and eliminate DRY.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
==== Authorization Flow Before Refactoring ====&lt;br /&gt;
The sequence diagram depicts the authorization process within the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;. When a user invokes an action, the controller checks if the action is allowed by verifying the user's role through the &amp;lt;code&amp;gt;current_role_name()&amp;lt;/code&amp;gt; method in the &amp;lt;code&amp;gt;ApplicationController&amp;lt;/code&amp;gt;. Depending on the action and the user's role, the controller either authorizes the user to proceed or denies access, ultimately executing the action logic if authorized and responding to the user.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 1.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We simplified the &amp;lt;code&amp;gt;action_allowed?&amp;lt;/code&amp;gt; method by decoupling authorization logic from the controller to use helper methods in the &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt; module. The &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; should not handle authentication, which is a violation of SRP. &lt;br /&gt;
&lt;br /&gt;
Note: We also made similar updates to the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; methods for &amp;lt;code&amp;gt;SignUpController&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;vertical-align:top;&amp;quot; | Original Code !! style=&amp;quot;vertical-align:top;&amp;quot; | Refactored Code&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index'&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator',&lt;br /&gt;
     'Student'].include? current_role_name and&lt;br /&gt;
        ((%w[list].include? action_name) ? are_needed_authorizations_present?(params[:id], &amp;quot;participant&amp;quot;, &amp;quot;reader&amp;quot;, &amp;quot;submitter&amp;quot;, &amp;quot;reviewer&amp;quot;) : true)&lt;br /&gt;
  else&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator'].include? current_role_name&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index', 'list'&lt;br /&gt;
    current_user_has_student_privileges? &amp;amp;&amp;amp; list_authorization_check&lt;br /&gt;
  else&lt;br /&gt;
    current_user_has_ta_privileges?&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; action ===&lt;br /&gt;
Previously, the &amp;lt;code&amp;gt;ReviewBidsController's index&amp;lt;/code&amp;gt; action was overloaded with responsibilities—it handled everything from locating and validating assignments to fetching review mappings and counting completed reviews. This all-in-one approach made the controller action messy. &lt;br /&gt;
&lt;br /&gt;
We have moved the business logic from the controller action and into dedicated service objects. With this change, we introduced two key service objects: &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; focuses on retrieving the participant along with their assignments, while the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; takes on the task of managing review mappings and calculating review-related stats, like how many reviews have been completed. &lt;br /&gt;
&lt;br /&gt;
This separation of responsibilities aligns with the Single Responsibility Principle. The logic for fetching assignments, retrieving review mappings, and counting reviews is now more flexible and reusable throughout the application. For example, we used methods in the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; in both the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; and the &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;, helping to avoid code duplication.The article [https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial Rails Service Objects Tutorial] served as a guide on when and how to use service objects in Rails.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
  return unless current_user_id?(@participant.user_id)&lt;br /&gt;
  @assignment = @participant.assignment&lt;br /&gt;
  @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
  # Finding how many reviews have been completed&lt;br /&gt;
  @num_reviews_completed = 0&lt;br /&gt;
  @review_mappings.each do |map|&lt;br /&gt;
    @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # render view for completing reviews after review bidding has been completed&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @assignment = participant_service.assignment&lt;br /&gt;
  unless @assignment.is_a?(Assignment)&lt;br /&gt;
    flash[:error] = 'Assignment not found.'&lt;br /&gt;
    redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  @review_mappings = review_service.review_mappings&lt;br /&gt;
  @num_reviews_completed = review_service.review_counts[:completed]&lt;br /&gt;
&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Optimize &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Action functionality ===&lt;br /&gt;
In the earlier version of the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, the server only needed a couple of key pieces of information: an array of topic IDs (params[:topic]) and the participant ID (params[:id}). From there, it would figure out the assignment_id by using the first topic as a reference. This is risky because the &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt; associated with the topic may not be a valid &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
With the new implementation, we structured the parameters to include the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt;, an array of topic IDs, and &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;. This change ensures that the server always receives complete and reliable information, preventing issues caused by missing or inconsistent data. It also removes the need for the server to perform unnecessary lookups, as in the case of &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;, allowing the controller to focus on receiving requests and rendering appropriate responses.&lt;br /&gt;
&lt;br /&gt;
While these updates have cleaned up the set_priority action, they still contain a lot of business logic, like validations and priority updates. To make the code even more maintainable, there’s room for further refactoring—perhaps moving some of this logic into a service class or model method to streamline the controller even more.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | Old Code&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    if params[:topic].nil?&lt;br /&gt;
      ReviewBid.where(participant_id: params[:id]).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      participant = AssignmentParticipant.find_by(id: params[:id])&lt;br /&gt;
      assignment_id = SignUpTopic.find(params[:topic].first).assignment.id&lt;br /&gt;
      @bids = ReviewBid.where(participant_id: params[:id])&lt;br /&gt;
      signed_up_topics = ReviewBid.where(participant_id: params[:id]).map(&amp;amp;:signuptopic_id)&lt;br /&gt;
      signed_up_topics -= params[:topic].map(&amp;amp;:to_i)&lt;br /&gt;
      signed_up_topics.each do |topic|&lt;br /&gt;
        ReviewBid.where(signuptopic_id: topic, participant_id: params[:id]).destroy_all&lt;br /&gt;
      end&lt;br /&gt;
      params[:topic].each_with_index do |topic_id, index|&lt;br /&gt;
        bid_existence = ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id])&lt;br /&gt;
        if bid_existence.empty?&lt;br /&gt;
          ReviewBid.create(priority: index + 1, signuptopic_id: topic_id, participant_id: params[:id], assignment_id: assignment_id)&lt;br /&gt;
        else&lt;br /&gt;
          ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id]).update_all(priority: index + 1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to action: 'show', assignment_id: params[:assignment_id], id: params[:id]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    participant_id = params[:participant_id].to_i&lt;br /&gt;
    selected_topic_ids = params[:topic]&amp;amp;.map(&amp;amp;:to_i) || []&lt;br /&gt;
    assignment_id = params[:assignment_id].to_i&lt;br /&gt;
&lt;br /&gt;
    unless current_user_can_modify_participant?(participant_id)&lt;br /&gt;
      flash[:error] = 'You are not authorized to modify these bids.'&lt;br /&gt;
      respond_to do |format|&lt;br /&gt;
        format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
        format.json { render json: { status: 'unauthorized' }, status: :unauthorized }&lt;br /&gt;
      end&lt;br /&gt;
      return&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    if selected_topic_ids.empty?&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      assignment = Assignment.find_by(id: assignment_id)&lt;br /&gt;
      if assignment.nil?&lt;br /&gt;
        flash[:error] = &amp;quot;Invalid assignment.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_assignment' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      unless SignUpTopic.where(id: selected_topic_ids, assignment_id: assignment_id).count == selected_topic_ids.size&lt;br /&gt;
        flash[:error] = &amp;quot;One or more selected topics are invalid.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_topics' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).where.not(signuptopic_id: selected_topic_ids).destroy_all&lt;br /&gt;
&lt;br /&gt;
      selected_topic_ids.each_with_index do |topic_id, index|&lt;br /&gt;
        bid = ReviewBid.find_or_initialize_by(signuptopic_id: topic_id, participant_id: participant_id)&lt;br /&gt;
        bid.priority = index + 1&lt;br /&gt;
        bid.assignment_id = assignment_id&lt;br /&gt;
        bid.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_to action: 'show', assignment_id: assignment_id, id: participant_id, notice: 'Review bids updated successfully.' }&lt;br /&gt;
      format.json { render json: { status: 'success' }, status: :ok }&lt;br /&gt;
    end&lt;br /&gt;
  rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
    flash[:error] = &amp;quot;Failed to update priorities: #{e.message}&amp;quot;&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
      format.json { render json: { status: 'error', message: e.message }, status: :unprocessable_entity }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Client Side Changes to Support &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Changes ====&lt;br /&gt;
We refactored the client-side code to ensure it passed the correct information from the view to the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action. On the client side, we now grab the topicIDs and send them as &amp;lt;code&amp;gt;selected_topic_ids&amp;lt;/code&amp;gt;, allowing the controller to figure out which topics were selected and in what order.  &lt;br /&gt;
&lt;br /&gt;
When working with jQuery, AJAX is awesome because it lets us update the server without reloading the page, making the user experience smooth and responsive. When we pull the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, we can grab it along with other data, like the list of topics, and include it as a single request. This way, we don’t have to rely on the URL to know which participant we’re working with, which makes the code cleaner.&lt;br /&gt;
&lt;br /&gt;
We also added an update handler so that any time the order of the selected topic changes in the view, information is sent to the appropriate controller action. This ensures that the controller and the view remain in sync, with real-time updates, and includes a mechanism for providing feedback if something goes wrong.&lt;br /&gt;
&lt;br /&gt;
Note: One interesting aspect of the application was the mix of JavaScript and CoffeeScript code. It wasn't clear why both types of code were used.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery -&amp;gt;&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: -&amp;gt;&lt;br /&gt;
      $.post($(this).data('update-url'), $(this).sortable('serialize'))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery(document).ready ($) -&amp;gt;&lt;br /&gt;
  sendUpdatedOrder = -&amp;gt;&lt;br /&gt;
    sortedIDs = $(this).sortable('toArray')&lt;br /&gt;
&lt;br /&gt;
    topicIDs = sortedIDs.map (id) -&amp;gt;&lt;br /&gt;
      id.replace 'topic_', ''&lt;br /&gt;
&lt;br /&gt;
    participant_id = $(this).data('participant-id')&lt;br /&gt;
&lt;br /&gt;
    assignment_id = new URL($(this).data('update-url'), window.location.origin).searchParams.get('assignment_id')&lt;br /&gt;
&lt;br /&gt;
    $.ajax&lt;br /&gt;
      type: 'POST'&lt;br /&gt;
      url: $(this).data('update-url')&lt;br /&gt;
      data:&lt;br /&gt;
        topic: topicIDs&lt;br /&gt;
        participant_id: participant_id&lt;br /&gt;
        assignment_id: assignment_id&lt;br /&gt;
      success: (response) -&amp;gt;&lt;br /&gt;
        console.log 'Selections updated successfully.'&lt;br /&gt;
      error: (xhr, status, error) -&amp;gt;&lt;br /&gt;
        alert 'An error occurred while saving your selections. Please try again.'&lt;br /&gt;
        console.error error&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: sendUpdatedOrder&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-1.-signupsheetcontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; Updates ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; method renders the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; action However, the view is associated with the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # provides variables for reviewing page located at views/review_bids/others_work.html.erb&lt;br /&gt;
  def index&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
    return unless current_user_id?(@participant.user_id)&lt;br /&gt;
&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
    # Finding how many reviews have been completed&lt;br /&gt;
    @num_reviews_completed = 0&lt;br /&gt;
    @review_mappings.each do |map|&lt;br /&gt;
      @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # render view for completing reviews after review bidding has been completed&lt;br /&gt;
    render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
  end  &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-2.-reviewbidsotherswork-view-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;review_bids_others_work.html.erb&amp;lt;/code&amp;gt; View Updates ====&lt;br /&gt;
The view is technically part of the SignUpSheetController, but it's being rendered by the ReviewBidsController, which initially threw us off. After chatting with the professor, we decided not to change this view. He indicated that the plan is to reuse these views for both assignment and review bidding.&lt;br /&gt;
&lt;br /&gt;
We suggest building the assignment bidding and review bidding features separately first to ensure each one works independently. Once that’s done, we can clean things up by combining any shared functionality. This could include creating shared views or even introducing an abstract model to handle both types of bidding more efficiently. It could also mean replacing the ReviewBidsController with something more generic, like a BidsController, to keep things reusable and easier to maintain down the line.&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Rename &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; method ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; should be &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt;&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:''' Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The primary activity here is to refactor the method name &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; and fix the hardcoded URL. However, the match topic bidding algorithm no longer works since Heroku is no longer free, per the professor. The reference link below has detailed information about the web service that we can leverage to gain that understanding.&lt;br /&gt;
&lt;br /&gt;
Reference Link for Webservice Details: [https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Updated Implementation ====&lt;br /&gt;
The original requirement was to rename the run_bidding_algorithm method to assign_reviewers, but after digging into the code, we realized that renaming it didn’t make sense. The method runs the bidding algorithm to match topics, and there’s already a separate method to handle assigning review topics.&lt;br /&gt;
&lt;br /&gt;
However, we improved the logic in the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; method (which calls the private &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt;) by introducing a separate &amp;lt;code&amp;gt;BiddingAlgorithmService&amp;lt;/code&amp;gt; object. This keeps the business logic out of the controller action, which makes things cleaner and more maintainable.&lt;br /&gt;
&lt;br /&gt;
==== review_bids_controller ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def run_bidding_algorithm&lt;br /&gt;
    review_bid = ReviewBid.new&lt;br /&gt;
    bidding_data = review_bid.bidding_data&lt;br /&gt;
    matched_topics = BiddingAlgorithmService.new(bidding_data).run&lt;br /&gt;
    raise ArgumentError, 'Failed to assign reviewers. Please try again later.' unless matched_topics&lt;br /&gt;
&lt;br /&gt;
    matched_topics&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== bidding_algorithm_service ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# frozen_string_literal: true&lt;br /&gt;
&lt;br /&gt;
# The `BiddingAlgorithmService` run the bid assignment algorithm&lt;br /&gt;
# Sends student IDs, topic IDs, student preferences, and timestamps to the web service&lt;br /&gt;
# The web service returns the matched assignments in the JSON response body&lt;br /&gt;
class BiddingAlgorithmService&lt;br /&gt;
  SERVICE_URL = 'http://app-csc517.herokuapp.com/match_topics'.freeze&lt;br /&gt;
&lt;br /&gt;
  # MOCK_DATA is based on the documentation detailing how the webservice behaves.&lt;br /&gt;
  # Reference: https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review#Webservice&lt;br /&gt;
  MOCK_DATA = {&lt;br /&gt;
    36239 =&amp;gt; [3970, 3972, 3975],&lt;br /&gt;
    36240 =&amp;gt; [3973, 3974, 3972],&lt;br /&gt;
    36241 =&amp;gt; [3969, 3971, 3972],&lt;br /&gt;
    36242 =&amp;gt; [3969, 3971, 3973],&lt;br /&gt;
    36243 =&amp;gt; [3969, 3970, 3971]&lt;br /&gt;
  }.freeze&lt;br /&gt;
&lt;br /&gt;
  def initialize(bidding_data)&lt;br /&gt;
    @bidding_data = bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def run&lt;br /&gt;
    return MOCK_DATA if Rails.application.config.use_mock_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
    perform_request&lt;br /&gt;
  rescue RestClient::ExceptionWithResponse, JSON::ParserError&lt;br /&gt;
    false&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def perform_request&lt;br /&gt;
    response = RestClient.post(&lt;br /&gt;
      SERVICE_URL,&lt;br /&gt;
      @bidding_data.to_json,&lt;br /&gt;
      content_type: :json,&lt;br /&gt;
      accept: :json&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    validate_response(response)&lt;br /&gt;
    JSON.parse(response.body)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def validate_response(response)&lt;br /&gt;
    raise 'Invalid response format' unless response.headers[:content_type] &amp;amp;&amp;amp; response.headers[:content_type].include?('application/json')&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Resolve Functionality Issues for Non Bidders ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Currently it doesn't work if some student does not bid. In this case, algorithm needs to be fixed to ignore anyone who didn’t bid, and let them choose from what’s left over.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to do several things within this controller. We will set a default state for non-bidders that we can point to to give them options to select topics to review from the leftovers. We will consolidate &amp;lt;code&amp;gt;@sign_up_topics&amp;lt;/code&amp;gt; to resolve the DRY violation. Lastly, we will remove business logic from the show method and push it to a new helper to conform to the SRP.&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # Additional method&lt;br /&gt;
&lt;br /&gt;
  # Provides variables for review bidding page&lt;br /&gt;
  def show&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id].to_i)&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @sign_up_topics = SignUpTopic.where(assignment_id: @assignment.id, private_to: nil)&lt;br /&gt;
    my_topic = SignedUpTeam.topic_id(@participant.parent_id, @participant.user_id)&lt;br /&gt;
    @sign_up_topics -= SignUpTopic.where(assignment_id: @assignment.id, id: my_topic)&lt;br /&gt;
    @num_participants = AssignmentParticipant.where(parent_id: @assignment.id).count&lt;br /&gt;
    @selected_topics = nil # This is used to list the topics assigned to review (i.e., select == assigned)&lt;br /&gt;
    @bids = ReviewBid.where(participant_id: @participant, assignment_id: @assignment.id)&lt;br /&gt;
    signed_up_topics = []&lt;br /&gt;
    &lt;br /&gt;
    @bids.each do |bid|&lt;br /&gt;
      sign_up_topic = SignUpTopic.find_by(id: bid.signuptopic_id)&lt;br /&gt;
      signed_up_topics &amp;lt;&amp;lt; sign_up_topic if sign_up_topic&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    signed_up_topics &amp;amp;= @sign_up_topics&lt;br /&gt;
    @sign_up_topics -= signed_up_topics&lt;br /&gt;
    @bids = signed_up_topics&lt;br /&gt;
    @num_of_topics = @sign_up_topics.size&lt;br /&gt;
    @assigned_review_maps = []&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: @assignment.id, reviewer_id: @participant.id).each do |review_map|&lt;br /&gt;
      @assigned_review_maps &amp;lt;&amp;lt; review_map&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Explicitly render view since it's in the sign up sheet views&lt;br /&gt;
    render 'sign_up_sheet/review_bids_show'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Additional methods&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-2.-reviewbidshelper-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
 # Assigns bidding topics to reviewers&lt;br /&gt;
  def assign_bidding&lt;br /&gt;
      assignment = validate_assignment(params[:assignment_id])&lt;br /&gt;
&lt;br /&gt;
      reviewers = validate_reviewers(assignment.id)&lt;br /&gt;
      reviewer_ids = reviewers.map(&amp;amp;:id)&lt;br /&gt;
&lt;br /&gt;
      matched_topics = run_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
      if matched_topics.blank?&lt;br /&gt;
        flash[:alert] = 'Topic or assignment is missing'&lt;br /&gt;
        redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      leftover_topics = find_leftover_topics(assignment.id, matched_topics)&lt;br /&gt;
      assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.new.assign_review_topics(matched_topics)&lt;br /&gt;
      assignment.update!(can_choose_topic_to_review: false)&lt;br /&gt;
&lt;br /&gt;
      flash[:notice] = 'Reviewers were successfully assigned to topics.'&lt;br /&gt;
      redirect_back fallback_location: root_path&lt;br /&gt;
    rescue ArgumentError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ArgumentError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: e.message&lt;br /&gt;
    rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::RecordInvalid: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue ActiveRecord::ActiveRecordError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::ActiveRecordError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue StandardError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;StandardError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers. Please try again later.'&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  #service methods here ...&lt;br /&gt;
&lt;br /&gt;
  def find_leftover_topics(assignment_id, matched_topics)&lt;br /&gt;
    all_topic_ids = SignUpTopic.where(assignment_id: assignment_id).pluck(:id)&lt;br /&gt;
    assigned_topic_ids = matched_topics.map { |match| match[:topic_id] }&lt;br /&gt;
&lt;br /&gt;
    # Calculate leftover topics&lt;br /&gt;
    all_topic_ids - assigned_topic_ids&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
    return if leftover_topics.blank?&lt;br /&gt;
&lt;br /&gt;
    # Find non-bidders by excluding already matched reviewers&lt;br /&gt;
    non_bidders = reviewer_ids - matched_topics.keys&lt;br /&gt;
&lt;br /&gt;
    # Assign leftover topics to non-bidders in a round-robin fashion&lt;br /&gt;
    non_bidders.each_with_index do |reviewer_id, index|&lt;br /&gt;
      topic_id = leftover_topics[index % leftover_topics.length]&lt;br /&gt;
      ReviewBid.create(priority: 1, signuptopic_id: topic_id, participant_id: reviewer_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirm Functionality for Single-person Teams ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Make sure your code works on individual assignments, i.e., assignments where participants sign up for topics instead of teams. In this case, a team is created for each participant when they sign up. So the code should work for assignments to which either individuals or teams submit.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to make an update to focus on topic rather than team or have it where teams are updated based on topic id.We will probably utilize sign up topic or a similar object to accomplish this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-5-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bid Status Messaging ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| To make the functionality more intuitive, include a message to say how many students are eligible to submit bids, how many have submitted their bids, when the deadline for submitting bids is.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
The logic for these messages will go into the helper file. It is effectively the same reasoning as our migration of other business/functionality code from the controller to the helper to conform to the SRP. It will go hand in hand with the SignUpTopic portion of the code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-6-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Why are the methods in review_bid.rb class methods? Can we change them to instance methods or move it to helpers?&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-1.-reviewbid-model-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model Updates ====&lt;br /&gt;
Since the current class methods are tied to &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model instances, we will change them to instance methods. There is no need to move them to helpers.&lt;br /&gt;
&lt;br /&gt;
===== Review Bid Model Flow Before Refactoring =====&lt;br /&gt;
This sequence diagram describes how the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; interacts with the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model in assigning review topics to reviewers according to their bids. Once the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; action is initiated by the user, the controller gathers the IDs of reviewers and calls &amp;lt;code&amp;gt;ReviewBid.bidding_data&amp;lt;/code&amp;gt; to gather bidding information. That is sent out to an external bidding algorithm, and the matched topics that come back are used by &amp;lt;code&amp;gt;ReviewBid.assign_review_topics&amp;lt;/code&amp;gt; to create &amp;lt;code&amp;gt;ReviewResponseMap&amp;lt;/code&amp;gt; entries, assigning topics to reviewers for the assignment.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
  belongs_to :assignment, class_name: 'Assignment'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  def self.bidding_data(assignment_id, reviewer_ids)&lt;br /&gt;
    # create basic hash and set basic hash data&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'users' =&amp;gt; {}, 'max_accepted_proposals' =&amp;gt; [] }&lt;br /&gt;
    bidding_data['tid'] = SignUpTopic.where(assignment_id: assignment_id).ids&lt;br /&gt;
    bidding_data['max_accepted_proposals'] = Assignment.where(id: assignment_id).pluck(:num_reviews_allowed).first&lt;br /&gt;
&lt;br /&gt;
    # loop through reviewer_ids to get reviewer specific bidding data&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      bidding_data['users'][reviewer_id] = reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # assigns topics to reviews as matched by the webservice algorithm&lt;br /&gt;
  def self.assign_review_topics(assignment_id, reviewer_ids, matched_topics, _min_num_reviews = 2)&lt;br /&gt;
    # if review response map already created, delete it&lt;br /&gt;
    if ReviewResponseMap.where(reviewed_object_id: assignment_id)&lt;br /&gt;
      ReviewResponseMap.where(reviewed_object_id: assignment_id).destroy_all&lt;br /&gt;
    end&lt;br /&gt;
    # loop through reviewer_ids to assign reviews to each reviewer&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      topics_to_assign = matched_topics[reviewer_id.to_s]&lt;br /&gt;
      topics_to_assign.each do |topic|&lt;br /&gt;
        assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method to assign a single topic to a reviewer&lt;br /&gt;
  def self.assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic).pluck(:team_id).first&lt;br /&gt;
    team_to_review.nil? ? [] : ReviewResponseMap.create(reviewed_object_id: assignment_id, reviewer_id: reviewer_id, reviewee_id: team_to_review, type: 'ReviewResponseMap')&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method for getting individual reviewer_ids bidding data&lt;br /&gt;
  # returns user's bidding data hash&lt;br /&gt;
  def self.reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    reviewer_user_id = AssignmentParticipant.find(reviewer_id).user_id&lt;br /&gt;
    self_topic = SignedUpTeam.topic_id(assignment_id, reviewer_user_id)&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'otid' =&amp;gt; self_topic, 'priority' =&amp;gt; [], 'time' =&amp;gt; [] }&lt;br /&gt;
    bids = ReviewBid.where(participant_id: reviewer_id)&lt;br /&gt;
&lt;br /&gt;
    # loop through each bid for a topic to get specific data&lt;br /&gt;
    bids.each do |bid|&lt;br /&gt;
      bidding_data['tid'] &amp;lt;&amp;lt; bid.signuptopic_id&lt;br /&gt;
      bidding_data['priority'] &amp;lt;&amp;lt; bid.priority&lt;br /&gt;
      bidding_data['time'] &amp;lt;&amp;lt; bid.updated_at&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  # Instance method to get bidding data for this review bid&lt;br /&gt;
  def bidding_data&lt;br /&gt;
    {&lt;br /&gt;
      'tid' =&amp;gt; topic_ids,&lt;br /&gt;
      'otid' =&amp;gt; self_topic,&lt;br /&gt;
      'priority' =&amp;gt; priorities,&lt;br /&gt;
      'time' =&amp;gt; updated_times&lt;br /&gt;
    }&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # bid object to limit db touches for similar data acquisitions&lt;br /&gt;
  def bids&lt;br /&gt;
    @bids ||= ReviewBid.where(participant_id: participant_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def topic_ids&lt;br /&gt;
    bids.pluck(:signuptopic_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def priorities&lt;br /&gt;
    bids.pluck(:priority)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def updated_times&lt;br /&gt;
    bids.pluck(:updated_at)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self_topic&lt;br /&gt;
    return nil unless topic &amp;amp;&amp;amp; topic.assignment_id&lt;br /&gt;
&lt;br /&gt;
    SignedUpTeam.topic_id(topic.assignment_id, participant.user_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign topics to reviews&lt;br /&gt;
  def assign_review_topics(matched_topics)&lt;br /&gt;
    validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: topic.assignment_id).destroy_all&lt;br /&gt;
&lt;br /&gt;
    matched_topics.each do |reviewer_id, topics|&lt;br /&gt;
      Array(topics).each { |topic_id| assign_topic_to_reviewer(reviewer_id, topic_id) }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign a single topic to a reviewer&lt;br /&gt;
  def assign_topic_to_reviewer(reviewer_id, topic_id)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic_id).pluck(:team_id).first&lt;br /&gt;
    return if team_to_review.nil?&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.create(&lt;br /&gt;
      reviewed_object_id: topic.assignment_id,&lt;br /&gt;
      reviewer_id: reviewer_id,&lt;br /&gt;
      reviewee_id: team_to_review,&lt;br /&gt;
      type: 'ReviewResponseMap'&lt;br /&gt;
    )&lt;br /&gt;
    # error handling&lt;br /&gt;
  rescue StandardError =&amp;gt; e&lt;br /&gt;
    Rails.logger.error(&amp;quot;Failed to assign topic #{topic_id} to reviewer #{reviewer_id}: #{e.message}&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
    raise ArgumentError, 'Topic or assignment is missing' if topic.nil? || topic.assignment_id.nil?&lt;br /&gt;
    raise ArgumentError, 'Matched topics must be a Hash' unless matched_topics.is_a?(Hash)&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
Once we changed them to instance methods, we modified their use in the controller code to instantiate the object first.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-3.-schema.db-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;Schema.db&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We also need to remove the foreign key reference to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model since the &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model already has access to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;, and that is the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; we should be referencing. This will also involve modifying the database to remove the foreign key reference to the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Steps required:&lt;br /&gt;
&lt;br /&gt;
# Review the records in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table in the database to see if there are any records and if those records have values for &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;&lt;br /&gt;
# Create a migration file using the command: &amp;lt;code&amp;gt;rails generate migration RemoveAssignmentFromReviewBids&amp;lt;/code&amp;gt;&lt;br /&gt;
# In the generated file, indicate the change that needs to be made using the change method and migration columns &amp;lt;code&amp;gt;remove_foreign_key&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;remove_column&amp;lt;/code&amp;gt;. Migration should be straightforward since there is no cascading behavior (i.e., &amp;lt;code&amp;gt;dependent:destroy&amp;lt;/code&amp;gt;) to worry about.&lt;br /&gt;
# Once comfortable with the updates to the generated file, run the migration using the &amp;lt;code&amp;gt;rails db:migrate&amp;lt;/code&amp;gt; command.&lt;br /&gt;
# Run a query in the DB to ensure no orphaned records are in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table after the updates.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-4.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implement Edge Case Testing ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| In the previous implementation wiki, there are edge cases which are not exhaustively tested. Should test those edge cases thoroughly and add more edge case testing - link&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span class=&amp;quot;mark&amp;quot;&amp;gt;TODO&amp;lt;/span&amp;gt;:We just got initial test results back. We still need to figure out why tests are failing and what may be slipping through the cracks.&lt;br /&gt;
&lt;br /&gt;
[[File:Test expertiza.png|411x459px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-8-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design Patterns and Principles ===&lt;br /&gt;
Below are the design patterns and principles we plan to use to address the problem statements outlined in the project requirements:&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
** Controller Responsibility&lt;br /&gt;
*** To address the issue of authentication logic residing in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, we will refactor the code to ensure the controller solely handles request and response flows.&lt;br /&gt;
*** Authentication logic will be moved to dedicated methods in &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt;, making the codebase easier to maintain and test.&lt;br /&gt;
** View Simplification&lt;br /&gt;
*** Currently, the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; view contains excessive business logic, violating SRP.&lt;br /&gt;
*** We will move this logic into helper methods or models, ensuring each component has a single responsibility and improving code readability.&lt;br /&gt;
&lt;br /&gt;
* Separation of Concerns (SoC)&lt;br /&gt;
** By relocating authorization logic out of the controller, we separate authentication from request handling.&lt;br /&gt;
** Adhering to SoC enhances readability and maintainability by allowing each part of the application to focus on its specific role.&lt;br /&gt;
* Don't Repeat Yourself (DRY)&lt;br /&gt;
** We will inspect the existing implementation for code duplication, particularly in the controller actions.&lt;br /&gt;
** Any repetitive code will be refactored or removed to reduce redundancy and improve clarity.&lt;br /&gt;
&lt;br /&gt;
* Model-View-Controller (MVC) Adherence&lt;br /&gt;
** Moving business logic from the view aligns with MVC best practices.&lt;br /&gt;
** By ensuring the view only presents data without processing it, we maintain clear boundaries between the model, view, and controller layers.&lt;br /&gt;
&lt;br /&gt;
* Dependency Inversion Principle (DIP)&lt;br /&gt;
** We plan to update class methods to instance methods, allowing the controller to depend on abstractions rather than concrete implementations.&lt;br /&gt;
** This change leverages Ruby's duck typing, providing flexibility and making the code easier to maintain and test.&lt;br /&gt;
&lt;br /&gt;
** Clear and intentional naming during refactoring will enhance code clarity and maintainability.&lt;br /&gt;
&lt;br /&gt;
== Files Modified/Added ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-1.-controllers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
review_bids_controller.rb&lt;br /&gt;
&lt;br /&gt;
signup_sheet_controller.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-2.-models&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
review_bid.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-3.-helpers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
&lt;br /&gt;
authorization_helper.rb&lt;br /&gt;
&lt;br /&gt;
Review_bids_helper.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;viii.-test-plan&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
The Excel spreadsheet below details our test plan for testing the code we changed or created to ensure code coverage. The process involved reviewing the code and determining all the possible scenarios that needed to be covered based on what the method was doing, then creating test cases accordingly.&lt;br /&gt;
&lt;br /&gt;
* [https://docs.google.com/spreadsheets/d/1iHzOdSwlTtxOtf5VsNdwOaCP8NzYMmiJ/edit?usp=drive_link Open Test Matrix Spreadsheet]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/review_bids_controller_spec.rb ReviewBidsController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/sign_up_sheet_controller_spec.rb SignUpSheetController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/models/review_bid_spec.rb ReviewBid model updated test cases]&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-1.-mentor&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Ed Gehringer&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-2.-members&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Members ===&lt;br /&gt;
&lt;br /&gt;
* Gavin Teague&lt;br /&gt;
* Janice Uwujaren&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xi.-links&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
Previous Wiki:  &lt;br /&gt;
[https://wiki.expertiza.ncsu.edu/index.php/CSC/ECE_517_Fall_2021_-_E2151._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;Link&amp;lt;/u&amp;gt;]  &lt;br /&gt;
&lt;br /&gt;
Reference Link for Webservice Details:  &lt;br /&gt;
[https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
Guidance on Using Service Objects in Rails:&lt;br /&gt;
[https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial Rails Service Objects Tutorial]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xii.-references&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/document/d/1LHVacylQmB15I6VcYUA5nuC7GRRU9szHzZhjlPcE-PA/edit?tab=t.0#heading=h.u0oycoj6e0ev &amp;lt;u&amp;gt;Project Instructions&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;appendix-a-uml-class-diagram-plantuml-code-for-review-bids&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Juwujar</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=161052</id>
		<title>CSC/ECE 517 Fall 2024 - E2485. Allow reviewers to bid on what to review</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=161052"/>
		<updated>2024-12-12T06:03:23Z</updated>

		<summary type="html">&lt;p&gt;Juwujar: /* Links */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
This project will fix the defects and improve the functionality in Expertiza of the Reviewer Bidding feature. It will make the functionality smoother, more reliable, and align with Expertiza's collaborative learning objectives. &lt;br /&gt;
&lt;br /&gt;
The Review Bidding feature lets reviewers focus on topics they know best or care about most. If someone feels exceptionally qualified or interested in a topic, they can bid to review submissions in that area. This helps ensure reviews are insightful and enjoyable for the reviewer while benefiting the other participants and the overall process.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iii.-problem-statements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Problem Statements ==&lt;br /&gt;
&lt;br /&gt;
Assigning reviews to users on Expertiza involves a complex bidding algorithm that requires optimization before production deployment. Below are key areas we plan to address as per the project requirements:&lt;br /&gt;
* Code Refactoring and Best Practices&lt;br /&gt;
** '''Utilize Authentication Utilities''': Modify the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; function in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to use existing authentication utilities, enhancing code reusability and adhering to the Single Responsibility Principle (SRP) and Separation of Concerns (SoC).&lt;br /&gt;
** '''Eliminate Code Duplication''': Refactor &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; to comply with the Don't Repeat Yourself (DRY) principle.&lt;br /&gt;
** '''Rename Functions for Clarity''': Change &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; to better reflect its purpose.&lt;br /&gt;
** '''Refactor Methods in '''&amp;lt;code&amp;gt;review_bid.rb&amp;lt;/code&amp;gt;''':''': Convert class methods to instance methods or relocate them to helper classes for improved code organization.&lt;br /&gt;
* Functionality Enhancements&lt;br /&gt;
** '''Handle Missing Bids Gracefully''': Adjust the bidding algorithm to accommodate students who do not bid, allowing them to select from the remaining options.&lt;br /&gt;
** '''Support Both Individual and Team Assignments''': Ensure the code functions correctly for participant and team-based assignments when signing up for topics.&lt;br /&gt;
* User Interface Improvements&lt;br /&gt;
** '''Increase Transparency in the Bidding Process''': Display messages indicating how many students are eligible to bid, how many have submitted bids, and the deadline.&lt;br /&gt;
** '''Accurate Bid Status Indicators''': Ensure topic colors change correctly based on the number of outstanding bids, providing clear visual feedback to users.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Exhaustive Edge Case Testing''': Conduct thorough testing for edge cases not previously covered, adding additional test cases as needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iv.-design-goals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Design Goals ==&lt;br /&gt;
&lt;br /&gt;
While addressing the existing defects and enhancing the Reviewer Bidding feature, we plan to adhere to the following design guidelines:&lt;br /&gt;
&lt;br /&gt;
* Code Quality and Maintainability&lt;br /&gt;
** '''Improve Code Structure: '''Implement refactoring strategies to enhance code readability and maintainability, ensuring adherence to best practices like the DRY principle.&lt;br /&gt;
** '''Standardize Authentication Logic: '''Utilize a centralized authentication utility to promote consistency and reusability across the application.&lt;br /&gt;
* System Stability and Flexibility&lt;br /&gt;
** '''Enhance Algorithm Robustness: '''Modify the bidding algorithm to handle scenarios where students do not bid, ensuring the system remains stable under all conditions.&lt;br /&gt;
** '''Ensure Assignment Versatility: '''Design the system to seamlessly support both individual and team assignments without additional configuration.&lt;br /&gt;
* User Experience Enhancements&lt;br /&gt;
** '''Increase Bidding Transparency: '''Develop user interface elements that clearly display bidding status, deadlines, and participation metrics.&lt;br /&gt;
** '''Implement Dynamic Visual Feedback: '''Use real-time visual cues, such as color changes, to inform users about the bidding status instantly.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Develop Comprehensive Test Suites: '''Create extensive automated tests covering edge cases and critical functionalities to guarantee reliable system behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;v.-uml-diagram&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== UML Diagram ==&lt;br /&gt;
&lt;br /&gt;
Below is a visual representation of the models associated with the review bids functionality, depicting the data structures and their relationships.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml class diagram2.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi.-implementation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
'''ReviewBid: '''The &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; manages the bidding process, where participants indicate their preferences for reviewing different topics or assignments. This helps distribute review tasks fairly based on participants' interests.&lt;br /&gt;
&lt;br /&gt;
'''Assignment: '''The &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model represents team or individual assignments in Expertiza. It stores all the essential information required to keep the assignment process organized, like managing participants, setting due dates, forming teams, and overseeing the review process.&lt;br /&gt;
&lt;br /&gt;
'''SignUpTopic: '''The &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt; model identifies specific topics or areas within an assignment for which participants can sign up. It manages the availability of these topics, tracks bids, coordinates team sign-ups, and assigns topics based on both bids and participant availability. &lt;br /&gt;
&lt;br /&gt;
'''Participant: '''The &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model is about the individual users who participate in an assignment. This includes students, instructors, teaching assistants, or other relevant roles. This model is responsible for managing key user information, monitoring interactions with the assignment, keeping track of team memberships, and recording things like badges and review grades.&lt;br /&gt;
&lt;br /&gt;
== Implementation ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1.-decoupling-authorization-logic-from-reviewbidscontroller&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Decoupling Authorization Logic from &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| '''&amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; needs to use the authentication utilities'''&lt;br /&gt;
&lt;br /&gt;
The function &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; should use the available authentication utilities to support the Single Responsibility Principle and Separation of Concerns and to support code reusability.&lt;br /&gt;
&lt;br /&gt;
| '''Authentication Consistency:''' Remove authentication logic in the controller and call logic in the authentication utility class instead to ensure consistent use of the same logic across the application, foster reusability, and eliminate DRY.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
==== Authorization Flow Before Refactoring ====&lt;br /&gt;
The sequence diagram depicts the authorization process within the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;. When a user invokes an action, the controller checks if the action is allowed by verifying the user's role through the &amp;lt;code&amp;gt;current_role_name()&amp;lt;/code&amp;gt; method in the &amp;lt;code&amp;gt;ApplicationController&amp;lt;/code&amp;gt;. Depending on the action and the user's role, the controller either authorizes the user to proceed or denies access, ultimately executing the action logic if authorized and responding to the user.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 1.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We simplified the &amp;lt;code&amp;gt;action_allowed?&amp;lt;/code&amp;gt; method by decoupling authorization logic from the controller to use helper methods in the &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt; module. The &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; should not handle authentication, which is a violation of SRP. &lt;br /&gt;
&lt;br /&gt;
Note: We also made similar updates to the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; methods for &amp;lt;code&amp;gt;SignUpController&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;vertical-align:top;&amp;quot; | Original Code !! style=&amp;quot;vertical-align:top;&amp;quot; | Refactored Code&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index'&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator',&lt;br /&gt;
     'Student'].include? current_role_name and&lt;br /&gt;
        ((%w[list].include? action_name) ? are_needed_authorizations_present?(params[:id], &amp;quot;participant&amp;quot;, &amp;quot;reader&amp;quot;, &amp;quot;submitter&amp;quot;, &amp;quot;reviewer&amp;quot;) : true)&lt;br /&gt;
  else&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator'].include? current_role_name&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index', 'list'&lt;br /&gt;
    current_user_has_student_privileges? &amp;amp;&amp;amp; list_authorization_check&lt;br /&gt;
  else&lt;br /&gt;
    current_user_has_ta_privileges?&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; action ===&lt;br /&gt;
Previously, the &amp;lt;code&amp;gt;ReviewBidsController's index&amp;lt;/code&amp;gt; action was overloaded with responsibilities—it handled everything from locating and validating assignments to fetching review mappings and counting completed reviews. This all-in-one approach made the controller action messy. &lt;br /&gt;
&lt;br /&gt;
We have moved the business logic from the controller action and into dedicated service objects. With this change, we introduced two key service objects: &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; focuses on retrieving the participant along with their assignments, while the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; takes on the task of managing review mappings and calculating review-related stats, like how many reviews have been completed. &lt;br /&gt;
&lt;br /&gt;
This separation of responsibilities aligns with the Single Responsibility Principle. The logic for fetching assignments, retrieving review mappings, and counting reviews is now more flexible and reusable throughout the application. For example, we used methods in the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; in both the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; and the &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;, helping to avoid code duplication.The article [https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial Rails Service Objects Tutorial] served as a guide on when and how to use service objects in Rails.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
  return unless current_user_id?(@participant.user_id)&lt;br /&gt;
  @assignment = @participant.assignment&lt;br /&gt;
  @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
  # Finding how many reviews have been completed&lt;br /&gt;
  @num_reviews_completed = 0&lt;br /&gt;
  @review_mappings.each do |map|&lt;br /&gt;
    @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # render view for completing reviews after review bidding has been completed&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @assignment = participant_service.assignment&lt;br /&gt;
  unless @assignment.is_a?(Assignment)&lt;br /&gt;
    flash[:error] = 'Assignment not found.'&lt;br /&gt;
    redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  @review_mappings = review_service.review_mappings&lt;br /&gt;
  @num_reviews_completed = review_service.review_counts[:completed]&lt;br /&gt;
&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Optimize &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Action functionality ===&lt;br /&gt;
In the earlier version of the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, the server only needed a couple of key pieces of information: an array of topic IDs (params[:topic]) and the participant ID (params[:id}). From there, it would figure out the assignment_id by using the first topic as a reference. This is risky because the &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt; associated with the topic may not be a valid &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
With the new implementation, we structured the parameters to include the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt;, an array of topic IDs, and &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;. This change ensures that the server always receives complete and reliable information, preventing issues caused by missing or inconsistent data. It also removes the need for the server to perform unnecessary lookups, as in the case of &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;, allowing the controller to focus on receiving requests and rendering appropriate responses.&lt;br /&gt;
&lt;br /&gt;
While these updates have cleaned up the set_priority action, they still contain a lot of business logic, like validations and priority updates. To make the code even more maintainable, there’s room for further refactoring—perhaps moving some of this logic into a service class or model method to streamline the controller even more.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | Old Code&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    if params[:topic].nil?&lt;br /&gt;
      ReviewBid.where(participant_id: params[:id]).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      participant = AssignmentParticipant.find_by(id: params[:id])&lt;br /&gt;
      assignment_id = SignUpTopic.find(params[:topic].first).assignment.id&lt;br /&gt;
      @bids = ReviewBid.where(participant_id: params[:id])&lt;br /&gt;
      signed_up_topics = ReviewBid.where(participant_id: params[:id]).map(&amp;amp;:signuptopic_id)&lt;br /&gt;
      signed_up_topics -= params[:topic].map(&amp;amp;:to_i)&lt;br /&gt;
      signed_up_topics.each do |topic|&lt;br /&gt;
        ReviewBid.where(signuptopic_id: topic, participant_id: params[:id]).destroy_all&lt;br /&gt;
      end&lt;br /&gt;
      params[:topic].each_with_index do |topic_id, index|&lt;br /&gt;
        bid_existence = ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id])&lt;br /&gt;
        if bid_existence.empty?&lt;br /&gt;
          ReviewBid.create(priority: index + 1, signuptopic_id: topic_id, participant_id: params[:id], assignment_id: assignment_id)&lt;br /&gt;
        else&lt;br /&gt;
          ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id]).update_all(priority: index + 1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to action: 'show', assignment_id: params[:assignment_id], id: params[:id]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    participant_id = params[:participant_id].to_i&lt;br /&gt;
    selected_topic_ids = params[:topic]&amp;amp;.map(&amp;amp;:to_i) || []&lt;br /&gt;
    assignment_id = params[:assignment_id].to_i&lt;br /&gt;
&lt;br /&gt;
    unless current_user_can_modify_participant?(participant_id)&lt;br /&gt;
      flash[:error] = 'You are not authorized to modify these bids.'&lt;br /&gt;
      respond_to do |format|&lt;br /&gt;
        format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
        format.json { render json: { status: 'unauthorized' }, status: :unauthorized }&lt;br /&gt;
      end&lt;br /&gt;
      return&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    if selected_topic_ids.empty?&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      assignment = Assignment.find_by(id: assignment_id)&lt;br /&gt;
      if assignment.nil?&lt;br /&gt;
        flash[:error] = &amp;quot;Invalid assignment.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_assignment' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      unless SignUpTopic.where(id: selected_topic_ids, assignment_id: assignment_id).count == selected_topic_ids.size&lt;br /&gt;
        flash[:error] = &amp;quot;One or more selected topics are invalid.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_topics' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).where.not(signuptopic_id: selected_topic_ids).destroy_all&lt;br /&gt;
&lt;br /&gt;
      selected_topic_ids.each_with_index do |topic_id, index|&lt;br /&gt;
        bid = ReviewBid.find_or_initialize_by(signuptopic_id: topic_id, participant_id: participant_id)&lt;br /&gt;
        bid.priority = index + 1&lt;br /&gt;
        bid.assignment_id = assignment_id&lt;br /&gt;
        bid.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_to action: 'show', assignment_id: assignment_id, id: participant_id, notice: 'Review bids updated successfully.' }&lt;br /&gt;
      format.json { render json: { status: 'success' }, status: :ok }&lt;br /&gt;
    end&lt;br /&gt;
  rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
    flash[:error] = &amp;quot;Failed to update priorities: #{e.message}&amp;quot;&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
      format.json { render json: { status: 'error', message: e.message }, status: :unprocessable_entity }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Client Side Changes to Support &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Changes ====&lt;br /&gt;
We refactored the client-side code to ensure it passed the correct information from the view to the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action. On the client side, we now grab the topicIDs and send them as &amp;lt;code&amp;gt;selected_topic_ids&amp;lt;/code&amp;gt;, allowing the controller to figure out which topics were selected and in what order.  &lt;br /&gt;
&lt;br /&gt;
When working with jQuery, AJAX is awesome because it lets us update the server without reloading the page, making the user experience smooth and responsive. When we pull the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, we can grab it along with other data, like the list of topics, and include it as a single request. This way, we don’t have to rely on the URL to know which participant we’re working with, which makes the code cleaner.&lt;br /&gt;
&lt;br /&gt;
We also added an update handler so that any time the order of the selected topic changes in the view, information is sent to the appropriate controller action. This ensures that the controller and the view remain in sync, with real-time updates, and includes a mechanism for providing feedback if something goes wrong.&lt;br /&gt;
&lt;br /&gt;
Note: One interesting aspect of the application was the mix of JavaScript and CoffeeScript code. It wasn't clear why both types of code were used.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery -&amp;gt;&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: -&amp;gt;&lt;br /&gt;
      $.post($(this).data('update-url'), $(this).sortable('serialize'))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery(document).ready ($) -&amp;gt;&lt;br /&gt;
  sendUpdatedOrder = -&amp;gt;&lt;br /&gt;
    sortedIDs = $(this).sortable('toArray')&lt;br /&gt;
&lt;br /&gt;
    topicIDs = sortedIDs.map (id) -&amp;gt;&lt;br /&gt;
      id.replace 'topic_', ''&lt;br /&gt;
&lt;br /&gt;
    participant_id = $(this).data('participant-id')&lt;br /&gt;
&lt;br /&gt;
    assignment_id = new URL($(this).data('update-url'), window.location.origin).searchParams.get('assignment_id')&lt;br /&gt;
&lt;br /&gt;
    $.ajax&lt;br /&gt;
      type: 'POST'&lt;br /&gt;
      url: $(this).data('update-url')&lt;br /&gt;
      data:&lt;br /&gt;
        topic: topicIDs&lt;br /&gt;
        participant_id: participant_id&lt;br /&gt;
        assignment_id: assignment_id&lt;br /&gt;
      success: (response) -&amp;gt;&lt;br /&gt;
        console.log 'Selections updated successfully.'&lt;br /&gt;
      error: (xhr, status, error) -&amp;gt;&lt;br /&gt;
        alert 'An error occurred while saving your selections. Please try again.'&lt;br /&gt;
        console.error error&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: sendUpdatedOrder&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-1.-signupsheetcontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; Updates ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; method renders the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; action However, the view is associated with the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # provides variables for reviewing page located at views/review_bids/others_work.html.erb&lt;br /&gt;
  def index&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
    return unless current_user_id?(@participant.user_id)&lt;br /&gt;
&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
    # Finding how many reviews have been completed&lt;br /&gt;
    @num_reviews_completed = 0&lt;br /&gt;
    @review_mappings.each do |map|&lt;br /&gt;
      @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # render view for completing reviews after review bidding has been completed&lt;br /&gt;
    render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
  end  &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-2.-reviewbidsotherswork-view-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;review_bids_others_work.html.erb&amp;lt;/code&amp;gt; View Updates ====&lt;br /&gt;
The view is technically part of the SignUpSheetController, but it's being rendered by the ReviewBidsController, which initially threw us off. After chatting with the professor, we decided not to change this view. He indicated that the plan is to reuse these views for both assignment and review bidding.&lt;br /&gt;
&lt;br /&gt;
We suggest building the assignment bidding and review bidding features separately first to ensure each one works independently. Once that’s done, we can clean things up by combining any shared functionality. This could include creating shared views or even introducing an abstract model to handle both types of bidding more efficiently. It could also mean replacing the ReviewBidsController with something more generic, like a BidsController, to keep things reusable and easier to maintain down the line.&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Rename &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; method ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; should be &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt;&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:''' Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The primary activity here is to refactor the method name &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; and fix the hardcoded URL. However, the match topic bidding algorithm no longer works since Heroku is no longer free, per the professor. The reference link below has detailed information about the web service that we can leverage to gain that understanding.&lt;br /&gt;
&lt;br /&gt;
Reference Link for Webservice Details: [https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Updated Implementation ====&lt;br /&gt;
The original requirement was to rename the run_bidding_algorithm method to assign_reviewers, but after digging into the code, we realized that renaming it didn’t make sense. The method runs the bidding algorithm to match topics, and there’s already a separate method to handle assigning review topics.&lt;br /&gt;
&lt;br /&gt;
However, we improved the logic in the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; method (which calls the private &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt;) by introducing a separate &amp;lt;code&amp;gt;BiddingAlgorithmService&amp;lt;/code&amp;gt; object. This keeps the business logic out of the controller action, which makes things cleaner and more maintainable.&lt;br /&gt;
&lt;br /&gt;
==== review_bids_controller ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def run_bidding_algorithm&lt;br /&gt;
    review_bid = ReviewBid.new&lt;br /&gt;
    bidding_data = review_bid.bidding_data&lt;br /&gt;
    matched_topics = BiddingAlgorithmService.new(bidding_data).run&lt;br /&gt;
    raise ArgumentError, 'Failed to assign reviewers. Please try again later.' unless matched_topics&lt;br /&gt;
&lt;br /&gt;
    matched_topics&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== bidding_algorithm_service ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# frozen_string_literal: true&lt;br /&gt;
&lt;br /&gt;
# The `BiddingAlgorithmService` run the bid assignment algorithm&lt;br /&gt;
# Sends student IDs, topic IDs, student preferences, and timestamps to the web service&lt;br /&gt;
# The web service returns the matched assignments in the JSON response body&lt;br /&gt;
class BiddingAlgorithmService&lt;br /&gt;
  SERVICE_URL = 'http://app-csc517.herokuapp.com/match_topics'.freeze&lt;br /&gt;
&lt;br /&gt;
  # MOCK_DATA is based on the documentation detailing how the webservice behaves.&lt;br /&gt;
  # Reference: https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review#Webservice&lt;br /&gt;
  MOCK_DATA = {&lt;br /&gt;
    36239 =&amp;gt; [3970, 3972, 3975],&lt;br /&gt;
    36240 =&amp;gt; [3973, 3974, 3972],&lt;br /&gt;
    36241 =&amp;gt; [3969, 3971, 3972],&lt;br /&gt;
    36242 =&amp;gt; [3969, 3971, 3973],&lt;br /&gt;
    36243 =&amp;gt; [3969, 3970, 3971]&lt;br /&gt;
  }.freeze&lt;br /&gt;
&lt;br /&gt;
  def initialize(bidding_data)&lt;br /&gt;
    @bidding_data = bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def run&lt;br /&gt;
    return MOCK_DATA if Rails.application.config.use_mock_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
    perform_request&lt;br /&gt;
  rescue RestClient::ExceptionWithResponse, JSON::ParserError&lt;br /&gt;
    false&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def perform_request&lt;br /&gt;
    response = RestClient.post(&lt;br /&gt;
      SERVICE_URL,&lt;br /&gt;
      @bidding_data.to_json,&lt;br /&gt;
      content_type: :json,&lt;br /&gt;
      accept: :json&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    validate_response(response)&lt;br /&gt;
    JSON.parse(response.body)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def validate_response(response)&lt;br /&gt;
    raise 'Invalid response format' unless response.headers[:content_type] &amp;amp;&amp;amp; response.headers[:content_type].include?('application/json')&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Resolve Functionality Issues for Non Bidders ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Currently it doesn't work if some student does not bid. In this case, algorithm needs to be fixed to ignore anyone who didn’t bid, and let them choose from what’s left over.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to do several things within this controller. We will set a default state for non-bidders that we can point to to give them options to select topics to review from the leftovers. We will consolidate &amp;lt;code&amp;gt;@sign_up_topics&amp;lt;/code&amp;gt; to resolve the DRY violation. Lastly, we will remove business logic from the show method and push it to a new helper to conform to the SRP.&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # Additional method&lt;br /&gt;
&lt;br /&gt;
  # Provides variables for review bidding page&lt;br /&gt;
  def show&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id].to_i)&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @sign_up_topics = SignUpTopic.where(assignment_id: @assignment.id, private_to: nil)&lt;br /&gt;
    my_topic = SignedUpTeam.topic_id(@participant.parent_id, @participant.user_id)&lt;br /&gt;
    @sign_up_topics -= SignUpTopic.where(assignment_id: @assignment.id, id: my_topic)&lt;br /&gt;
    @num_participants = AssignmentParticipant.where(parent_id: @assignment.id).count&lt;br /&gt;
    @selected_topics = nil # This is used to list the topics assigned to review (i.e., select == assigned)&lt;br /&gt;
    @bids = ReviewBid.where(participant_id: @participant, assignment_id: @assignment.id)&lt;br /&gt;
    signed_up_topics = []&lt;br /&gt;
    &lt;br /&gt;
    @bids.each do |bid|&lt;br /&gt;
      sign_up_topic = SignUpTopic.find_by(id: bid.signuptopic_id)&lt;br /&gt;
      signed_up_topics &amp;lt;&amp;lt; sign_up_topic if sign_up_topic&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    signed_up_topics &amp;amp;= @sign_up_topics&lt;br /&gt;
    @sign_up_topics -= signed_up_topics&lt;br /&gt;
    @bids = signed_up_topics&lt;br /&gt;
    @num_of_topics = @sign_up_topics.size&lt;br /&gt;
    @assigned_review_maps = []&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: @assignment.id, reviewer_id: @participant.id).each do |review_map|&lt;br /&gt;
      @assigned_review_maps &amp;lt;&amp;lt; review_map&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Explicitly render view since it's in the sign up sheet views&lt;br /&gt;
    render 'sign_up_sheet/review_bids_show'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Additional methods&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-2.-reviewbidshelper-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
 # Assigns bidding topics to reviewers&lt;br /&gt;
  def assign_bidding&lt;br /&gt;
      assignment = validate_assignment(params[:assignment_id])&lt;br /&gt;
&lt;br /&gt;
      reviewers = validate_reviewers(assignment.id)&lt;br /&gt;
      reviewer_ids = reviewers.map(&amp;amp;:id)&lt;br /&gt;
&lt;br /&gt;
      matched_topics = run_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
      if matched_topics.blank?&lt;br /&gt;
        flash[:alert] = 'Topic or assignment is missing'&lt;br /&gt;
        redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      leftover_topics = find_leftover_topics(assignment.id, matched_topics)&lt;br /&gt;
      assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.new.assign_review_topics(matched_topics)&lt;br /&gt;
      assignment.update!(can_choose_topic_to_review: false)&lt;br /&gt;
&lt;br /&gt;
      flash[:notice] = 'Reviewers were successfully assigned to topics.'&lt;br /&gt;
      redirect_back fallback_location: root_path&lt;br /&gt;
    rescue ArgumentError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ArgumentError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: e.message&lt;br /&gt;
    rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::RecordInvalid: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue ActiveRecord::ActiveRecordError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::ActiveRecordError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue StandardError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;StandardError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers. Please try again later.'&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  #service methods here ...&lt;br /&gt;
&lt;br /&gt;
  def find_leftover_topics(assignment_id, matched_topics)&lt;br /&gt;
    all_topic_ids = SignUpTopic.where(assignment_id: assignment_id).pluck(:id)&lt;br /&gt;
    assigned_topic_ids = matched_topics.map { |match| match[:topic_id] }&lt;br /&gt;
&lt;br /&gt;
    # Calculate leftover topics&lt;br /&gt;
    all_topic_ids - assigned_topic_ids&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
    return if leftover_topics.blank?&lt;br /&gt;
&lt;br /&gt;
    # Find non-bidders by excluding already matched reviewers&lt;br /&gt;
    non_bidders = reviewer_ids - matched_topics.keys&lt;br /&gt;
&lt;br /&gt;
    # Assign leftover topics to non-bidders in a round-robin fashion&lt;br /&gt;
    non_bidders.each_with_index do |reviewer_id, index|&lt;br /&gt;
      topic_id = leftover_topics[index % leftover_topics.length]&lt;br /&gt;
      ReviewBid.create(priority: 1, signuptopic_id: topic_id, participant_id: reviewer_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirm Functionality for Single-person Teams ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Make sure your code works on individual assignments, i.e., assignments where participants sign up for topics instead of teams. In this case, a team is created for each participant when they sign up. So the code should work for assignments to which either individuals or teams submit.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to make an update to focus on topic rather than team or have it where teams are updated based on topic id.We will probably utilize sign up topic or a similar object to accomplish this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-5-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bid Status Messaging ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| To make the functionality more intuitive, include a message to say how many students are eligible to submit bids, how many have submitted their bids, when the deadline for submitting bids is.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
The logic for these messages will go into the helper file. It is effectively the same reasoning as our migration of other business/functionality code from the controller to the helper to conform to the SRP. It will go hand in hand with the SignUpTopic portion of the code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-6-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Why are the methods in review_bid.rb class methods? Can we change them to instance methods or move it to helpers?&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-1.-reviewbid-model-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model Updates ====&lt;br /&gt;
Since the current class methods are tied to &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model instances, we will change them to instance methods. There is no need to move them to helpers.&lt;br /&gt;
&lt;br /&gt;
===== Review Bid Model Flow Before Refactoring =====&lt;br /&gt;
This sequence diagram describes how the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; interacts with the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model in assigning review topics to reviewers according to their bids. Once the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; action is initiated by the user, the controller gathers the IDs of reviewers and calls &amp;lt;code&amp;gt;ReviewBid.bidding_data&amp;lt;/code&amp;gt; to gather bidding information. That is sent out to an external bidding algorithm, and the matched topics that come back are used by &amp;lt;code&amp;gt;ReviewBid.assign_review_topics&amp;lt;/code&amp;gt; to create &amp;lt;code&amp;gt;ReviewResponseMap&amp;lt;/code&amp;gt; entries, assigning topics to reviewers for the assignment.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
  belongs_to :assignment, class_name: 'Assignment'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  def self.bidding_data(assignment_id, reviewer_ids)&lt;br /&gt;
    # create basic hash and set basic hash data&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'users' =&amp;gt; {}, 'max_accepted_proposals' =&amp;gt; [] }&lt;br /&gt;
    bidding_data['tid'] = SignUpTopic.where(assignment_id: assignment_id).ids&lt;br /&gt;
    bidding_data['max_accepted_proposals'] = Assignment.where(id: assignment_id).pluck(:num_reviews_allowed).first&lt;br /&gt;
&lt;br /&gt;
    # loop through reviewer_ids to get reviewer specific bidding data&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      bidding_data['users'][reviewer_id] = reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # assigns topics to reviews as matched by the webservice algorithm&lt;br /&gt;
  def self.assign_review_topics(assignment_id, reviewer_ids, matched_topics, _min_num_reviews = 2)&lt;br /&gt;
    # if review response map already created, delete it&lt;br /&gt;
    if ReviewResponseMap.where(reviewed_object_id: assignment_id)&lt;br /&gt;
      ReviewResponseMap.where(reviewed_object_id: assignment_id).destroy_all&lt;br /&gt;
    end&lt;br /&gt;
    # loop through reviewer_ids to assign reviews to each reviewer&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      topics_to_assign = matched_topics[reviewer_id.to_s]&lt;br /&gt;
      topics_to_assign.each do |topic|&lt;br /&gt;
        assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method to assign a single topic to a reviewer&lt;br /&gt;
  def self.assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic).pluck(:team_id).first&lt;br /&gt;
    team_to_review.nil? ? [] : ReviewResponseMap.create(reviewed_object_id: assignment_id, reviewer_id: reviewer_id, reviewee_id: team_to_review, type: 'ReviewResponseMap')&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method for getting individual reviewer_ids bidding data&lt;br /&gt;
  # returns user's bidding data hash&lt;br /&gt;
  def self.reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    reviewer_user_id = AssignmentParticipant.find(reviewer_id).user_id&lt;br /&gt;
    self_topic = SignedUpTeam.topic_id(assignment_id, reviewer_user_id)&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'otid' =&amp;gt; self_topic, 'priority' =&amp;gt; [], 'time' =&amp;gt; [] }&lt;br /&gt;
    bids = ReviewBid.where(participant_id: reviewer_id)&lt;br /&gt;
&lt;br /&gt;
    # loop through each bid for a topic to get specific data&lt;br /&gt;
    bids.each do |bid|&lt;br /&gt;
      bidding_data['tid'] &amp;lt;&amp;lt; bid.signuptopic_id&lt;br /&gt;
      bidding_data['priority'] &amp;lt;&amp;lt; bid.priority&lt;br /&gt;
      bidding_data['time'] &amp;lt;&amp;lt; bid.updated_at&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  # Instance method to get bidding data for this review bid&lt;br /&gt;
  def bidding_data&lt;br /&gt;
    {&lt;br /&gt;
      'tid' =&amp;gt; topic_ids,&lt;br /&gt;
      'otid' =&amp;gt; self_topic,&lt;br /&gt;
      'priority' =&amp;gt; priorities,&lt;br /&gt;
      'time' =&amp;gt; updated_times&lt;br /&gt;
    }&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # bid object to limit db touches for similar data acquisitions&lt;br /&gt;
  def bids&lt;br /&gt;
    @bids ||= ReviewBid.where(participant_id: participant_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def topic_ids&lt;br /&gt;
    bids.pluck(:signuptopic_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def priorities&lt;br /&gt;
    bids.pluck(:priority)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def updated_times&lt;br /&gt;
    bids.pluck(:updated_at)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self_topic&lt;br /&gt;
    return nil unless topic &amp;amp;&amp;amp; topic.assignment_id&lt;br /&gt;
&lt;br /&gt;
    SignedUpTeam.topic_id(topic.assignment_id, participant.user_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign topics to reviews&lt;br /&gt;
  def assign_review_topics(matched_topics)&lt;br /&gt;
    validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: topic.assignment_id).destroy_all&lt;br /&gt;
&lt;br /&gt;
    matched_topics.each do |reviewer_id, topics|&lt;br /&gt;
      Array(topics).each { |topic_id| assign_topic_to_reviewer(reviewer_id, topic_id) }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign a single topic to a reviewer&lt;br /&gt;
  def assign_topic_to_reviewer(reviewer_id, topic_id)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic_id).pluck(:team_id).first&lt;br /&gt;
    return if team_to_review.nil?&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.create(&lt;br /&gt;
      reviewed_object_id: topic.assignment_id,&lt;br /&gt;
      reviewer_id: reviewer_id,&lt;br /&gt;
      reviewee_id: team_to_review,&lt;br /&gt;
      type: 'ReviewResponseMap'&lt;br /&gt;
    )&lt;br /&gt;
    # error handling&lt;br /&gt;
  rescue StandardError =&amp;gt; e&lt;br /&gt;
    Rails.logger.error(&amp;quot;Failed to assign topic #{topic_id} to reviewer #{reviewer_id}: #{e.message}&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
    raise ArgumentError, 'Topic or assignment is missing' if topic.nil? || topic.assignment_id.nil?&lt;br /&gt;
    raise ArgumentError, 'Matched topics must be a Hash' unless matched_topics.is_a?(Hash)&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
Once we changed them to instance methods, we modified their use in the controller code to instantiate the object first.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-3.-schema.db-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;Schema.db&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We also need to remove the foreign key reference to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model since the &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model already has access to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;, and that is the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; we should be referencing. This will also involve modifying the database to remove the foreign key reference to the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Steps required:&lt;br /&gt;
&lt;br /&gt;
# Review the records in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table in the database to see if there are any records and if those records have values for &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;&lt;br /&gt;
# Create a migration file using the command: &amp;lt;code&amp;gt;rails generate migration RemoveAssignmentFromReviewBids&amp;lt;/code&amp;gt;&lt;br /&gt;
# In the generated file, indicate the change that needs to be made using the change method and migration columns &amp;lt;code&amp;gt;remove_foreign_key&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;remove_column&amp;lt;/code&amp;gt;. Migration should be straightforward since there is no cascading behavior (i.e., &amp;lt;code&amp;gt;dependent:destroy&amp;lt;/code&amp;gt;) to worry about.&lt;br /&gt;
# Once comfortable with the updates to the generated file, run the migration using the &amp;lt;code&amp;gt;rails db:migrate&amp;lt;/code&amp;gt; command.&lt;br /&gt;
# Run a query in the DB to ensure no orphaned records are in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table after the updates.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-4.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implement Edge Case Testing ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| In the previous implementation wiki, there are edge cases which are not exhaustively tested. Should test those edge cases thoroughly and add more edge case testing - link&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span class=&amp;quot;mark&amp;quot;&amp;gt;TODO&amp;lt;/span&amp;gt;:We just got initial test results back. We still need to figure out why tests are failing and what may be slipping through the cracks.[[File:Test expertiza.png|411x459px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-8-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design Patterns and Principles ===&lt;br /&gt;
Below are the design patterns and principles we plan to use to address the problem statements outlined in the project requirements:&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
** Controller Responsibility&lt;br /&gt;
*** To address the issue of authentication logic residing in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, we will refactor the code to ensure the controller solely handles request and response flows.&lt;br /&gt;
*** Authentication logic will be moved to dedicated methods in &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt;, making the codebase easier to maintain and test.&lt;br /&gt;
** View Simplification&lt;br /&gt;
*** Currently, the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; view contains excessive business logic, violating SRP.&lt;br /&gt;
*** We will move this logic into helper methods or models, ensuring each component has a single responsibility and improving code readability.&lt;br /&gt;
&lt;br /&gt;
* Separation of Concerns (SoC)&lt;br /&gt;
** By relocating authorization logic out of the controller, we separate authentication from request handling.&lt;br /&gt;
** Adhering to SoC enhances readability and maintainability by allowing each part of the application to focus on its specific role.&lt;br /&gt;
* Don't Repeat Yourself (DRY)&lt;br /&gt;
** We will inspect the existing implementation for code duplication, particularly in the controller actions.&lt;br /&gt;
** Any repetitive code will be refactored or removed to reduce redundancy and improve clarity.&lt;br /&gt;
&lt;br /&gt;
* Model-View-Controller (MVC) Adherence&lt;br /&gt;
** Moving business logic from the view aligns with MVC best practices.&lt;br /&gt;
** By ensuring the view only presents data without processing it, we maintain clear boundaries between the model, view, and controller layers.&lt;br /&gt;
&lt;br /&gt;
* Dependency Inversion Principle (DIP)&lt;br /&gt;
** We plan to update class methods to instance methods, allowing the controller to depend on abstractions rather than concrete implementations.&lt;br /&gt;
** This change leverages Ruby's duck typing, providing flexibility and making the code easier to maintain and test.&lt;br /&gt;
&lt;br /&gt;
** Clear and intentional naming during refactoring will enhance code clarity and maintainability.&lt;br /&gt;
&lt;br /&gt;
== Files Modified/Added ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-1.-controllers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
review_bids_controller.rb&lt;br /&gt;
&lt;br /&gt;
signup_sheet_controller.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-2.-models&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
review_bid.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-3.-helpers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
&lt;br /&gt;
authorization_helper.rb&lt;br /&gt;
&lt;br /&gt;
Review_bids_helper.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;viii.-test-plan&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
The Excel spreadsheet below details our test plan for testing the code we changed or created to ensure code coverage. The process involved reviewing the code and determining all the possible scenarios that needed to be covered based on what the method was doing, then creating test cases accordingly.&lt;br /&gt;
&lt;br /&gt;
* [https://docs.google.com/spreadsheets/d/1iHzOdSwlTtxOtf5VsNdwOaCP8NzYMmiJ/edit?usp=drive_link Open Test Matrix Spreadsheet]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/review_bids_controller_spec.rb ReviewBidsController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/sign_up_sheet_controller_spec.rb SignUpSheetController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/models/review_bid_spec.rb ReviewBid model updated test cases]&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-1.-mentor&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Ed Gehringer&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-2.-members&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Members ===&lt;br /&gt;
&lt;br /&gt;
* Gavin Teague&lt;br /&gt;
* Janice Uwujaren&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xi.-links&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
Previous Wiki:  &lt;br /&gt;
[https://wiki.expertiza.ncsu.edu/index.php/CSC/ECE_517_Fall_2021_-_E2151._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;Link&amp;lt;/u&amp;gt;]  &lt;br /&gt;
&lt;br /&gt;
Reference Link for Webservice Details:  &lt;br /&gt;
[https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
Guidance on Using Service Objects in Rails:&lt;br /&gt;
[https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial Rails Service Objects Tutorial]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xii.-references&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/document/d/1LHVacylQmB15I6VcYUA5nuC7GRRU9szHzZhjlPcE-PA/edit?tab=t.0#heading=h.u0oycoj6e0ev &amp;lt;u&amp;gt;Project Instructions&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;appendix-a-uml-class-diagram-plantuml-code-for-review-bids&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Juwujar</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=161051</id>
		<title>CSC/ECE 517 Fall 2024 - E2485. Allow reviewers to bid on what to review</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=161051"/>
		<updated>2024-12-12T06:02:41Z</updated>

		<summary type="html">&lt;p&gt;Juwujar: /* Refactor ReviewBidsController#index action */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
This project will fix the defects and improve the functionality in Expertiza of the Reviewer Bidding feature. It will make the functionality smoother, more reliable, and align with Expertiza's collaborative learning objectives. &lt;br /&gt;
&lt;br /&gt;
The Review Bidding feature lets reviewers focus on topics they know best or care about most. If someone feels exceptionally qualified or interested in a topic, they can bid to review submissions in that area. This helps ensure reviews are insightful and enjoyable for the reviewer while benefiting the other participants and the overall process.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iii.-problem-statements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Problem Statements ==&lt;br /&gt;
&lt;br /&gt;
Assigning reviews to users on Expertiza involves a complex bidding algorithm that requires optimization before production deployment. Below are key areas we plan to address as per the project requirements:&lt;br /&gt;
* Code Refactoring and Best Practices&lt;br /&gt;
** '''Utilize Authentication Utilities''': Modify the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; function in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to use existing authentication utilities, enhancing code reusability and adhering to the Single Responsibility Principle (SRP) and Separation of Concerns (SoC).&lt;br /&gt;
** '''Eliminate Code Duplication''': Refactor &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; to comply with the Don't Repeat Yourself (DRY) principle.&lt;br /&gt;
** '''Rename Functions for Clarity''': Change &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; to better reflect its purpose.&lt;br /&gt;
** '''Refactor Methods in '''&amp;lt;code&amp;gt;review_bid.rb&amp;lt;/code&amp;gt;''':''': Convert class methods to instance methods or relocate them to helper classes for improved code organization.&lt;br /&gt;
* Functionality Enhancements&lt;br /&gt;
** '''Handle Missing Bids Gracefully''': Adjust the bidding algorithm to accommodate students who do not bid, allowing them to select from the remaining options.&lt;br /&gt;
** '''Support Both Individual and Team Assignments''': Ensure the code functions correctly for participant and team-based assignments when signing up for topics.&lt;br /&gt;
* User Interface Improvements&lt;br /&gt;
** '''Increase Transparency in the Bidding Process''': Display messages indicating how many students are eligible to bid, how many have submitted bids, and the deadline.&lt;br /&gt;
** '''Accurate Bid Status Indicators''': Ensure topic colors change correctly based on the number of outstanding bids, providing clear visual feedback to users.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Exhaustive Edge Case Testing''': Conduct thorough testing for edge cases not previously covered, adding additional test cases as needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iv.-design-goals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Design Goals ==&lt;br /&gt;
&lt;br /&gt;
While addressing the existing defects and enhancing the Reviewer Bidding feature, we plan to adhere to the following design guidelines:&lt;br /&gt;
&lt;br /&gt;
* Code Quality and Maintainability&lt;br /&gt;
** '''Improve Code Structure: '''Implement refactoring strategies to enhance code readability and maintainability, ensuring adherence to best practices like the DRY principle.&lt;br /&gt;
** '''Standardize Authentication Logic: '''Utilize a centralized authentication utility to promote consistency and reusability across the application.&lt;br /&gt;
* System Stability and Flexibility&lt;br /&gt;
** '''Enhance Algorithm Robustness: '''Modify the bidding algorithm to handle scenarios where students do not bid, ensuring the system remains stable under all conditions.&lt;br /&gt;
** '''Ensure Assignment Versatility: '''Design the system to seamlessly support both individual and team assignments without additional configuration.&lt;br /&gt;
* User Experience Enhancements&lt;br /&gt;
** '''Increase Bidding Transparency: '''Develop user interface elements that clearly display bidding status, deadlines, and participation metrics.&lt;br /&gt;
** '''Implement Dynamic Visual Feedback: '''Use real-time visual cues, such as color changes, to inform users about the bidding status instantly.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Develop Comprehensive Test Suites: '''Create extensive automated tests covering edge cases and critical functionalities to guarantee reliable system behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;v.-uml-diagram&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== UML Diagram ==&lt;br /&gt;
&lt;br /&gt;
Below is a visual representation of the models associated with the review bids functionality, depicting the data structures and their relationships.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml class diagram2.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi.-implementation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
'''ReviewBid: '''The &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; manages the bidding process, where participants indicate their preferences for reviewing different topics or assignments. This helps distribute review tasks fairly based on participants' interests.&lt;br /&gt;
&lt;br /&gt;
'''Assignment: '''The &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model represents team or individual assignments in Expertiza. It stores all the essential information required to keep the assignment process organized, like managing participants, setting due dates, forming teams, and overseeing the review process.&lt;br /&gt;
&lt;br /&gt;
'''SignUpTopic: '''The &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt; model identifies specific topics or areas within an assignment for which participants can sign up. It manages the availability of these topics, tracks bids, coordinates team sign-ups, and assigns topics based on both bids and participant availability. &lt;br /&gt;
&lt;br /&gt;
'''Participant: '''The &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model is about the individual users who participate in an assignment. This includes students, instructors, teaching assistants, or other relevant roles. This model is responsible for managing key user information, monitoring interactions with the assignment, keeping track of team memberships, and recording things like badges and review grades.&lt;br /&gt;
&lt;br /&gt;
== Implementation ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1.-decoupling-authorization-logic-from-reviewbidscontroller&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Decoupling Authorization Logic from &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| '''&amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; needs to use the authentication utilities'''&lt;br /&gt;
&lt;br /&gt;
The function &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; should use the available authentication utilities to support the Single Responsibility Principle and Separation of Concerns and to support code reusability.&lt;br /&gt;
&lt;br /&gt;
| '''Authentication Consistency:''' Remove authentication logic in the controller and call logic in the authentication utility class instead to ensure consistent use of the same logic across the application, foster reusability, and eliminate DRY.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
==== Authorization Flow Before Refactoring ====&lt;br /&gt;
The sequence diagram depicts the authorization process within the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;. When a user invokes an action, the controller checks if the action is allowed by verifying the user's role through the &amp;lt;code&amp;gt;current_role_name()&amp;lt;/code&amp;gt; method in the &amp;lt;code&amp;gt;ApplicationController&amp;lt;/code&amp;gt;. Depending on the action and the user's role, the controller either authorizes the user to proceed or denies access, ultimately executing the action logic if authorized and responding to the user.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 1.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We simplified the &amp;lt;code&amp;gt;action_allowed?&amp;lt;/code&amp;gt; method by decoupling authorization logic from the controller to use helper methods in the &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt; module. The &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; should not handle authentication, which is a violation of SRP. &lt;br /&gt;
&lt;br /&gt;
Note: We also made similar updates to the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; methods for &amp;lt;code&amp;gt;SignUpController&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;vertical-align:top;&amp;quot; | Original Code !! style=&amp;quot;vertical-align:top;&amp;quot; | Refactored Code&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index'&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator',&lt;br /&gt;
     'Student'].include? current_role_name and&lt;br /&gt;
        ((%w[list].include? action_name) ? are_needed_authorizations_present?(params[:id], &amp;quot;participant&amp;quot;, &amp;quot;reader&amp;quot;, &amp;quot;submitter&amp;quot;, &amp;quot;reviewer&amp;quot;) : true)&lt;br /&gt;
  else&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator'].include? current_role_name&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index', 'list'&lt;br /&gt;
    current_user_has_student_privileges? &amp;amp;&amp;amp; list_authorization_check&lt;br /&gt;
  else&lt;br /&gt;
    current_user_has_ta_privileges?&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; action ===&lt;br /&gt;
Previously, the &amp;lt;code&amp;gt;ReviewBidsController's index&amp;lt;/code&amp;gt; action was overloaded with responsibilities—it handled everything from locating and validating assignments to fetching review mappings and counting completed reviews. This all-in-one approach made the controller action messy. &lt;br /&gt;
&lt;br /&gt;
We have moved the business logic from the controller action and into dedicated service objects. With this change, we introduced two key service objects: &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; focuses on retrieving the participant along with their assignments, while the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; takes on the task of managing review mappings and calculating review-related stats, like how many reviews have been completed. &lt;br /&gt;
&lt;br /&gt;
This separation of responsibilities aligns with the Single Responsibility Principle. The logic for fetching assignments, retrieving review mappings, and counting reviews is now more flexible and reusable throughout the application. For example, we used methods in the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; in both the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; and the &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;, helping to avoid code duplication.The article [https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial Rails Service Objects Tutorial] served as a guide on when and how to use service objects in Rails.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
  return unless current_user_id?(@participant.user_id)&lt;br /&gt;
  @assignment = @participant.assignment&lt;br /&gt;
  @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
  # Finding how many reviews have been completed&lt;br /&gt;
  @num_reviews_completed = 0&lt;br /&gt;
  @review_mappings.each do |map|&lt;br /&gt;
    @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # render view for completing reviews after review bidding has been completed&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @assignment = participant_service.assignment&lt;br /&gt;
  unless @assignment.is_a?(Assignment)&lt;br /&gt;
    flash[:error] = 'Assignment not found.'&lt;br /&gt;
    redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  @review_mappings = review_service.review_mappings&lt;br /&gt;
  @num_reviews_completed = review_service.review_counts[:completed]&lt;br /&gt;
&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Optimize &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Action functionality ===&lt;br /&gt;
In the earlier version of the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, the server only needed a couple of key pieces of information: an array of topic IDs (params[:topic]) and the participant ID (params[:id}). From there, it would figure out the assignment_id by using the first topic as a reference. This is risky because the &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt; associated with the topic may not be a valid &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
With the new implementation, we structured the parameters to include the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt;, an array of topic IDs, and &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;. This change ensures that the server always receives complete and reliable information, preventing issues caused by missing or inconsistent data. It also removes the need for the server to perform unnecessary lookups, as in the case of &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;, allowing the controller to focus on receiving requests and rendering appropriate responses.&lt;br /&gt;
&lt;br /&gt;
While these updates have cleaned up the set_priority action, they still contain a lot of business logic, like validations and priority updates. To make the code even more maintainable, there’s room for further refactoring—perhaps moving some of this logic into a service class or model method to streamline the controller even more.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | Old Code&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    if params[:topic].nil?&lt;br /&gt;
      ReviewBid.where(participant_id: params[:id]).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      participant = AssignmentParticipant.find_by(id: params[:id])&lt;br /&gt;
      assignment_id = SignUpTopic.find(params[:topic].first).assignment.id&lt;br /&gt;
      @bids = ReviewBid.where(participant_id: params[:id])&lt;br /&gt;
      signed_up_topics = ReviewBid.where(participant_id: params[:id]).map(&amp;amp;:signuptopic_id)&lt;br /&gt;
      signed_up_topics -= params[:topic].map(&amp;amp;:to_i)&lt;br /&gt;
      signed_up_topics.each do |topic|&lt;br /&gt;
        ReviewBid.where(signuptopic_id: topic, participant_id: params[:id]).destroy_all&lt;br /&gt;
      end&lt;br /&gt;
      params[:topic].each_with_index do |topic_id, index|&lt;br /&gt;
        bid_existence = ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id])&lt;br /&gt;
        if bid_existence.empty?&lt;br /&gt;
          ReviewBid.create(priority: index + 1, signuptopic_id: topic_id, participant_id: params[:id], assignment_id: assignment_id)&lt;br /&gt;
        else&lt;br /&gt;
          ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id]).update_all(priority: index + 1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to action: 'show', assignment_id: params[:assignment_id], id: params[:id]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    participant_id = params[:participant_id].to_i&lt;br /&gt;
    selected_topic_ids = params[:topic]&amp;amp;.map(&amp;amp;:to_i) || []&lt;br /&gt;
    assignment_id = params[:assignment_id].to_i&lt;br /&gt;
&lt;br /&gt;
    unless current_user_can_modify_participant?(participant_id)&lt;br /&gt;
      flash[:error] = 'You are not authorized to modify these bids.'&lt;br /&gt;
      respond_to do |format|&lt;br /&gt;
        format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
        format.json { render json: { status: 'unauthorized' }, status: :unauthorized }&lt;br /&gt;
      end&lt;br /&gt;
      return&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    if selected_topic_ids.empty?&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      assignment = Assignment.find_by(id: assignment_id)&lt;br /&gt;
      if assignment.nil?&lt;br /&gt;
        flash[:error] = &amp;quot;Invalid assignment.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_assignment' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      unless SignUpTopic.where(id: selected_topic_ids, assignment_id: assignment_id).count == selected_topic_ids.size&lt;br /&gt;
        flash[:error] = &amp;quot;One or more selected topics are invalid.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_topics' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).where.not(signuptopic_id: selected_topic_ids).destroy_all&lt;br /&gt;
&lt;br /&gt;
      selected_topic_ids.each_with_index do |topic_id, index|&lt;br /&gt;
        bid = ReviewBid.find_or_initialize_by(signuptopic_id: topic_id, participant_id: participant_id)&lt;br /&gt;
        bid.priority = index + 1&lt;br /&gt;
        bid.assignment_id = assignment_id&lt;br /&gt;
        bid.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_to action: 'show', assignment_id: assignment_id, id: participant_id, notice: 'Review bids updated successfully.' }&lt;br /&gt;
      format.json { render json: { status: 'success' }, status: :ok }&lt;br /&gt;
    end&lt;br /&gt;
  rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
    flash[:error] = &amp;quot;Failed to update priorities: #{e.message}&amp;quot;&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
      format.json { render json: { status: 'error', message: e.message }, status: :unprocessable_entity }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Client Side Changes to Support &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Changes ====&lt;br /&gt;
We refactored the client-side code to ensure it passed the correct information from the view to the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action. On the client side, we now grab the topicIDs and send them as &amp;lt;code&amp;gt;selected_topic_ids&amp;lt;/code&amp;gt;, allowing the controller to figure out which topics were selected and in what order.  &lt;br /&gt;
&lt;br /&gt;
When working with jQuery, AJAX is awesome because it lets us update the server without reloading the page, making the user experience smooth and responsive. When we pull the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, we can grab it along with other data, like the list of topics, and include it as a single request. This way, we don’t have to rely on the URL to know which participant we’re working with, which makes the code cleaner.&lt;br /&gt;
&lt;br /&gt;
We also added an update handler so that any time the order of the selected topic changes in the view, information is sent to the appropriate controller action. This ensures that the controller and the view remain in sync, with real-time updates, and includes a mechanism for providing feedback if something goes wrong.&lt;br /&gt;
&lt;br /&gt;
Note: One interesting aspect of the application was the mix of JavaScript and CoffeeScript code. It wasn't clear why both types of code were used.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery -&amp;gt;&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: -&amp;gt;&lt;br /&gt;
      $.post($(this).data('update-url'), $(this).sortable('serialize'))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery(document).ready ($) -&amp;gt;&lt;br /&gt;
  sendUpdatedOrder = -&amp;gt;&lt;br /&gt;
    sortedIDs = $(this).sortable('toArray')&lt;br /&gt;
&lt;br /&gt;
    topicIDs = sortedIDs.map (id) -&amp;gt;&lt;br /&gt;
      id.replace 'topic_', ''&lt;br /&gt;
&lt;br /&gt;
    participant_id = $(this).data('participant-id')&lt;br /&gt;
&lt;br /&gt;
    assignment_id = new URL($(this).data('update-url'), window.location.origin).searchParams.get('assignment_id')&lt;br /&gt;
&lt;br /&gt;
    $.ajax&lt;br /&gt;
      type: 'POST'&lt;br /&gt;
      url: $(this).data('update-url')&lt;br /&gt;
      data:&lt;br /&gt;
        topic: topicIDs&lt;br /&gt;
        participant_id: participant_id&lt;br /&gt;
        assignment_id: assignment_id&lt;br /&gt;
      success: (response) -&amp;gt;&lt;br /&gt;
        console.log 'Selections updated successfully.'&lt;br /&gt;
      error: (xhr, status, error) -&amp;gt;&lt;br /&gt;
        alert 'An error occurred while saving your selections. Please try again.'&lt;br /&gt;
        console.error error&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: sendUpdatedOrder&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-1.-signupsheetcontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; Updates ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; method renders the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; action However, the view is associated with the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # provides variables for reviewing page located at views/review_bids/others_work.html.erb&lt;br /&gt;
  def index&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
    return unless current_user_id?(@participant.user_id)&lt;br /&gt;
&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
    # Finding how many reviews have been completed&lt;br /&gt;
    @num_reviews_completed = 0&lt;br /&gt;
    @review_mappings.each do |map|&lt;br /&gt;
      @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # render view for completing reviews after review bidding has been completed&lt;br /&gt;
    render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
  end  &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-2.-reviewbidsotherswork-view-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;review_bids_others_work.html.erb&amp;lt;/code&amp;gt; View Updates ====&lt;br /&gt;
The view is technically part of the SignUpSheetController, but it's being rendered by the ReviewBidsController, which initially threw us off. After chatting with the professor, we decided not to change this view. He indicated that the plan is to reuse these views for both assignment and review bidding.&lt;br /&gt;
&lt;br /&gt;
We suggest building the assignment bidding and review bidding features separately first to ensure each one works independently. Once that’s done, we can clean things up by combining any shared functionality. This could include creating shared views or even introducing an abstract model to handle both types of bidding more efficiently. It could also mean replacing the ReviewBidsController with something more generic, like a BidsController, to keep things reusable and easier to maintain down the line.&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Rename &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; method ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; should be &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt;&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:''' Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The primary activity here is to refactor the method name &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; and fix the hardcoded URL. However, the match topic bidding algorithm no longer works since Heroku is no longer free, per the professor. The reference link below has detailed information about the web service that we can leverage to gain that understanding.&lt;br /&gt;
&lt;br /&gt;
Reference Link for Webservice Details: [https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Updated Implementation ====&lt;br /&gt;
The original requirement was to rename the run_bidding_algorithm method to assign_reviewers, but after digging into the code, we realized that renaming it didn’t make sense. The method runs the bidding algorithm to match topics, and there’s already a separate method to handle assigning review topics.&lt;br /&gt;
&lt;br /&gt;
However, we improved the logic in the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; method (which calls the private &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt;) by introducing a separate &amp;lt;code&amp;gt;BiddingAlgorithmService&amp;lt;/code&amp;gt; object. This keeps the business logic out of the controller action, which makes things cleaner and more maintainable.&lt;br /&gt;
&lt;br /&gt;
==== review_bids_controller ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def run_bidding_algorithm&lt;br /&gt;
    review_bid = ReviewBid.new&lt;br /&gt;
    bidding_data = review_bid.bidding_data&lt;br /&gt;
    matched_topics = BiddingAlgorithmService.new(bidding_data).run&lt;br /&gt;
    raise ArgumentError, 'Failed to assign reviewers. Please try again later.' unless matched_topics&lt;br /&gt;
&lt;br /&gt;
    matched_topics&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== bidding_algorithm_service ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# frozen_string_literal: true&lt;br /&gt;
&lt;br /&gt;
# The `BiddingAlgorithmService` run the bid assignment algorithm&lt;br /&gt;
# Sends student IDs, topic IDs, student preferences, and timestamps to the web service&lt;br /&gt;
# The web service returns the matched assignments in the JSON response body&lt;br /&gt;
class BiddingAlgorithmService&lt;br /&gt;
  SERVICE_URL = 'http://app-csc517.herokuapp.com/match_topics'.freeze&lt;br /&gt;
&lt;br /&gt;
  # MOCK_DATA is based on the documentation detailing how the webservice behaves.&lt;br /&gt;
  # Reference: https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review#Webservice&lt;br /&gt;
  MOCK_DATA = {&lt;br /&gt;
    36239 =&amp;gt; [3970, 3972, 3975],&lt;br /&gt;
    36240 =&amp;gt; [3973, 3974, 3972],&lt;br /&gt;
    36241 =&amp;gt; [3969, 3971, 3972],&lt;br /&gt;
    36242 =&amp;gt; [3969, 3971, 3973],&lt;br /&gt;
    36243 =&amp;gt; [3969, 3970, 3971]&lt;br /&gt;
  }.freeze&lt;br /&gt;
&lt;br /&gt;
  def initialize(bidding_data)&lt;br /&gt;
    @bidding_data = bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def run&lt;br /&gt;
    return MOCK_DATA if Rails.application.config.use_mock_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
    perform_request&lt;br /&gt;
  rescue RestClient::ExceptionWithResponse, JSON::ParserError&lt;br /&gt;
    false&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def perform_request&lt;br /&gt;
    response = RestClient.post(&lt;br /&gt;
      SERVICE_URL,&lt;br /&gt;
      @bidding_data.to_json,&lt;br /&gt;
      content_type: :json,&lt;br /&gt;
      accept: :json&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    validate_response(response)&lt;br /&gt;
    JSON.parse(response.body)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def validate_response(response)&lt;br /&gt;
    raise 'Invalid response format' unless response.headers[:content_type] &amp;amp;&amp;amp; response.headers[:content_type].include?('application/json')&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Resolve Functionality Issues for Non Bidders ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Currently it doesn't work if some student does not bid. In this case, algorithm needs to be fixed to ignore anyone who didn’t bid, and let them choose from what’s left over.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to do several things within this controller. We will set a default state for non-bidders that we can point to to give them options to select topics to review from the leftovers. We will consolidate &amp;lt;code&amp;gt;@sign_up_topics&amp;lt;/code&amp;gt; to resolve the DRY violation. Lastly, we will remove business logic from the show method and push it to a new helper to conform to the SRP.&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # Additional method&lt;br /&gt;
&lt;br /&gt;
  # Provides variables for review bidding page&lt;br /&gt;
  def show&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id].to_i)&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @sign_up_topics = SignUpTopic.where(assignment_id: @assignment.id, private_to: nil)&lt;br /&gt;
    my_topic = SignedUpTeam.topic_id(@participant.parent_id, @participant.user_id)&lt;br /&gt;
    @sign_up_topics -= SignUpTopic.where(assignment_id: @assignment.id, id: my_topic)&lt;br /&gt;
    @num_participants = AssignmentParticipant.where(parent_id: @assignment.id).count&lt;br /&gt;
    @selected_topics = nil # This is used to list the topics assigned to review (i.e., select == assigned)&lt;br /&gt;
    @bids = ReviewBid.where(participant_id: @participant, assignment_id: @assignment.id)&lt;br /&gt;
    signed_up_topics = []&lt;br /&gt;
    &lt;br /&gt;
    @bids.each do |bid|&lt;br /&gt;
      sign_up_topic = SignUpTopic.find_by(id: bid.signuptopic_id)&lt;br /&gt;
      signed_up_topics &amp;lt;&amp;lt; sign_up_topic if sign_up_topic&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    signed_up_topics &amp;amp;= @sign_up_topics&lt;br /&gt;
    @sign_up_topics -= signed_up_topics&lt;br /&gt;
    @bids = signed_up_topics&lt;br /&gt;
    @num_of_topics = @sign_up_topics.size&lt;br /&gt;
    @assigned_review_maps = []&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: @assignment.id, reviewer_id: @participant.id).each do |review_map|&lt;br /&gt;
      @assigned_review_maps &amp;lt;&amp;lt; review_map&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Explicitly render view since it's in the sign up sheet views&lt;br /&gt;
    render 'sign_up_sheet/review_bids_show'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Additional methods&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-2.-reviewbidshelper-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
 # Assigns bidding topics to reviewers&lt;br /&gt;
  def assign_bidding&lt;br /&gt;
      assignment = validate_assignment(params[:assignment_id])&lt;br /&gt;
&lt;br /&gt;
      reviewers = validate_reviewers(assignment.id)&lt;br /&gt;
      reviewer_ids = reviewers.map(&amp;amp;:id)&lt;br /&gt;
&lt;br /&gt;
      matched_topics = run_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
      if matched_topics.blank?&lt;br /&gt;
        flash[:alert] = 'Topic or assignment is missing'&lt;br /&gt;
        redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      leftover_topics = find_leftover_topics(assignment.id, matched_topics)&lt;br /&gt;
      assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.new.assign_review_topics(matched_topics)&lt;br /&gt;
      assignment.update!(can_choose_topic_to_review: false)&lt;br /&gt;
&lt;br /&gt;
      flash[:notice] = 'Reviewers were successfully assigned to topics.'&lt;br /&gt;
      redirect_back fallback_location: root_path&lt;br /&gt;
    rescue ArgumentError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ArgumentError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: e.message&lt;br /&gt;
    rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::RecordInvalid: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue ActiveRecord::ActiveRecordError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::ActiveRecordError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue StandardError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;StandardError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers. Please try again later.'&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  #service methods here ...&lt;br /&gt;
&lt;br /&gt;
  def find_leftover_topics(assignment_id, matched_topics)&lt;br /&gt;
    all_topic_ids = SignUpTopic.where(assignment_id: assignment_id).pluck(:id)&lt;br /&gt;
    assigned_topic_ids = matched_topics.map { |match| match[:topic_id] }&lt;br /&gt;
&lt;br /&gt;
    # Calculate leftover topics&lt;br /&gt;
    all_topic_ids - assigned_topic_ids&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
    return if leftover_topics.blank?&lt;br /&gt;
&lt;br /&gt;
    # Find non-bidders by excluding already matched reviewers&lt;br /&gt;
    non_bidders = reviewer_ids - matched_topics.keys&lt;br /&gt;
&lt;br /&gt;
    # Assign leftover topics to non-bidders in a round-robin fashion&lt;br /&gt;
    non_bidders.each_with_index do |reviewer_id, index|&lt;br /&gt;
      topic_id = leftover_topics[index % leftover_topics.length]&lt;br /&gt;
      ReviewBid.create(priority: 1, signuptopic_id: topic_id, participant_id: reviewer_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirm Functionality for Single-person Teams ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Make sure your code works on individual assignments, i.e., assignments where participants sign up for topics instead of teams. In this case, a team is created for each participant when they sign up. So the code should work for assignments to which either individuals or teams submit.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to make an update to focus on topic rather than team or have it where teams are updated based on topic id.We will probably utilize sign up topic or a similar object to accomplish this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-5-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bid Status Messaging ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| To make the functionality more intuitive, include a message to say how many students are eligible to submit bids, how many have submitted their bids, when the deadline for submitting bids is.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
The logic for these messages will go into the helper file. It is effectively the same reasoning as our migration of other business/functionality code from the controller to the helper to conform to the SRP. It will go hand in hand with the SignUpTopic portion of the code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-6-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Why are the methods in review_bid.rb class methods? Can we change them to instance methods or move it to helpers?&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-1.-reviewbid-model-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model Updates ====&lt;br /&gt;
Since the current class methods are tied to &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model instances, we will change them to instance methods. There is no need to move them to helpers.&lt;br /&gt;
&lt;br /&gt;
===== Review Bid Model Flow Before Refactoring =====&lt;br /&gt;
This sequence diagram describes how the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; interacts with the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model in assigning review topics to reviewers according to their bids. Once the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; action is initiated by the user, the controller gathers the IDs of reviewers and calls &amp;lt;code&amp;gt;ReviewBid.bidding_data&amp;lt;/code&amp;gt; to gather bidding information. That is sent out to an external bidding algorithm, and the matched topics that come back are used by &amp;lt;code&amp;gt;ReviewBid.assign_review_topics&amp;lt;/code&amp;gt; to create &amp;lt;code&amp;gt;ReviewResponseMap&amp;lt;/code&amp;gt; entries, assigning topics to reviewers for the assignment.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
  belongs_to :assignment, class_name: 'Assignment'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  def self.bidding_data(assignment_id, reviewer_ids)&lt;br /&gt;
    # create basic hash and set basic hash data&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'users' =&amp;gt; {}, 'max_accepted_proposals' =&amp;gt; [] }&lt;br /&gt;
    bidding_data['tid'] = SignUpTopic.where(assignment_id: assignment_id).ids&lt;br /&gt;
    bidding_data['max_accepted_proposals'] = Assignment.where(id: assignment_id).pluck(:num_reviews_allowed).first&lt;br /&gt;
&lt;br /&gt;
    # loop through reviewer_ids to get reviewer specific bidding data&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      bidding_data['users'][reviewer_id] = reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # assigns topics to reviews as matched by the webservice algorithm&lt;br /&gt;
  def self.assign_review_topics(assignment_id, reviewer_ids, matched_topics, _min_num_reviews = 2)&lt;br /&gt;
    # if review response map already created, delete it&lt;br /&gt;
    if ReviewResponseMap.where(reviewed_object_id: assignment_id)&lt;br /&gt;
      ReviewResponseMap.where(reviewed_object_id: assignment_id).destroy_all&lt;br /&gt;
    end&lt;br /&gt;
    # loop through reviewer_ids to assign reviews to each reviewer&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      topics_to_assign = matched_topics[reviewer_id.to_s]&lt;br /&gt;
      topics_to_assign.each do |topic|&lt;br /&gt;
        assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method to assign a single topic to a reviewer&lt;br /&gt;
  def self.assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic).pluck(:team_id).first&lt;br /&gt;
    team_to_review.nil? ? [] : ReviewResponseMap.create(reviewed_object_id: assignment_id, reviewer_id: reviewer_id, reviewee_id: team_to_review, type: 'ReviewResponseMap')&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method for getting individual reviewer_ids bidding data&lt;br /&gt;
  # returns user's bidding data hash&lt;br /&gt;
  def self.reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    reviewer_user_id = AssignmentParticipant.find(reviewer_id).user_id&lt;br /&gt;
    self_topic = SignedUpTeam.topic_id(assignment_id, reviewer_user_id)&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'otid' =&amp;gt; self_topic, 'priority' =&amp;gt; [], 'time' =&amp;gt; [] }&lt;br /&gt;
    bids = ReviewBid.where(participant_id: reviewer_id)&lt;br /&gt;
&lt;br /&gt;
    # loop through each bid for a topic to get specific data&lt;br /&gt;
    bids.each do |bid|&lt;br /&gt;
      bidding_data['tid'] &amp;lt;&amp;lt; bid.signuptopic_id&lt;br /&gt;
      bidding_data['priority'] &amp;lt;&amp;lt; bid.priority&lt;br /&gt;
      bidding_data['time'] &amp;lt;&amp;lt; bid.updated_at&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  # Instance method to get bidding data for this review bid&lt;br /&gt;
  def bidding_data&lt;br /&gt;
    {&lt;br /&gt;
      'tid' =&amp;gt; topic_ids,&lt;br /&gt;
      'otid' =&amp;gt; self_topic,&lt;br /&gt;
      'priority' =&amp;gt; priorities,&lt;br /&gt;
      'time' =&amp;gt; updated_times&lt;br /&gt;
    }&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # bid object to limit db touches for similar data acquisitions&lt;br /&gt;
  def bids&lt;br /&gt;
    @bids ||= ReviewBid.where(participant_id: participant_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def topic_ids&lt;br /&gt;
    bids.pluck(:signuptopic_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def priorities&lt;br /&gt;
    bids.pluck(:priority)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def updated_times&lt;br /&gt;
    bids.pluck(:updated_at)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self_topic&lt;br /&gt;
    return nil unless topic &amp;amp;&amp;amp; topic.assignment_id&lt;br /&gt;
&lt;br /&gt;
    SignedUpTeam.topic_id(topic.assignment_id, participant.user_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign topics to reviews&lt;br /&gt;
  def assign_review_topics(matched_topics)&lt;br /&gt;
    validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: topic.assignment_id).destroy_all&lt;br /&gt;
&lt;br /&gt;
    matched_topics.each do |reviewer_id, topics|&lt;br /&gt;
      Array(topics).each { |topic_id| assign_topic_to_reviewer(reviewer_id, topic_id) }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign a single topic to a reviewer&lt;br /&gt;
  def assign_topic_to_reviewer(reviewer_id, topic_id)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic_id).pluck(:team_id).first&lt;br /&gt;
    return if team_to_review.nil?&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.create(&lt;br /&gt;
      reviewed_object_id: topic.assignment_id,&lt;br /&gt;
      reviewer_id: reviewer_id,&lt;br /&gt;
      reviewee_id: team_to_review,&lt;br /&gt;
      type: 'ReviewResponseMap'&lt;br /&gt;
    )&lt;br /&gt;
    # error handling&lt;br /&gt;
  rescue StandardError =&amp;gt; e&lt;br /&gt;
    Rails.logger.error(&amp;quot;Failed to assign topic #{topic_id} to reviewer #{reviewer_id}: #{e.message}&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
    raise ArgumentError, 'Topic or assignment is missing' if topic.nil? || topic.assignment_id.nil?&lt;br /&gt;
    raise ArgumentError, 'Matched topics must be a Hash' unless matched_topics.is_a?(Hash)&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
Once we changed them to instance methods, we modified their use in the controller code to instantiate the object first.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-3.-schema.db-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;Schema.db&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We also need to remove the foreign key reference to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model since the &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model already has access to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;, and that is the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; we should be referencing. This will also involve modifying the database to remove the foreign key reference to the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Steps required:&lt;br /&gt;
&lt;br /&gt;
# Review the records in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table in the database to see if there are any records and if those records have values for &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;&lt;br /&gt;
# Create a migration file using the command: &amp;lt;code&amp;gt;rails generate migration RemoveAssignmentFromReviewBids&amp;lt;/code&amp;gt;&lt;br /&gt;
# In the generated file, indicate the change that needs to be made using the change method and migration columns &amp;lt;code&amp;gt;remove_foreign_key&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;remove_column&amp;lt;/code&amp;gt;. Migration should be straightforward since there is no cascading behavior (i.e., &amp;lt;code&amp;gt;dependent:destroy&amp;lt;/code&amp;gt;) to worry about.&lt;br /&gt;
# Once comfortable with the updates to the generated file, run the migration using the &amp;lt;code&amp;gt;rails db:migrate&amp;lt;/code&amp;gt; command.&lt;br /&gt;
# Run a query in the DB to ensure no orphaned records are in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table after the updates.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-4.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implement Edge Case Testing ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| In the previous implementation wiki, there are edge cases which are not exhaustively tested. Should test those edge cases thoroughly and add more edge case testing - link&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span class=&amp;quot;mark&amp;quot;&amp;gt;TODO&amp;lt;/span&amp;gt;:We just got initial test results back. We still need to figure out why tests are failing and what may be slipping through the cracks.[[File:Test expertiza.png|411x459px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-8-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design Patterns and Principles ===&lt;br /&gt;
Below are the design patterns and principles we plan to use to address the problem statements outlined in the project requirements:&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
** Controller Responsibility&lt;br /&gt;
*** To address the issue of authentication logic residing in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, we will refactor the code to ensure the controller solely handles request and response flows.&lt;br /&gt;
*** Authentication logic will be moved to dedicated methods in &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt;, making the codebase easier to maintain and test.&lt;br /&gt;
** View Simplification&lt;br /&gt;
*** Currently, the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; view contains excessive business logic, violating SRP.&lt;br /&gt;
*** We will move this logic into helper methods or models, ensuring each component has a single responsibility and improving code readability.&lt;br /&gt;
&lt;br /&gt;
* Separation of Concerns (SoC)&lt;br /&gt;
** By relocating authorization logic out of the controller, we separate authentication from request handling.&lt;br /&gt;
** Adhering to SoC enhances readability and maintainability by allowing each part of the application to focus on its specific role.&lt;br /&gt;
* Don't Repeat Yourself (DRY)&lt;br /&gt;
** We will inspect the existing implementation for code duplication, particularly in the controller actions.&lt;br /&gt;
** Any repetitive code will be refactored or removed to reduce redundancy and improve clarity.&lt;br /&gt;
&lt;br /&gt;
* Model-View-Controller (MVC) Adherence&lt;br /&gt;
** Moving business logic from the view aligns with MVC best practices.&lt;br /&gt;
** By ensuring the view only presents data without processing it, we maintain clear boundaries between the model, view, and controller layers.&lt;br /&gt;
&lt;br /&gt;
* Dependency Inversion Principle (DIP)&lt;br /&gt;
** We plan to update class methods to instance methods, allowing the controller to depend on abstractions rather than concrete implementations.&lt;br /&gt;
** This change leverages Ruby's duck typing, providing flexibility and making the code easier to maintain and test.&lt;br /&gt;
&lt;br /&gt;
** Clear and intentional naming during refactoring will enhance code clarity and maintainability.&lt;br /&gt;
&lt;br /&gt;
== Files Modified/Added ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-1.-controllers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
review_bids_controller.rb&lt;br /&gt;
&lt;br /&gt;
signup_sheet_controller.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-2.-models&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
review_bid.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-3.-helpers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
&lt;br /&gt;
authorization_helper.rb&lt;br /&gt;
&lt;br /&gt;
Review_bids_helper.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;viii.-test-plan&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
The Excel spreadsheet below details our test plan for testing the code we changed or created to ensure code coverage. The process involved reviewing the code and determining all the possible scenarios that needed to be covered based on what the method was doing, then creating test cases accordingly.&lt;br /&gt;
&lt;br /&gt;
* [https://docs.google.com/spreadsheets/d/1iHzOdSwlTtxOtf5VsNdwOaCP8NzYMmiJ/edit?usp=drive_link Open Test Matrix Spreadsheet]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/review_bids_controller_spec.rb ReviewBidsController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/sign_up_sheet_controller_spec.rb SignUpSheetController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/models/review_bid_spec.rb ReviewBid model updated test cases]&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-1.-mentor&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Ed Gehringer&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-2.-members&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Members ===&lt;br /&gt;
&lt;br /&gt;
* Gavin Teague&lt;br /&gt;
* Janice Uwujaren&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xi.-links&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
Previous Wiki:  &lt;br /&gt;
[https://wiki.expertiza.ncsu.edu/index.php/CSC/ECE_517_Fall_2021_-_E2151._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;Link&amp;lt;/u&amp;gt;]  &lt;br /&gt;
&lt;br /&gt;
Reference Link for Webservice Details:  &lt;br /&gt;
[https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xii.-references&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/document/d/1LHVacylQmB15I6VcYUA5nuC7GRRU9szHzZhjlPcE-PA/edit?tab=t.0#heading=h.u0oycoj6e0ev &amp;lt;u&amp;gt;Project Instructions&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;appendix-a-uml-class-diagram-plantuml-code-for-review-bids&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Juwujar</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=161050</id>
		<title>CSC/ECE 517 Fall 2024 - E2485. Allow reviewers to bid on what to review</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=161050"/>
		<updated>2024-12-12T06:02:14Z</updated>

		<summary type="html">&lt;p&gt;Juwujar: /* Refactor ReviewBidsController#index action */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
This project will fix the defects and improve the functionality in Expertiza of the Reviewer Bidding feature. It will make the functionality smoother, more reliable, and align with Expertiza's collaborative learning objectives. &lt;br /&gt;
&lt;br /&gt;
The Review Bidding feature lets reviewers focus on topics they know best or care about most. If someone feels exceptionally qualified or interested in a topic, they can bid to review submissions in that area. This helps ensure reviews are insightful and enjoyable for the reviewer while benefiting the other participants and the overall process.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iii.-problem-statements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Problem Statements ==&lt;br /&gt;
&lt;br /&gt;
Assigning reviews to users on Expertiza involves a complex bidding algorithm that requires optimization before production deployment. Below are key areas we plan to address as per the project requirements:&lt;br /&gt;
* Code Refactoring and Best Practices&lt;br /&gt;
** '''Utilize Authentication Utilities''': Modify the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; function in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to use existing authentication utilities, enhancing code reusability and adhering to the Single Responsibility Principle (SRP) and Separation of Concerns (SoC).&lt;br /&gt;
** '''Eliminate Code Duplication''': Refactor &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; to comply with the Don't Repeat Yourself (DRY) principle.&lt;br /&gt;
** '''Rename Functions for Clarity''': Change &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; to better reflect its purpose.&lt;br /&gt;
** '''Refactor Methods in '''&amp;lt;code&amp;gt;review_bid.rb&amp;lt;/code&amp;gt;''':''': Convert class methods to instance methods or relocate them to helper classes for improved code organization.&lt;br /&gt;
* Functionality Enhancements&lt;br /&gt;
** '''Handle Missing Bids Gracefully''': Adjust the bidding algorithm to accommodate students who do not bid, allowing them to select from the remaining options.&lt;br /&gt;
** '''Support Both Individual and Team Assignments''': Ensure the code functions correctly for participant and team-based assignments when signing up for topics.&lt;br /&gt;
* User Interface Improvements&lt;br /&gt;
** '''Increase Transparency in the Bidding Process''': Display messages indicating how many students are eligible to bid, how many have submitted bids, and the deadline.&lt;br /&gt;
** '''Accurate Bid Status Indicators''': Ensure topic colors change correctly based on the number of outstanding bids, providing clear visual feedback to users.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Exhaustive Edge Case Testing''': Conduct thorough testing for edge cases not previously covered, adding additional test cases as needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iv.-design-goals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Design Goals ==&lt;br /&gt;
&lt;br /&gt;
While addressing the existing defects and enhancing the Reviewer Bidding feature, we plan to adhere to the following design guidelines:&lt;br /&gt;
&lt;br /&gt;
* Code Quality and Maintainability&lt;br /&gt;
** '''Improve Code Structure: '''Implement refactoring strategies to enhance code readability and maintainability, ensuring adherence to best practices like the DRY principle.&lt;br /&gt;
** '''Standardize Authentication Logic: '''Utilize a centralized authentication utility to promote consistency and reusability across the application.&lt;br /&gt;
* System Stability and Flexibility&lt;br /&gt;
** '''Enhance Algorithm Robustness: '''Modify the bidding algorithm to handle scenarios where students do not bid, ensuring the system remains stable under all conditions.&lt;br /&gt;
** '''Ensure Assignment Versatility: '''Design the system to seamlessly support both individual and team assignments without additional configuration.&lt;br /&gt;
* User Experience Enhancements&lt;br /&gt;
** '''Increase Bidding Transparency: '''Develop user interface elements that clearly display bidding status, deadlines, and participation metrics.&lt;br /&gt;
** '''Implement Dynamic Visual Feedback: '''Use real-time visual cues, such as color changes, to inform users about the bidding status instantly.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Develop Comprehensive Test Suites: '''Create extensive automated tests covering edge cases and critical functionalities to guarantee reliable system behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;v.-uml-diagram&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== UML Diagram ==&lt;br /&gt;
&lt;br /&gt;
Below is a visual representation of the models associated with the review bids functionality, depicting the data structures and their relationships.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml class diagram2.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi.-implementation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
'''ReviewBid: '''The &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; manages the bidding process, where participants indicate their preferences for reviewing different topics or assignments. This helps distribute review tasks fairly based on participants' interests.&lt;br /&gt;
&lt;br /&gt;
'''Assignment: '''The &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model represents team or individual assignments in Expertiza. It stores all the essential information required to keep the assignment process organized, like managing participants, setting due dates, forming teams, and overseeing the review process.&lt;br /&gt;
&lt;br /&gt;
'''SignUpTopic: '''The &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt; model identifies specific topics or areas within an assignment for which participants can sign up. It manages the availability of these topics, tracks bids, coordinates team sign-ups, and assigns topics based on both bids and participant availability. &lt;br /&gt;
&lt;br /&gt;
'''Participant: '''The &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model is about the individual users who participate in an assignment. This includes students, instructors, teaching assistants, or other relevant roles. This model is responsible for managing key user information, monitoring interactions with the assignment, keeping track of team memberships, and recording things like badges and review grades.&lt;br /&gt;
&lt;br /&gt;
== Implementation ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1.-decoupling-authorization-logic-from-reviewbidscontroller&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Decoupling Authorization Logic from &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| '''&amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; needs to use the authentication utilities'''&lt;br /&gt;
&lt;br /&gt;
The function &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; should use the available authentication utilities to support the Single Responsibility Principle and Separation of Concerns and to support code reusability.&lt;br /&gt;
&lt;br /&gt;
| '''Authentication Consistency:''' Remove authentication logic in the controller and call logic in the authentication utility class instead to ensure consistent use of the same logic across the application, foster reusability, and eliminate DRY.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
==== Authorization Flow Before Refactoring ====&lt;br /&gt;
The sequence diagram depicts the authorization process within the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;. When a user invokes an action, the controller checks if the action is allowed by verifying the user's role through the &amp;lt;code&amp;gt;current_role_name()&amp;lt;/code&amp;gt; method in the &amp;lt;code&amp;gt;ApplicationController&amp;lt;/code&amp;gt;. Depending on the action and the user's role, the controller either authorizes the user to proceed or denies access, ultimately executing the action logic if authorized and responding to the user.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 1.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We simplified the &amp;lt;code&amp;gt;action_allowed?&amp;lt;/code&amp;gt; method by decoupling authorization logic from the controller to use helper methods in the &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt; module. The &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; should not handle authentication, which is a violation of SRP. &lt;br /&gt;
&lt;br /&gt;
Note: We also made similar updates to the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; methods for &amp;lt;code&amp;gt;SignUpController&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;vertical-align:top;&amp;quot; | Original Code !! style=&amp;quot;vertical-align:top;&amp;quot; | Refactored Code&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index'&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator',&lt;br /&gt;
     'Student'].include? current_role_name and&lt;br /&gt;
        ((%w[list].include? action_name) ? are_needed_authorizations_present?(params[:id], &amp;quot;participant&amp;quot;, &amp;quot;reader&amp;quot;, &amp;quot;submitter&amp;quot;, &amp;quot;reviewer&amp;quot;) : true)&lt;br /&gt;
  else&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator'].include? current_role_name&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index', 'list'&lt;br /&gt;
    current_user_has_student_privileges? &amp;amp;&amp;amp; list_authorization_check&lt;br /&gt;
  else&lt;br /&gt;
    current_user_has_ta_privileges?&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; action ===&lt;br /&gt;
Previously, the &amp;lt;code&amp;gt;ReviewBidsController's index&amp;lt;/code&amp;gt; action was overloaded with responsibilities—it handled everything from locating and validating assignments to fetching review mappings and counting completed reviews. This all-in-one approach made the controller action messy. &lt;br /&gt;
&lt;br /&gt;
We have moved the business logic from the controller action and into dedicated service objects. With this change, we introduced two key service objects: &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; focuses on retrieving the participant along with their assignments, while the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; takes on the task of managing review mappings and calculating review-related stats, like how many reviews have been completed. &lt;br /&gt;
&lt;br /&gt;
This separation of responsibilities aligns with the Single Responsibility Principle. The logic for fetching assignments, retrieving review mappings, and counting reviews is now more flexible and reusable throughout the application. For example, we used methods in the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; in both the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; and the &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;, helping to avoid code duplication.The article [https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial Rails Service Objects Tutorial]&lt;br /&gt;
 served as a guide on when and how to use service objects in Rails.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
  return unless current_user_id?(@participant.user_id)&lt;br /&gt;
  @assignment = @participant.assignment&lt;br /&gt;
  @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
  # Finding how many reviews have been completed&lt;br /&gt;
  @num_reviews_completed = 0&lt;br /&gt;
  @review_mappings.each do |map|&lt;br /&gt;
    @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # render view for completing reviews after review bidding has been completed&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @assignment = participant_service.assignment&lt;br /&gt;
  unless @assignment.is_a?(Assignment)&lt;br /&gt;
    flash[:error] = 'Assignment not found.'&lt;br /&gt;
    redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  @review_mappings = review_service.review_mappings&lt;br /&gt;
  @num_reviews_completed = review_service.review_counts[:completed]&lt;br /&gt;
&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Optimize &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Action functionality ===&lt;br /&gt;
In the earlier version of the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, the server only needed a couple of key pieces of information: an array of topic IDs (params[:topic]) and the participant ID (params[:id}). From there, it would figure out the assignment_id by using the first topic as a reference. This is risky because the &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt; associated with the topic may not be a valid &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
With the new implementation, we structured the parameters to include the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt;, an array of topic IDs, and &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;. This change ensures that the server always receives complete and reliable information, preventing issues caused by missing or inconsistent data. It also removes the need for the server to perform unnecessary lookups, as in the case of &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;, allowing the controller to focus on receiving requests and rendering appropriate responses.&lt;br /&gt;
&lt;br /&gt;
While these updates have cleaned up the set_priority action, they still contain a lot of business logic, like validations and priority updates. To make the code even more maintainable, there’s room for further refactoring—perhaps moving some of this logic into a service class or model method to streamline the controller even more.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | Old Code&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    if params[:topic].nil?&lt;br /&gt;
      ReviewBid.where(participant_id: params[:id]).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      participant = AssignmentParticipant.find_by(id: params[:id])&lt;br /&gt;
      assignment_id = SignUpTopic.find(params[:topic].first).assignment.id&lt;br /&gt;
      @bids = ReviewBid.where(participant_id: params[:id])&lt;br /&gt;
      signed_up_topics = ReviewBid.where(participant_id: params[:id]).map(&amp;amp;:signuptopic_id)&lt;br /&gt;
      signed_up_topics -= params[:topic].map(&amp;amp;:to_i)&lt;br /&gt;
      signed_up_topics.each do |topic|&lt;br /&gt;
        ReviewBid.where(signuptopic_id: topic, participant_id: params[:id]).destroy_all&lt;br /&gt;
      end&lt;br /&gt;
      params[:topic].each_with_index do |topic_id, index|&lt;br /&gt;
        bid_existence = ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id])&lt;br /&gt;
        if bid_existence.empty?&lt;br /&gt;
          ReviewBid.create(priority: index + 1, signuptopic_id: topic_id, participant_id: params[:id], assignment_id: assignment_id)&lt;br /&gt;
        else&lt;br /&gt;
          ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id]).update_all(priority: index + 1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to action: 'show', assignment_id: params[:assignment_id], id: params[:id]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    participant_id = params[:participant_id].to_i&lt;br /&gt;
    selected_topic_ids = params[:topic]&amp;amp;.map(&amp;amp;:to_i) || []&lt;br /&gt;
    assignment_id = params[:assignment_id].to_i&lt;br /&gt;
&lt;br /&gt;
    unless current_user_can_modify_participant?(participant_id)&lt;br /&gt;
      flash[:error] = 'You are not authorized to modify these bids.'&lt;br /&gt;
      respond_to do |format|&lt;br /&gt;
        format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
        format.json { render json: { status: 'unauthorized' }, status: :unauthorized }&lt;br /&gt;
      end&lt;br /&gt;
      return&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    if selected_topic_ids.empty?&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      assignment = Assignment.find_by(id: assignment_id)&lt;br /&gt;
      if assignment.nil?&lt;br /&gt;
        flash[:error] = &amp;quot;Invalid assignment.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_assignment' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      unless SignUpTopic.where(id: selected_topic_ids, assignment_id: assignment_id).count == selected_topic_ids.size&lt;br /&gt;
        flash[:error] = &amp;quot;One or more selected topics are invalid.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_topics' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).where.not(signuptopic_id: selected_topic_ids).destroy_all&lt;br /&gt;
&lt;br /&gt;
      selected_topic_ids.each_with_index do |topic_id, index|&lt;br /&gt;
        bid = ReviewBid.find_or_initialize_by(signuptopic_id: topic_id, participant_id: participant_id)&lt;br /&gt;
        bid.priority = index + 1&lt;br /&gt;
        bid.assignment_id = assignment_id&lt;br /&gt;
        bid.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_to action: 'show', assignment_id: assignment_id, id: participant_id, notice: 'Review bids updated successfully.' }&lt;br /&gt;
      format.json { render json: { status: 'success' }, status: :ok }&lt;br /&gt;
    end&lt;br /&gt;
  rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
    flash[:error] = &amp;quot;Failed to update priorities: #{e.message}&amp;quot;&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
      format.json { render json: { status: 'error', message: e.message }, status: :unprocessable_entity }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Client Side Changes to Support &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Changes ====&lt;br /&gt;
We refactored the client-side code to ensure it passed the correct information from the view to the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action. On the client side, we now grab the topicIDs and send them as &amp;lt;code&amp;gt;selected_topic_ids&amp;lt;/code&amp;gt;, allowing the controller to figure out which topics were selected and in what order.  &lt;br /&gt;
&lt;br /&gt;
When working with jQuery, AJAX is awesome because it lets us update the server without reloading the page, making the user experience smooth and responsive. When we pull the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, we can grab it along with other data, like the list of topics, and include it as a single request. This way, we don’t have to rely on the URL to know which participant we’re working with, which makes the code cleaner.&lt;br /&gt;
&lt;br /&gt;
We also added an update handler so that any time the order of the selected topic changes in the view, information is sent to the appropriate controller action. This ensures that the controller and the view remain in sync, with real-time updates, and includes a mechanism for providing feedback if something goes wrong.&lt;br /&gt;
&lt;br /&gt;
Note: One interesting aspect of the application was the mix of JavaScript and CoffeeScript code. It wasn't clear why both types of code were used.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery -&amp;gt;&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: -&amp;gt;&lt;br /&gt;
      $.post($(this).data('update-url'), $(this).sortable('serialize'))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery(document).ready ($) -&amp;gt;&lt;br /&gt;
  sendUpdatedOrder = -&amp;gt;&lt;br /&gt;
    sortedIDs = $(this).sortable('toArray')&lt;br /&gt;
&lt;br /&gt;
    topicIDs = sortedIDs.map (id) -&amp;gt;&lt;br /&gt;
      id.replace 'topic_', ''&lt;br /&gt;
&lt;br /&gt;
    participant_id = $(this).data('participant-id')&lt;br /&gt;
&lt;br /&gt;
    assignment_id = new URL($(this).data('update-url'), window.location.origin).searchParams.get('assignment_id')&lt;br /&gt;
&lt;br /&gt;
    $.ajax&lt;br /&gt;
      type: 'POST'&lt;br /&gt;
      url: $(this).data('update-url')&lt;br /&gt;
      data:&lt;br /&gt;
        topic: topicIDs&lt;br /&gt;
        participant_id: participant_id&lt;br /&gt;
        assignment_id: assignment_id&lt;br /&gt;
      success: (response) -&amp;gt;&lt;br /&gt;
        console.log 'Selections updated successfully.'&lt;br /&gt;
      error: (xhr, status, error) -&amp;gt;&lt;br /&gt;
        alert 'An error occurred while saving your selections. Please try again.'&lt;br /&gt;
        console.error error&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: sendUpdatedOrder&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-1.-signupsheetcontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; Updates ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; method renders the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; action However, the view is associated with the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # provides variables for reviewing page located at views/review_bids/others_work.html.erb&lt;br /&gt;
  def index&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
    return unless current_user_id?(@participant.user_id)&lt;br /&gt;
&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
    # Finding how many reviews have been completed&lt;br /&gt;
    @num_reviews_completed = 0&lt;br /&gt;
    @review_mappings.each do |map|&lt;br /&gt;
      @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # render view for completing reviews after review bidding has been completed&lt;br /&gt;
    render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
  end  &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-2.-reviewbidsotherswork-view-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;review_bids_others_work.html.erb&amp;lt;/code&amp;gt; View Updates ====&lt;br /&gt;
The view is technically part of the SignUpSheetController, but it's being rendered by the ReviewBidsController, which initially threw us off. After chatting with the professor, we decided not to change this view. He indicated that the plan is to reuse these views for both assignment and review bidding.&lt;br /&gt;
&lt;br /&gt;
We suggest building the assignment bidding and review bidding features separately first to ensure each one works independently. Once that’s done, we can clean things up by combining any shared functionality. This could include creating shared views or even introducing an abstract model to handle both types of bidding more efficiently. It could also mean replacing the ReviewBidsController with something more generic, like a BidsController, to keep things reusable and easier to maintain down the line.&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Rename &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; method ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; should be &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt;&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:''' Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The primary activity here is to refactor the method name &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; and fix the hardcoded URL. However, the match topic bidding algorithm no longer works since Heroku is no longer free, per the professor. The reference link below has detailed information about the web service that we can leverage to gain that understanding.&lt;br /&gt;
&lt;br /&gt;
Reference Link for Webservice Details: [https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Updated Implementation ====&lt;br /&gt;
The original requirement was to rename the run_bidding_algorithm method to assign_reviewers, but after digging into the code, we realized that renaming it didn’t make sense. The method runs the bidding algorithm to match topics, and there’s already a separate method to handle assigning review topics.&lt;br /&gt;
&lt;br /&gt;
However, we improved the logic in the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; method (which calls the private &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt;) by introducing a separate &amp;lt;code&amp;gt;BiddingAlgorithmService&amp;lt;/code&amp;gt; object. This keeps the business logic out of the controller action, which makes things cleaner and more maintainable.&lt;br /&gt;
&lt;br /&gt;
==== review_bids_controller ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def run_bidding_algorithm&lt;br /&gt;
    review_bid = ReviewBid.new&lt;br /&gt;
    bidding_data = review_bid.bidding_data&lt;br /&gt;
    matched_topics = BiddingAlgorithmService.new(bidding_data).run&lt;br /&gt;
    raise ArgumentError, 'Failed to assign reviewers. Please try again later.' unless matched_topics&lt;br /&gt;
&lt;br /&gt;
    matched_topics&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== bidding_algorithm_service ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# frozen_string_literal: true&lt;br /&gt;
&lt;br /&gt;
# The `BiddingAlgorithmService` run the bid assignment algorithm&lt;br /&gt;
# Sends student IDs, topic IDs, student preferences, and timestamps to the web service&lt;br /&gt;
# The web service returns the matched assignments in the JSON response body&lt;br /&gt;
class BiddingAlgorithmService&lt;br /&gt;
  SERVICE_URL = 'http://app-csc517.herokuapp.com/match_topics'.freeze&lt;br /&gt;
&lt;br /&gt;
  # MOCK_DATA is based on the documentation detailing how the webservice behaves.&lt;br /&gt;
  # Reference: https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review#Webservice&lt;br /&gt;
  MOCK_DATA = {&lt;br /&gt;
    36239 =&amp;gt; [3970, 3972, 3975],&lt;br /&gt;
    36240 =&amp;gt; [3973, 3974, 3972],&lt;br /&gt;
    36241 =&amp;gt; [3969, 3971, 3972],&lt;br /&gt;
    36242 =&amp;gt; [3969, 3971, 3973],&lt;br /&gt;
    36243 =&amp;gt; [3969, 3970, 3971]&lt;br /&gt;
  }.freeze&lt;br /&gt;
&lt;br /&gt;
  def initialize(bidding_data)&lt;br /&gt;
    @bidding_data = bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def run&lt;br /&gt;
    return MOCK_DATA if Rails.application.config.use_mock_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
    perform_request&lt;br /&gt;
  rescue RestClient::ExceptionWithResponse, JSON::ParserError&lt;br /&gt;
    false&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def perform_request&lt;br /&gt;
    response = RestClient.post(&lt;br /&gt;
      SERVICE_URL,&lt;br /&gt;
      @bidding_data.to_json,&lt;br /&gt;
      content_type: :json,&lt;br /&gt;
      accept: :json&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    validate_response(response)&lt;br /&gt;
    JSON.parse(response.body)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def validate_response(response)&lt;br /&gt;
    raise 'Invalid response format' unless response.headers[:content_type] &amp;amp;&amp;amp; response.headers[:content_type].include?('application/json')&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Resolve Functionality Issues for Non Bidders ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Currently it doesn't work if some student does not bid. In this case, algorithm needs to be fixed to ignore anyone who didn’t bid, and let them choose from what’s left over.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to do several things within this controller. We will set a default state for non-bidders that we can point to to give them options to select topics to review from the leftovers. We will consolidate &amp;lt;code&amp;gt;@sign_up_topics&amp;lt;/code&amp;gt; to resolve the DRY violation. Lastly, we will remove business logic from the show method and push it to a new helper to conform to the SRP.&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # Additional method&lt;br /&gt;
&lt;br /&gt;
  # Provides variables for review bidding page&lt;br /&gt;
  def show&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id].to_i)&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @sign_up_topics = SignUpTopic.where(assignment_id: @assignment.id, private_to: nil)&lt;br /&gt;
    my_topic = SignedUpTeam.topic_id(@participant.parent_id, @participant.user_id)&lt;br /&gt;
    @sign_up_topics -= SignUpTopic.where(assignment_id: @assignment.id, id: my_topic)&lt;br /&gt;
    @num_participants = AssignmentParticipant.where(parent_id: @assignment.id).count&lt;br /&gt;
    @selected_topics = nil # This is used to list the topics assigned to review (i.e., select == assigned)&lt;br /&gt;
    @bids = ReviewBid.where(participant_id: @participant, assignment_id: @assignment.id)&lt;br /&gt;
    signed_up_topics = []&lt;br /&gt;
    &lt;br /&gt;
    @bids.each do |bid|&lt;br /&gt;
      sign_up_topic = SignUpTopic.find_by(id: bid.signuptopic_id)&lt;br /&gt;
      signed_up_topics &amp;lt;&amp;lt; sign_up_topic if sign_up_topic&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    signed_up_topics &amp;amp;= @sign_up_topics&lt;br /&gt;
    @sign_up_topics -= signed_up_topics&lt;br /&gt;
    @bids = signed_up_topics&lt;br /&gt;
    @num_of_topics = @sign_up_topics.size&lt;br /&gt;
    @assigned_review_maps = []&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: @assignment.id, reviewer_id: @participant.id).each do |review_map|&lt;br /&gt;
      @assigned_review_maps &amp;lt;&amp;lt; review_map&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Explicitly render view since it's in the sign up sheet views&lt;br /&gt;
    render 'sign_up_sheet/review_bids_show'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Additional methods&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-2.-reviewbidshelper-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
 # Assigns bidding topics to reviewers&lt;br /&gt;
  def assign_bidding&lt;br /&gt;
      assignment = validate_assignment(params[:assignment_id])&lt;br /&gt;
&lt;br /&gt;
      reviewers = validate_reviewers(assignment.id)&lt;br /&gt;
      reviewer_ids = reviewers.map(&amp;amp;:id)&lt;br /&gt;
&lt;br /&gt;
      matched_topics = run_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
      if matched_topics.blank?&lt;br /&gt;
        flash[:alert] = 'Topic or assignment is missing'&lt;br /&gt;
        redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      leftover_topics = find_leftover_topics(assignment.id, matched_topics)&lt;br /&gt;
      assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.new.assign_review_topics(matched_topics)&lt;br /&gt;
      assignment.update!(can_choose_topic_to_review: false)&lt;br /&gt;
&lt;br /&gt;
      flash[:notice] = 'Reviewers were successfully assigned to topics.'&lt;br /&gt;
      redirect_back fallback_location: root_path&lt;br /&gt;
    rescue ArgumentError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ArgumentError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: e.message&lt;br /&gt;
    rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::RecordInvalid: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue ActiveRecord::ActiveRecordError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::ActiveRecordError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue StandardError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;StandardError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers. Please try again later.'&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  #service methods here ...&lt;br /&gt;
&lt;br /&gt;
  def find_leftover_topics(assignment_id, matched_topics)&lt;br /&gt;
    all_topic_ids = SignUpTopic.where(assignment_id: assignment_id).pluck(:id)&lt;br /&gt;
    assigned_topic_ids = matched_topics.map { |match| match[:topic_id] }&lt;br /&gt;
&lt;br /&gt;
    # Calculate leftover topics&lt;br /&gt;
    all_topic_ids - assigned_topic_ids&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
    return if leftover_topics.blank?&lt;br /&gt;
&lt;br /&gt;
    # Find non-bidders by excluding already matched reviewers&lt;br /&gt;
    non_bidders = reviewer_ids - matched_topics.keys&lt;br /&gt;
&lt;br /&gt;
    # Assign leftover topics to non-bidders in a round-robin fashion&lt;br /&gt;
    non_bidders.each_with_index do |reviewer_id, index|&lt;br /&gt;
      topic_id = leftover_topics[index % leftover_topics.length]&lt;br /&gt;
      ReviewBid.create(priority: 1, signuptopic_id: topic_id, participant_id: reviewer_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirm Functionality for Single-person Teams ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Make sure your code works on individual assignments, i.e., assignments where participants sign up for topics instead of teams. In this case, a team is created for each participant when they sign up. So the code should work for assignments to which either individuals or teams submit.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to make an update to focus on topic rather than team or have it where teams are updated based on topic id.We will probably utilize sign up topic or a similar object to accomplish this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-5-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bid Status Messaging ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| To make the functionality more intuitive, include a message to say how many students are eligible to submit bids, how many have submitted their bids, when the deadline for submitting bids is.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
The logic for these messages will go into the helper file. It is effectively the same reasoning as our migration of other business/functionality code from the controller to the helper to conform to the SRP. It will go hand in hand with the SignUpTopic portion of the code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-6-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Why are the methods in review_bid.rb class methods? Can we change them to instance methods or move it to helpers?&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-1.-reviewbid-model-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model Updates ====&lt;br /&gt;
Since the current class methods are tied to &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model instances, we will change them to instance methods. There is no need to move them to helpers.&lt;br /&gt;
&lt;br /&gt;
===== Review Bid Model Flow Before Refactoring =====&lt;br /&gt;
This sequence diagram describes how the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; interacts with the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model in assigning review topics to reviewers according to their bids. Once the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; action is initiated by the user, the controller gathers the IDs of reviewers and calls &amp;lt;code&amp;gt;ReviewBid.bidding_data&amp;lt;/code&amp;gt; to gather bidding information. That is sent out to an external bidding algorithm, and the matched topics that come back are used by &amp;lt;code&amp;gt;ReviewBid.assign_review_topics&amp;lt;/code&amp;gt; to create &amp;lt;code&amp;gt;ReviewResponseMap&amp;lt;/code&amp;gt; entries, assigning topics to reviewers for the assignment.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
  belongs_to :assignment, class_name: 'Assignment'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  def self.bidding_data(assignment_id, reviewer_ids)&lt;br /&gt;
    # create basic hash and set basic hash data&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'users' =&amp;gt; {}, 'max_accepted_proposals' =&amp;gt; [] }&lt;br /&gt;
    bidding_data['tid'] = SignUpTopic.where(assignment_id: assignment_id).ids&lt;br /&gt;
    bidding_data['max_accepted_proposals'] = Assignment.where(id: assignment_id).pluck(:num_reviews_allowed).first&lt;br /&gt;
&lt;br /&gt;
    # loop through reviewer_ids to get reviewer specific bidding data&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      bidding_data['users'][reviewer_id] = reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # assigns topics to reviews as matched by the webservice algorithm&lt;br /&gt;
  def self.assign_review_topics(assignment_id, reviewer_ids, matched_topics, _min_num_reviews = 2)&lt;br /&gt;
    # if review response map already created, delete it&lt;br /&gt;
    if ReviewResponseMap.where(reviewed_object_id: assignment_id)&lt;br /&gt;
      ReviewResponseMap.where(reviewed_object_id: assignment_id).destroy_all&lt;br /&gt;
    end&lt;br /&gt;
    # loop through reviewer_ids to assign reviews to each reviewer&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      topics_to_assign = matched_topics[reviewer_id.to_s]&lt;br /&gt;
      topics_to_assign.each do |topic|&lt;br /&gt;
        assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method to assign a single topic to a reviewer&lt;br /&gt;
  def self.assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic).pluck(:team_id).first&lt;br /&gt;
    team_to_review.nil? ? [] : ReviewResponseMap.create(reviewed_object_id: assignment_id, reviewer_id: reviewer_id, reviewee_id: team_to_review, type: 'ReviewResponseMap')&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method for getting individual reviewer_ids bidding data&lt;br /&gt;
  # returns user's bidding data hash&lt;br /&gt;
  def self.reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    reviewer_user_id = AssignmentParticipant.find(reviewer_id).user_id&lt;br /&gt;
    self_topic = SignedUpTeam.topic_id(assignment_id, reviewer_user_id)&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'otid' =&amp;gt; self_topic, 'priority' =&amp;gt; [], 'time' =&amp;gt; [] }&lt;br /&gt;
    bids = ReviewBid.where(participant_id: reviewer_id)&lt;br /&gt;
&lt;br /&gt;
    # loop through each bid for a topic to get specific data&lt;br /&gt;
    bids.each do |bid|&lt;br /&gt;
      bidding_data['tid'] &amp;lt;&amp;lt; bid.signuptopic_id&lt;br /&gt;
      bidding_data['priority'] &amp;lt;&amp;lt; bid.priority&lt;br /&gt;
      bidding_data['time'] &amp;lt;&amp;lt; bid.updated_at&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  # Instance method to get bidding data for this review bid&lt;br /&gt;
  def bidding_data&lt;br /&gt;
    {&lt;br /&gt;
      'tid' =&amp;gt; topic_ids,&lt;br /&gt;
      'otid' =&amp;gt; self_topic,&lt;br /&gt;
      'priority' =&amp;gt; priorities,&lt;br /&gt;
      'time' =&amp;gt; updated_times&lt;br /&gt;
    }&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # bid object to limit db touches for similar data acquisitions&lt;br /&gt;
  def bids&lt;br /&gt;
    @bids ||= ReviewBid.where(participant_id: participant_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def topic_ids&lt;br /&gt;
    bids.pluck(:signuptopic_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def priorities&lt;br /&gt;
    bids.pluck(:priority)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def updated_times&lt;br /&gt;
    bids.pluck(:updated_at)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self_topic&lt;br /&gt;
    return nil unless topic &amp;amp;&amp;amp; topic.assignment_id&lt;br /&gt;
&lt;br /&gt;
    SignedUpTeam.topic_id(topic.assignment_id, participant.user_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign topics to reviews&lt;br /&gt;
  def assign_review_topics(matched_topics)&lt;br /&gt;
    validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: topic.assignment_id).destroy_all&lt;br /&gt;
&lt;br /&gt;
    matched_topics.each do |reviewer_id, topics|&lt;br /&gt;
      Array(topics).each { |topic_id| assign_topic_to_reviewer(reviewer_id, topic_id) }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign a single topic to a reviewer&lt;br /&gt;
  def assign_topic_to_reviewer(reviewer_id, topic_id)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic_id).pluck(:team_id).first&lt;br /&gt;
    return if team_to_review.nil?&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.create(&lt;br /&gt;
      reviewed_object_id: topic.assignment_id,&lt;br /&gt;
      reviewer_id: reviewer_id,&lt;br /&gt;
      reviewee_id: team_to_review,&lt;br /&gt;
      type: 'ReviewResponseMap'&lt;br /&gt;
    )&lt;br /&gt;
    # error handling&lt;br /&gt;
  rescue StandardError =&amp;gt; e&lt;br /&gt;
    Rails.logger.error(&amp;quot;Failed to assign topic #{topic_id} to reviewer #{reviewer_id}: #{e.message}&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
    raise ArgumentError, 'Topic or assignment is missing' if topic.nil? || topic.assignment_id.nil?&lt;br /&gt;
    raise ArgumentError, 'Matched topics must be a Hash' unless matched_topics.is_a?(Hash)&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
Once we changed them to instance methods, we modified their use in the controller code to instantiate the object first.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-3.-schema.db-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;Schema.db&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We also need to remove the foreign key reference to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model since the &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model already has access to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;, and that is the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; we should be referencing. This will also involve modifying the database to remove the foreign key reference to the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Steps required:&lt;br /&gt;
&lt;br /&gt;
# Review the records in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table in the database to see if there are any records and if those records have values for &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;&lt;br /&gt;
# Create a migration file using the command: &amp;lt;code&amp;gt;rails generate migration RemoveAssignmentFromReviewBids&amp;lt;/code&amp;gt;&lt;br /&gt;
# In the generated file, indicate the change that needs to be made using the change method and migration columns &amp;lt;code&amp;gt;remove_foreign_key&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;remove_column&amp;lt;/code&amp;gt;. Migration should be straightforward since there is no cascading behavior (i.e., &amp;lt;code&amp;gt;dependent:destroy&amp;lt;/code&amp;gt;) to worry about.&lt;br /&gt;
# Once comfortable with the updates to the generated file, run the migration using the &amp;lt;code&amp;gt;rails db:migrate&amp;lt;/code&amp;gt; command.&lt;br /&gt;
# Run a query in the DB to ensure no orphaned records are in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table after the updates.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-4.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implement Edge Case Testing ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| In the previous implementation wiki, there are edge cases which are not exhaustively tested. Should test those edge cases thoroughly and add more edge case testing - link&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span class=&amp;quot;mark&amp;quot;&amp;gt;TODO&amp;lt;/span&amp;gt;:We just got initial test results back. We still need to figure out why tests are failing and what may be slipping through the cracks.[[File:Test expertiza.png|411x459px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-8-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design Patterns and Principles ===&lt;br /&gt;
Below are the design patterns and principles we plan to use to address the problem statements outlined in the project requirements:&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
** Controller Responsibility&lt;br /&gt;
*** To address the issue of authentication logic residing in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, we will refactor the code to ensure the controller solely handles request and response flows.&lt;br /&gt;
*** Authentication logic will be moved to dedicated methods in &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt;, making the codebase easier to maintain and test.&lt;br /&gt;
** View Simplification&lt;br /&gt;
*** Currently, the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; view contains excessive business logic, violating SRP.&lt;br /&gt;
*** We will move this logic into helper methods or models, ensuring each component has a single responsibility and improving code readability.&lt;br /&gt;
&lt;br /&gt;
* Separation of Concerns (SoC)&lt;br /&gt;
** By relocating authorization logic out of the controller, we separate authentication from request handling.&lt;br /&gt;
** Adhering to SoC enhances readability and maintainability by allowing each part of the application to focus on its specific role.&lt;br /&gt;
* Don't Repeat Yourself (DRY)&lt;br /&gt;
** We will inspect the existing implementation for code duplication, particularly in the controller actions.&lt;br /&gt;
** Any repetitive code will be refactored or removed to reduce redundancy and improve clarity.&lt;br /&gt;
&lt;br /&gt;
* Model-View-Controller (MVC) Adherence&lt;br /&gt;
** Moving business logic from the view aligns with MVC best practices.&lt;br /&gt;
** By ensuring the view only presents data without processing it, we maintain clear boundaries between the model, view, and controller layers.&lt;br /&gt;
&lt;br /&gt;
* Dependency Inversion Principle (DIP)&lt;br /&gt;
** We plan to update class methods to instance methods, allowing the controller to depend on abstractions rather than concrete implementations.&lt;br /&gt;
** This change leverages Ruby's duck typing, providing flexibility and making the code easier to maintain and test.&lt;br /&gt;
&lt;br /&gt;
** Clear and intentional naming during refactoring will enhance code clarity and maintainability.&lt;br /&gt;
&lt;br /&gt;
== Files Modified/Added ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-1.-controllers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
review_bids_controller.rb&lt;br /&gt;
&lt;br /&gt;
signup_sheet_controller.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-2.-models&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
review_bid.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-3.-helpers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
&lt;br /&gt;
authorization_helper.rb&lt;br /&gt;
&lt;br /&gt;
Review_bids_helper.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;viii.-test-plan&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
The Excel spreadsheet below details our test plan for testing the code we changed or created to ensure code coverage. The process involved reviewing the code and determining all the possible scenarios that needed to be covered based on what the method was doing, then creating test cases accordingly.&lt;br /&gt;
&lt;br /&gt;
* [https://docs.google.com/spreadsheets/d/1iHzOdSwlTtxOtf5VsNdwOaCP8NzYMmiJ/edit?usp=drive_link Open Test Matrix Spreadsheet]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/review_bids_controller_spec.rb ReviewBidsController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/sign_up_sheet_controller_spec.rb SignUpSheetController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/models/review_bid_spec.rb ReviewBid model updated test cases]&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-1.-mentor&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Ed Gehringer&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-2.-members&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Members ===&lt;br /&gt;
&lt;br /&gt;
* Gavin Teague&lt;br /&gt;
* Janice Uwujaren&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xi.-links&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
Previous Wiki:  &lt;br /&gt;
[https://wiki.expertiza.ncsu.edu/index.php/CSC/ECE_517_Fall_2021_-_E2151._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;Link&amp;lt;/u&amp;gt;]  &lt;br /&gt;
&lt;br /&gt;
Reference Link for Webservice Details:  &lt;br /&gt;
[https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xii.-references&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/document/d/1LHVacylQmB15I6VcYUA5nuC7GRRU9szHzZhjlPcE-PA/edit?tab=t.0#heading=h.u0oycoj6e0ev &amp;lt;u&amp;gt;Project Instructions&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;appendix-a-uml-class-diagram-plantuml-code-for-review-bids&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Juwujar</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=161049</id>
		<title>CSC/ECE 517 Fall 2024 - E2485. Allow reviewers to bid on what to review</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=161049"/>
		<updated>2024-12-12T06:00:07Z</updated>

		<summary type="html">&lt;p&gt;Juwujar: /* Links */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
This project will fix the defects and improve the functionality in Expertiza of the Reviewer Bidding feature. It will make the functionality smoother, more reliable, and align with Expertiza's collaborative learning objectives. &lt;br /&gt;
&lt;br /&gt;
The Review Bidding feature lets reviewers focus on topics they know best or care about most. If someone feels exceptionally qualified or interested in a topic, they can bid to review submissions in that area. This helps ensure reviews are insightful and enjoyable for the reviewer while benefiting the other participants and the overall process.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iii.-problem-statements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Problem Statements ==&lt;br /&gt;
&lt;br /&gt;
Assigning reviews to users on Expertiza involves a complex bidding algorithm that requires optimization before production deployment. Below are key areas we plan to address as per the project requirements:&lt;br /&gt;
* Code Refactoring and Best Practices&lt;br /&gt;
** '''Utilize Authentication Utilities''': Modify the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; function in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to use existing authentication utilities, enhancing code reusability and adhering to the Single Responsibility Principle (SRP) and Separation of Concerns (SoC).&lt;br /&gt;
** '''Eliminate Code Duplication''': Refactor &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; to comply with the Don't Repeat Yourself (DRY) principle.&lt;br /&gt;
** '''Rename Functions for Clarity''': Change &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; to better reflect its purpose.&lt;br /&gt;
** '''Refactor Methods in '''&amp;lt;code&amp;gt;review_bid.rb&amp;lt;/code&amp;gt;''':''': Convert class methods to instance methods or relocate them to helper classes for improved code organization.&lt;br /&gt;
* Functionality Enhancements&lt;br /&gt;
** '''Handle Missing Bids Gracefully''': Adjust the bidding algorithm to accommodate students who do not bid, allowing them to select from the remaining options.&lt;br /&gt;
** '''Support Both Individual and Team Assignments''': Ensure the code functions correctly for participant and team-based assignments when signing up for topics.&lt;br /&gt;
* User Interface Improvements&lt;br /&gt;
** '''Increase Transparency in the Bidding Process''': Display messages indicating how many students are eligible to bid, how many have submitted bids, and the deadline.&lt;br /&gt;
** '''Accurate Bid Status Indicators''': Ensure topic colors change correctly based on the number of outstanding bids, providing clear visual feedback to users.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Exhaustive Edge Case Testing''': Conduct thorough testing for edge cases not previously covered, adding additional test cases as needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iv.-design-goals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Design Goals ==&lt;br /&gt;
&lt;br /&gt;
While addressing the existing defects and enhancing the Reviewer Bidding feature, we plan to adhere to the following design guidelines:&lt;br /&gt;
&lt;br /&gt;
* Code Quality and Maintainability&lt;br /&gt;
** '''Improve Code Structure: '''Implement refactoring strategies to enhance code readability and maintainability, ensuring adherence to best practices like the DRY principle.&lt;br /&gt;
** '''Standardize Authentication Logic: '''Utilize a centralized authentication utility to promote consistency and reusability across the application.&lt;br /&gt;
* System Stability and Flexibility&lt;br /&gt;
** '''Enhance Algorithm Robustness: '''Modify the bidding algorithm to handle scenarios where students do not bid, ensuring the system remains stable under all conditions.&lt;br /&gt;
** '''Ensure Assignment Versatility: '''Design the system to seamlessly support both individual and team assignments without additional configuration.&lt;br /&gt;
* User Experience Enhancements&lt;br /&gt;
** '''Increase Bidding Transparency: '''Develop user interface elements that clearly display bidding status, deadlines, and participation metrics.&lt;br /&gt;
** '''Implement Dynamic Visual Feedback: '''Use real-time visual cues, such as color changes, to inform users about the bidding status instantly.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Develop Comprehensive Test Suites: '''Create extensive automated tests covering edge cases and critical functionalities to guarantee reliable system behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;v.-uml-diagram&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== UML Diagram ==&lt;br /&gt;
&lt;br /&gt;
Below is a visual representation of the models associated with the review bids functionality, depicting the data structures and their relationships.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml class diagram2.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi.-implementation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
'''ReviewBid: '''The &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; manages the bidding process, where participants indicate their preferences for reviewing different topics or assignments. This helps distribute review tasks fairly based on participants' interests.&lt;br /&gt;
&lt;br /&gt;
'''Assignment: '''The &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model represents team or individual assignments in Expertiza. It stores all the essential information required to keep the assignment process organized, like managing participants, setting due dates, forming teams, and overseeing the review process.&lt;br /&gt;
&lt;br /&gt;
'''SignUpTopic: '''The &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt; model identifies specific topics or areas within an assignment for which participants can sign up. It manages the availability of these topics, tracks bids, coordinates team sign-ups, and assigns topics based on both bids and participant availability. &lt;br /&gt;
&lt;br /&gt;
'''Participant: '''The &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model is about the individual users who participate in an assignment. This includes students, instructors, teaching assistants, or other relevant roles. This model is responsible for managing key user information, monitoring interactions with the assignment, keeping track of team memberships, and recording things like badges and review grades.&lt;br /&gt;
&lt;br /&gt;
== Implementation ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1.-decoupling-authorization-logic-from-reviewbidscontroller&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Decoupling Authorization Logic from &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| '''&amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; needs to use the authentication utilities'''&lt;br /&gt;
&lt;br /&gt;
The function &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; should use the available authentication utilities to support the Single Responsibility Principle and Separation of Concerns and to support code reusability.&lt;br /&gt;
&lt;br /&gt;
| '''Authentication Consistency:''' Remove authentication logic in the controller and call logic in the authentication utility class instead to ensure consistent use of the same logic across the application, foster reusability, and eliminate DRY.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
==== Authorization Flow Before Refactoring ====&lt;br /&gt;
The sequence diagram depicts the authorization process within the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;. When a user invokes an action, the controller checks if the action is allowed by verifying the user's role through the &amp;lt;code&amp;gt;current_role_name()&amp;lt;/code&amp;gt; method in the &amp;lt;code&amp;gt;ApplicationController&amp;lt;/code&amp;gt;. Depending on the action and the user's role, the controller either authorizes the user to proceed or denies access, ultimately executing the action logic if authorized and responding to the user.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 1.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We simplified the &amp;lt;code&amp;gt;action_allowed?&amp;lt;/code&amp;gt; method by decoupling authorization logic from the controller to use helper methods in the &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt; module. The &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; should not handle authentication, which is a violation of SRP. &lt;br /&gt;
&lt;br /&gt;
Note: We also made similar updates to the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; methods for &amp;lt;code&amp;gt;SignUpController&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;vertical-align:top;&amp;quot; | Original Code !! style=&amp;quot;vertical-align:top;&amp;quot; | Refactored Code&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index'&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator',&lt;br /&gt;
     'Student'].include? current_role_name and&lt;br /&gt;
        ((%w[list].include? action_name) ? are_needed_authorizations_present?(params[:id], &amp;quot;participant&amp;quot;, &amp;quot;reader&amp;quot;, &amp;quot;submitter&amp;quot;, &amp;quot;reviewer&amp;quot;) : true)&lt;br /&gt;
  else&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator'].include? current_role_name&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index', 'list'&lt;br /&gt;
    current_user_has_student_privileges? &amp;amp;&amp;amp; list_authorization_check&lt;br /&gt;
  else&lt;br /&gt;
    current_user_has_ta_privileges?&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; action ===&lt;br /&gt;
Previously, the &amp;lt;code&amp;gt;ReviewBidsController's index&amp;lt;/code&amp;gt; action was overloaded with responsibilities—it handled everything from locating and validating assignments to fetching review mappings and counting completed reviews. This all-in-one approach made the controller action messy. &lt;br /&gt;
&lt;br /&gt;
We have moved the business logic from the controller action and into dedicated service objects. With this change, we introduced two key service objects: &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; focuses on retrieving the participant along with their assignments, while the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; takes on the task of managing review mappings and calculating review-related stats, like how many reviews have been completed. &lt;br /&gt;
&lt;br /&gt;
This separation of responsibilities aligns with the Single Responsibility Principle. The logic for fetching assignments, retrieving review mappings, and counting reviews is now more flexible and reusable throughout the application. For example, we used methods in the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; in both the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; and the &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;, helping to avoid code duplication.The article https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial served as a guide on when and how to use service objects in Rails.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
  return unless current_user_id?(@participant.user_id)&lt;br /&gt;
  @assignment = @participant.assignment&lt;br /&gt;
  @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
  # Finding how many reviews have been completed&lt;br /&gt;
  @num_reviews_completed = 0&lt;br /&gt;
  @review_mappings.each do |map|&lt;br /&gt;
    @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # render view for completing reviews after review bidding has been completed&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @assignment = participant_service.assignment&lt;br /&gt;
  unless @assignment.is_a?(Assignment)&lt;br /&gt;
    flash[:error] = 'Assignment not found.'&lt;br /&gt;
    redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  @review_mappings = review_service.review_mappings&lt;br /&gt;
  @num_reviews_completed = review_service.review_counts[:completed]&lt;br /&gt;
&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Optimize &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Action functionality ===&lt;br /&gt;
In the earlier version of the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, the server only needed a couple of key pieces of information: an array of topic IDs (params[:topic]) and the participant ID (params[:id}). From there, it would figure out the assignment_id by using the first topic as a reference. This is risky because the &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt; associated with the topic may not be a valid &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
With the new implementation, we structured the parameters to include the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt;, an array of topic IDs, and &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;. This change ensures that the server always receives complete and reliable information, preventing issues caused by missing or inconsistent data. It also removes the need for the server to perform unnecessary lookups, as in the case of &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;, allowing the controller to focus on receiving requests and rendering appropriate responses.&lt;br /&gt;
&lt;br /&gt;
While these updates have cleaned up the set_priority action, they still contain a lot of business logic, like validations and priority updates. To make the code even more maintainable, there’s room for further refactoring—perhaps moving some of this logic into a service class or model method to streamline the controller even more.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | Old Code&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    if params[:topic].nil?&lt;br /&gt;
      ReviewBid.where(participant_id: params[:id]).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      participant = AssignmentParticipant.find_by(id: params[:id])&lt;br /&gt;
      assignment_id = SignUpTopic.find(params[:topic].first).assignment.id&lt;br /&gt;
      @bids = ReviewBid.where(participant_id: params[:id])&lt;br /&gt;
      signed_up_topics = ReviewBid.where(participant_id: params[:id]).map(&amp;amp;:signuptopic_id)&lt;br /&gt;
      signed_up_topics -= params[:topic].map(&amp;amp;:to_i)&lt;br /&gt;
      signed_up_topics.each do |topic|&lt;br /&gt;
        ReviewBid.where(signuptopic_id: topic, participant_id: params[:id]).destroy_all&lt;br /&gt;
      end&lt;br /&gt;
      params[:topic].each_with_index do |topic_id, index|&lt;br /&gt;
        bid_existence = ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id])&lt;br /&gt;
        if bid_existence.empty?&lt;br /&gt;
          ReviewBid.create(priority: index + 1, signuptopic_id: topic_id, participant_id: params[:id], assignment_id: assignment_id)&lt;br /&gt;
        else&lt;br /&gt;
          ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id]).update_all(priority: index + 1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to action: 'show', assignment_id: params[:assignment_id], id: params[:id]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    participant_id = params[:participant_id].to_i&lt;br /&gt;
    selected_topic_ids = params[:topic]&amp;amp;.map(&amp;amp;:to_i) || []&lt;br /&gt;
    assignment_id = params[:assignment_id].to_i&lt;br /&gt;
&lt;br /&gt;
    unless current_user_can_modify_participant?(participant_id)&lt;br /&gt;
      flash[:error] = 'You are not authorized to modify these bids.'&lt;br /&gt;
      respond_to do |format|&lt;br /&gt;
        format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
        format.json { render json: { status: 'unauthorized' }, status: :unauthorized }&lt;br /&gt;
      end&lt;br /&gt;
      return&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    if selected_topic_ids.empty?&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      assignment = Assignment.find_by(id: assignment_id)&lt;br /&gt;
      if assignment.nil?&lt;br /&gt;
        flash[:error] = &amp;quot;Invalid assignment.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_assignment' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      unless SignUpTopic.where(id: selected_topic_ids, assignment_id: assignment_id).count == selected_topic_ids.size&lt;br /&gt;
        flash[:error] = &amp;quot;One or more selected topics are invalid.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_topics' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).where.not(signuptopic_id: selected_topic_ids).destroy_all&lt;br /&gt;
&lt;br /&gt;
      selected_topic_ids.each_with_index do |topic_id, index|&lt;br /&gt;
        bid = ReviewBid.find_or_initialize_by(signuptopic_id: topic_id, participant_id: participant_id)&lt;br /&gt;
        bid.priority = index + 1&lt;br /&gt;
        bid.assignment_id = assignment_id&lt;br /&gt;
        bid.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_to action: 'show', assignment_id: assignment_id, id: participant_id, notice: 'Review bids updated successfully.' }&lt;br /&gt;
      format.json { render json: { status: 'success' }, status: :ok }&lt;br /&gt;
    end&lt;br /&gt;
  rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
    flash[:error] = &amp;quot;Failed to update priorities: #{e.message}&amp;quot;&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
      format.json { render json: { status: 'error', message: e.message }, status: :unprocessable_entity }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Client Side Changes to Support &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Changes ====&lt;br /&gt;
We refactored the client-side code to ensure it passed the correct information from the view to the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action. On the client side, we now grab the topicIDs and send them as &amp;lt;code&amp;gt;selected_topic_ids&amp;lt;/code&amp;gt;, allowing the controller to figure out which topics were selected and in what order.  &lt;br /&gt;
&lt;br /&gt;
When working with jQuery, AJAX is awesome because it lets us update the server without reloading the page, making the user experience smooth and responsive. When we pull the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, we can grab it along with other data, like the list of topics, and include it as a single request. This way, we don’t have to rely on the URL to know which participant we’re working with, which makes the code cleaner.&lt;br /&gt;
&lt;br /&gt;
We also added an update handler so that any time the order of the selected topic changes in the view, information is sent to the appropriate controller action. This ensures that the controller and the view remain in sync, with real-time updates, and includes a mechanism for providing feedback if something goes wrong.&lt;br /&gt;
&lt;br /&gt;
Note: One interesting aspect of the application was the mix of JavaScript and CoffeeScript code. It wasn't clear why both types of code were used.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery -&amp;gt;&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: -&amp;gt;&lt;br /&gt;
      $.post($(this).data('update-url'), $(this).sortable('serialize'))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery(document).ready ($) -&amp;gt;&lt;br /&gt;
  sendUpdatedOrder = -&amp;gt;&lt;br /&gt;
    sortedIDs = $(this).sortable('toArray')&lt;br /&gt;
&lt;br /&gt;
    topicIDs = sortedIDs.map (id) -&amp;gt;&lt;br /&gt;
      id.replace 'topic_', ''&lt;br /&gt;
&lt;br /&gt;
    participant_id = $(this).data('participant-id')&lt;br /&gt;
&lt;br /&gt;
    assignment_id = new URL($(this).data('update-url'), window.location.origin).searchParams.get('assignment_id')&lt;br /&gt;
&lt;br /&gt;
    $.ajax&lt;br /&gt;
      type: 'POST'&lt;br /&gt;
      url: $(this).data('update-url')&lt;br /&gt;
      data:&lt;br /&gt;
        topic: topicIDs&lt;br /&gt;
        participant_id: participant_id&lt;br /&gt;
        assignment_id: assignment_id&lt;br /&gt;
      success: (response) -&amp;gt;&lt;br /&gt;
        console.log 'Selections updated successfully.'&lt;br /&gt;
      error: (xhr, status, error) -&amp;gt;&lt;br /&gt;
        alert 'An error occurred while saving your selections. Please try again.'&lt;br /&gt;
        console.error error&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: sendUpdatedOrder&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-1.-signupsheetcontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; Updates ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; method renders the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; action However, the view is associated with the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # provides variables for reviewing page located at views/review_bids/others_work.html.erb&lt;br /&gt;
  def index&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
    return unless current_user_id?(@participant.user_id)&lt;br /&gt;
&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
    # Finding how many reviews have been completed&lt;br /&gt;
    @num_reviews_completed = 0&lt;br /&gt;
    @review_mappings.each do |map|&lt;br /&gt;
      @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # render view for completing reviews after review bidding has been completed&lt;br /&gt;
    render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
  end  &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-2.-reviewbidsotherswork-view-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;review_bids_others_work.html.erb&amp;lt;/code&amp;gt; View Updates ====&lt;br /&gt;
The view is technically part of the SignUpSheetController, but it's being rendered by the ReviewBidsController, which initially threw us off. After chatting with the professor, we decided not to change this view. He indicated that the plan is to reuse these views for both assignment and review bidding.&lt;br /&gt;
&lt;br /&gt;
We suggest building the assignment bidding and review bidding features separately first to ensure each one works independently. Once that’s done, we can clean things up by combining any shared functionality. This could include creating shared views or even introducing an abstract model to handle both types of bidding more efficiently. It could also mean replacing the ReviewBidsController with something more generic, like a BidsController, to keep things reusable and easier to maintain down the line.&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Rename &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; method ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; should be &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt;&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:''' Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The primary activity here is to refactor the method name &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; and fix the hardcoded URL. However, the match topic bidding algorithm no longer works since Heroku is no longer free, per the professor. The reference link below has detailed information about the web service that we can leverage to gain that understanding.&lt;br /&gt;
&lt;br /&gt;
Reference Link for Webservice Details: [https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Updated Implementation ====&lt;br /&gt;
The original requirement was to rename the run_bidding_algorithm method to assign_reviewers, but after digging into the code, we realized that renaming it didn’t make sense. The method runs the bidding algorithm to match topics, and there’s already a separate method to handle assigning review topics.&lt;br /&gt;
&lt;br /&gt;
However, we improved the logic in the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; method (which calls the private &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt;) by introducing a separate &amp;lt;code&amp;gt;BiddingAlgorithmService&amp;lt;/code&amp;gt; object. This keeps the business logic out of the controller action, which makes things cleaner and more maintainable.&lt;br /&gt;
&lt;br /&gt;
==== review_bids_controller ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def run_bidding_algorithm&lt;br /&gt;
    review_bid = ReviewBid.new&lt;br /&gt;
    bidding_data = review_bid.bidding_data&lt;br /&gt;
    matched_topics = BiddingAlgorithmService.new(bidding_data).run&lt;br /&gt;
    raise ArgumentError, 'Failed to assign reviewers. Please try again later.' unless matched_topics&lt;br /&gt;
&lt;br /&gt;
    matched_topics&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== bidding_algorithm_service ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# frozen_string_literal: true&lt;br /&gt;
&lt;br /&gt;
# The `BiddingAlgorithmService` run the bid assignment algorithm&lt;br /&gt;
# Sends student IDs, topic IDs, student preferences, and timestamps to the web service&lt;br /&gt;
# The web service returns the matched assignments in the JSON response body&lt;br /&gt;
class BiddingAlgorithmService&lt;br /&gt;
  SERVICE_URL = 'http://app-csc517.herokuapp.com/match_topics'.freeze&lt;br /&gt;
&lt;br /&gt;
  # MOCK_DATA is based on the documentation detailing how the webservice behaves.&lt;br /&gt;
  # Reference: https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review#Webservice&lt;br /&gt;
  MOCK_DATA = {&lt;br /&gt;
    36239 =&amp;gt; [3970, 3972, 3975],&lt;br /&gt;
    36240 =&amp;gt; [3973, 3974, 3972],&lt;br /&gt;
    36241 =&amp;gt; [3969, 3971, 3972],&lt;br /&gt;
    36242 =&amp;gt; [3969, 3971, 3973],&lt;br /&gt;
    36243 =&amp;gt; [3969, 3970, 3971]&lt;br /&gt;
  }.freeze&lt;br /&gt;
&lt;br /&gt;
  def initialize(bidding_data)&lt;br /&gt;
    @bidding_data = bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def run&lt;br /&gt;
    return MOCK_DATA if Rails.application.config.use_mock_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
    perform_request&lt;br /&gt;
  rescue RestClient::ExceptionWithResponse, JSON::ParserError&lt;br /&gt;
    false&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def perform_request&lt;br /&gt;
    response = RestClient.post(&lt;br /&gt;
      SERVICE_URL,&lt;br /&gt;
      @bidding_data.to_json,&lt;br /&gt;
      content_type: :json,&lt;br /&gt;
      accept: :json&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    validate_response(response)&lt;br /&gt;
    JSON.parse(response.body)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def validate_response(response)&lt;br /&gt;
    raise 'Invalid response format' unless response.headers[:content_type] &amp;amp;&amp;amp; response.headers[:content_type].include?('application/json')&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Resolve Functionality Issues for Non Bidders ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Currently it doesn't work if some student does not bid. In this case, algorithm needs to be fixed to ignore anyone who didn’t bid, and let them choose from what’s left over.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to do several things within this controller. We will set a default state for non-bidders that we can point to to give them options to select topics to review from the leftovers. We will consolidate &amp;lt;code&amp;gt;@sign_up_topics&amp;lt;/code&amp;gt; to resolve the DRY violation. Lastly, we will remove business logic from the show method and push it to a new helper to conform to the SRP.&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # Additional method&lt;br /&gt;
&lt;br /&gt;
  # Provides variables for review bidding page&lt;br /&gt;
  def show&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id].to_i)&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @sign_up_topics = SignUpTopic.where(assignment_id: @assignment.id, private_to: nil)&lt;br /&gt;
    my_topic = SignedUpTeam.topic_id(@participant.parent_id, @participant.user_id)&lt;br /&gt;
    @sign_up_topics -= SignUpTopic.where(assignment_id: @assignment.id, id: my_topic)&lt;br /&gt;
    @num_participants = AssignmentParticipant.where(parent_id: @assignment.id).count&lt;br /&gt;
    @selected_topics = nil # This is used to list the topics assigned to review (i.e., select == assigned)&lt;br /&gt;
    @bids = ReviewBid.where(participant_id: @participant, assignment_id: @assignment.id)&lt;br /&gt;
    signed_up_topics = []&lt;br /&gt;
    &lt;br /&gt;
    @bids.each do |bid|&lt;br /&gt;
      sign_up_topic = SignUpTopic.find_by(id: bid.signuptopic_id)&lt;br /&gt;
      signed_up_topics &amp;lt;&amp;lt; sign_up_topic if sign_up_topic&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    signed_up_topics &amp;amp;= @sign_up_topics&lt;br /&gt;
    @sign_up_topics -= signed_up_topics&lt;br /&gt;
    @bids = signed_up_topics&lt;br /&gt;
    @num_of_topics = @sign_up_topics.size&lt;br /&gt;
    @assigned_review_maps = []&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: @assignment.id, reviewer_id: @participant.id).each do |review_map|&lt;br /&gt;
      @assigned_review_maps &amp;lt;&amp;lt; review_map&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Explicitly render view since it's in the sign up sheet views&lt;br /&gt;
    render 'sign_up_sheet/review_bids_show'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Additional methods&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-2.-reviewbidshelper-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
 # Assigns bidding topics to reviewers&lt;br /&gt;
  def assign_bidding&lt;br /&gt;
      assignment = validate_assignment(params[:assignment_id])&lt;br /&gt;
&lt;br /&gt;
      reviewers = validate_reviewers(assignment.id)&lt;br /&gt;
      reviewer_ids = reviewers.map(&amp;amp;:id)&lt;br /&gt;
&lt;br /&gt;
      matched_topics = run_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
      if matched_topics.blank?&lt;br /&gt;
        flash[:alert] = 'Topic or assignment is missing'&lt;br /&gt;
        redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      leftover_topics = find_leftover_topics(assignment.id, matched_topics)&lt;br /&gt;
      assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.new.assign_review_topics(matched_topics)&lt;br /&gt;
      assignment.update!(can_choose_topic_to_review: false)&lt;br /&gt;
&lt;br /&gt;
      flash[:notice] = 'Reviewers were successfully assigned to topics.'&lt;br /&gt;
      redirect_back fallback_location: root_path&lt;br /&gt;
    rescue ArgumentError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ArgumentError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: e.message&lt;br /&gt;
    rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::RecordInvalid: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue ActiveRecord::ActiveRecordError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::ActiveRecordError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue StandardError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;StandardError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers. Please try again later.'&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  #service methods here ...&lt;br /&gt;
&lt;br /&gt;
  def find_leftover_topics(assignment_id, matched_topics)&lt;br /&gt;
    all_topic_ids = SignUpTopic.where(assignment_id: assignment_id).pluck(:id)&lt;br /&gt;
    assigned_topic_ids = matched_topics.map { |match| match[:topic_id] }&lt;br /&gt;
&lt;br /&gt;
    # Calculate leftover topics&lt;br /&gt;
    all_topic_ids - assigned_topic_ids&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
    return if leftover_topics.blank?&lt;br /&gt;
&lt;br /&gt;
    # Find non-bidders by excluding already matched reviewers&lt;br /&gt;
    non_bidders = reviewer_ids - matched_topics.keys&lt;br /&gt;
&lt;br /&gt;
    # Assign leftover topics to non-bidders in a round-robin fashion&lt;br /&gt;
    non_bidders.each_with_index do |reviewer_id, index|&lt;br /&gt;
      topic_id = leftover_topics[index % leftover_topics.length]&lt;br /&gt;
      ReviewBid.create(priority: 1, signuptopic_id: topic_id, participant_id: reviewer_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirm Functionality for Single-person Teams ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Make sure your code works on individual assignments, i.e., assignments where participants sign up for topics instead of teams. In this case, a team is created for each participant when they sign up. So the code should work for assignments to which either individuals or teams submit.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to make an update to focus on topic rather than team or have it where teams are updated based on topic id.We will probably utilize sign up topic or a similar object to accomplish this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-5-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bid Status Messaging ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| To make the functionality more intuitive, include a message to say how many students are eligible to submit bids, how many have submitted their bids, when the deadline for submitting bids is.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
The logic for these messages will go into the helper file. It is effectively the same reasoning as our migration of other business/functionality code from the controller to the helper to conform to the SRP. It will go hand in hand with the SignUpTopic portion of the code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-6-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Why are the methods in review_bid.rb class methods? Can we change them to instance methods or move it to helpers?&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-1.-reviewbid-model-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model Updates ====&lt;br /&gt;
Since the current class methods are tied to &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model instances, we will change them to instance methods. There is no need to move them to helpers.&lt;br /&gt;
&lt;br /&gt;
===== Review Bid Model Flow Before Refactoring =====&lt;br /&gt;
This sequence diagram describes how the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; interacts with the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model in assigning review topics to reviewers according to their bids. Once the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; action is initiated by the user, the controller gathers the IDs of reviewers and calls &amp;lt;code&amp;gt;ReviewBid.bidding_data&amp;lt;/code&amp;gt; to gather bidding information. That is sent out to an external bidding algorithm, and the matched topics that come back are used by &amp;lt;code&amp;gt;ReviewBid.assign_review_topics&amp;lt;/code&amp;gt; to create &amp;lt;code&amp;gt;ReviewResponseMap&amp;lt;/code&amp;gt; entries, assigning topics to reviewers for the assignment.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
  belongs_to :assignment, class_name: 'Assignment'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  def self.bidding_data(assignment_id, reviewer_ids)&lt;br /&gt;
    # create basic hash and set basic hash data&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'users' =&amp;gt; {}, 'max_accepted_proposals' =&amp;gt; [] }&lt;br /&gt;
    bidding_data['tid'] = SignUpTopic.where(assignment_id: assignment_id).ids&lt;br /&gt;
    bidding_data['max_accepted_proposals'] = Assignment.where(id: assignment_id).pluck(:num_reviews_allowed).first&lt;br /&gt;
&lt;br /&gt;
    # loop through reviewer_ids to get reviewer specific bidding data&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      bidding_data['users'][reviewer_id] = reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # assigns topics to reviews as matched by the webservice algorithm&lt;br /&gt;
  def self.assign_review_topics(assignment_id, reviewer_ids, matched_topics, _min_num_reviews = 2)&lt;br /&gt;
    # if review response map already created, delete it&lt;br /&gt;
    if ReviewResponseMap.where(reviewed_object_id: assignment_id)&lt;br /&gt;
      ReviewResponseMap.where(reviewed_object_id: assignment_id).destroy_all&lt;br /&gt;
    end&lt;br /&gt;
    # loop through reviewer_ids to assign reviews to each reviewer&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      topics_to_assign = matched_topics[reviewer_id.to_s]&lt;br /&gt;
      topics_to_assign.each do |topic|&lt;br /&gt;
        assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method to assign a single topic to a reviewer&lt;br /&gt;
  def self.assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic).pluck(:team_id).first&lt;br /&gt;
    team_to_review.nil? ? [] : ReviewResponseMap.create(reviewed_object_id: assignment_id, reviewer_id: reviewer_id, reviewee_id: team_to_review, type: 'ReviewResponseMap')&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method for getting individual reviewer_ids bidding data&lt;br /&gt;
  # returns user's bidding data hash&lt;br /&gt;
  def self.reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    reviewer_user_id = AssignmentParticipant.find(reviewer_id).user_id&lt;br /&gt;
    self_topic = SignedUpTeam.topic_id(assignment_id, reviewer_user_id)&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'otid' =&amp;gt; self_topic, 'priority' =&amp;gt; [], 'time' =&amp;gt; [] }&lt;br /&gt;
    bids = ReviewBid.where(participant_id: reviewer_id)&lt;br /&gt;
&lt;br /&gt;
    # loop through each bid for a topic to get specific data&lt;br /&gt;
    bids.each do |bid|&lt;br /&gt;
      bidding_data['tid'] &amp;lt;&amp;lt; bid.signuptopic_id&lt;br /&gt;
      bidding_data['priority'] &amp;lt;&amp;lt; bid.priority&lt;br /&gt;
      bidding_data['time'] &amp;lt;&amp;lt; bid.updated_at&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  # Instance method to get bidding data for this review bid&lt;br /&gt;
  def bidding_data&lt;br /&gt;
    {&lt;br /&gt;
      'tid' =&amp;gt; topic_ids,&lt;br /&gt;
      'otid' =&amp;gt; self_topic,&lt;br /&gt;
      'priority' =&amp;gt; priorities,&lt;br /&gt;
      'time' =&amp;gt; updated_times&lt;br /&gt;
    }&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # bid object to limit db touches for similar data acquisitions&lt;br /&gt;
  def bids&lt;br /&gt;
    @bids ||= ReviewBid.where(participant_id: participant_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def topic_ids&lt;br /&gt;
    bids.pluck(:signuptopic_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def priorities&lt;br /&gt;
    bids.pluck(:priority)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def updated_times&lt;br /&gt;
    bids.pluck(:updated_at)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self_topic&lt;br /&gt;
    return nil unless topic &amp;amp;&amp;amp; topic.assignment_id&lt;br /&gt;
&lt;br /&gt;
    SignedUpTeam.topic_id(topic.assignment_id, participant.user_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign topics to reviews&lt;br /&gt;
  def assign_review_topics(matched_topics)&lt;br /&gt;
    validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: topic.assignment_id).destroy_all&lt;br /&gt;
&lt;br /&gt;
    matched_topics.each do |reviewer_id, topics|&lt;br /&gt;
      Array(topics).each { |topic_id| assign_topic_to_reviewer(reviewer_id, topic_id) }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign a single topic to a reviewer&lt;br /&gt;
  def assign_topic_to_reviewer(reviewer_id, topic_id)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic_id).pluck(:team_id).first&lt;br /&gt;
    return if team_to_review.nil?&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.create(&lt;br /&gt;
      reviewed_object_id: topic.assignment_id,&lt;br /&gt;
      reviewer_id: reviewer_id,&lt;br /&gt;
      reviewee_id: team_to_review,&lt;br /&gt;
      type: 'ReviewResponseMap'&lt;br /&gt;
    )&lt;br /&gt;
    # error handling&lt;br /&gt;
  rescue StandardError =&amp;gt; e&lt;br /&gt;
    Rails.logger.error(&amp;quot;Failed to assign topic #{topic_id} to reviewer #{reviewer_id}: #{e.message}&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
    raise ArgumentError, 'Topic or assignment is missing' if topic.nil? || topic.assignment_id.nil?&lt;br /&gt;
    raise ArgumentError, 'Matched topics must be a Hash' unless matched_topics.is_a?(Hash)&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
Once we changed them to instance methods, we modified their use in the controller code to instantiate the object first.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-3.-schema.db-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;Schema.db&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We also need to remove the foreign key reference to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model since the &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model already has access to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;, and that is the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; we should be referencing. This will also involve modifying the database to remove the foreign key reference to the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Steps required:&lt;br /&gt;
&lt;br /&gt;
# Review the records in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table in the database to see if there are any records and if those records have values for &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;&lt;br /&gt;
# Create a migration file using the command: &amp;lt;code&amp;gt;rails generate migration RemoveAssignmentFromReviewBids&amp;lt;/code&amp;gt;&lt;br /&gt;
# In the generated file, indicate the change that needs to be made using the change method and migration columns &amp;lt;code&amp;gt;remove_foreign_key&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;remove_column&amp;lt;/code&amp;gt;. Migration should be straightforward since there is no cascading behavior (i.e., &amp;lt;code&amp;gt;dependent:destroy&amp;lt;/code&amp;gt;) to worry about.&lt;br /&gt;
# Once comfortable with the updates to the generated file, run the migration using the &amp;lt;code&amp;gt;rails db:migrate&amp;lt;/code&amp;gt; command.&lt;br /&gt;
# Run a query in the DB to ensure no orphaned records are in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table after the updates.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-4.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implement Edge Case Testing ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| In the previous implementation wiki, there are edge cases which are not exhaustively tested. Should test those edge cases thoroughly and add more edge case testing - link&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span class=&amp;quot;mark&amp;quot;&amp;gt;TODO&amp;lt;/span&amp;gt;:We just got initial test results back. We still need to figure out why tests are failing and what may be slipping through the cracks.[[File:Test expertiza.png|411x459px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-8-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design Patterns and Principles ===&lt;br /&gt;
Below are the design patterns and principles we plan to use to address the problem statements outlined in the project requirements:&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
** Controller Responsibility&lt;br /&gt;
*** To address the issue of authentication logic residing in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, we will refactor the code to ensure the controller solely handles request and response flows.&lt;br /&gt;
*** Authentication logic will be moved to dedicated methods in &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt;, making the codebase easier to maintain and test.&lt;br /&gt;
** View Simplification&lt;br /&gt;
*** Currently, the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; view contains excessive business logic, violating SRP.&lt;br /&gt;
*** We will move this logic into helper methods or models, ensuring each component has a single responsibility and improving code readability.&lt;br /&gt;
&lt;br /&gt;
* Separation of Concerns (SoC)&lt;br /&gt;
** By relocating authorization logic out of the controller, we separate authentication from request handling.&lt;br /&gt;
** Adhering to SoC enhances readability and maintainability by allowing each part of the application to focus on its specific role.&lt;br /&gt;
* Don't Repeat Yourself (DRY)&lt;br /&gt;
** We will inspect the existing implementation for code duplication, particularly in the controller actions.&lt;br /&gt;
** Any repetitive code will be refactored or removed to reduce redundancy and improve clarity.&lt;br /&gt;
&lt;br /&gt;
* Model-View-Controller (MVC) Adherence&lt;br /&gt;
** Moving business logic from the view aligns with MVC best practices.&lt;br /&gt;
** By ensuring the view only presents data without processing it, we maintain clear boundaries between the model, view, and controller layers.&lt;br /&gt;
&lt;br /&gt;
* Dependency Inversion Principle (DIP)&lt;br /&gt;
** We plan to update class methods to instance methods, allowing the controller to depend on abstractions rather than concrete implementations.&lt;br /&gt;
** This change leverages Ruby's duck typing, providing flexibility and making the code easier to maintain and test.&lt;br /&gt;
&lt;br /&gt;
** Clear and intentional naming during refactoring will enhance code clarity and maintainability.&lt;br /&gt;
&lt;br /&gt;
== Files Modified/Added ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-1.-controllers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
review_bids_controller.rb&lt;br /&gt;
&lt;br /&gt;
signup_sheet_controller.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-2.-models&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
review_bid.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-3.-helpers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
&lt;br /&gt;
authorization_helper.rb&lt;br /&gt;
&lt;br /&gt;
Review_bids_helper.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;viii.-test-plan&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
The Excel spreadsheet below details our test plan for testing the code we changed or created to ensure code coverage. The process involved reviewing the code and determining all the possible scenarios that needed to be covered based on what the method was doing, then creating test cases accordingly.&lt;br /&gt;
&lt;br /&gt;
* [https://docs.google.com/spreadsheets/d/1iHzOdSwlTtxOtf5VsNdwOaCP8NzYMmiJ/edit?usp=drive_link Open Test Matrix Spreadsheet]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/review_bids_controller_spec.rb ReviewBidsController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/sign_up_sheet_controller_spec.rb SignUpSheetController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/models/review_bid_spec.rb ReviewBid model updated test cases]&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-1.-mentor&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Ed Gehringer&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-2.-members&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Members ===&lt;br /&gt;
&lt;br /&gt;
* Gavin Teague&lt;br /&gt;
* Janice Uwujaren&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xi.-links&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
Previous Wiki:  &lt;br /&gt;
[https://wiki.expertiza.ncsu.edu/index.php/CSC/ECE_517_Fall_2021_-_E2151._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;Link&amp;lt;/u&amp;gt;]  &lt;br /&gt;
&lt;br /&gt;
Reference Link for Webservice Details:  &lt;br /&gt;
[https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xii.-references&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/document/d/1LHVacylQmB15I6VcYUA5nuC7GRRU9szHzZhjlPcE-PA/edit?tab=t.0#heading=h.u0oycoj6e0ev &amp;lt;u&amp;gt;Project Instructions&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;appendix-a-uml-class-diagram-plantuml-code-for-review-bids&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Juwujar</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=161048</id>
		<title>CSC/ECE 517 Fall 2024 - E2485. Allow reviewers to bid on what to review</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=161048"/>
		<updated>2024-12-12T05:58:59Z</updated>

		<summary type="html">&lt;p&gt;Juwujar: /* Links */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
This project will fix the defects and improve the functionality in Expertiza of the Reviewer Bidding feature. It will make the functionality smoother, more reliable, and align with Expertiza's collaborative learning objectives. &lt;br /&gt;
&lt;br /&gt;
The Review Bidding feature lets reviewers focus on topics they know best or care about most. If someone feels exceptionally qualified or interested in a topic, they can bid to review submissions in that area. This helps ensure reviews are insightful and enjoyable for the reviewer while benefiting the other participants and the overall process.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iii.-problem-statements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Problem Statements ==&lt;br /&gt;
&lt;br /&gt;
Assigning reviews to users on Expertiza involves a complex bidding algorithm that requires optimization before production deployment. Below are key areas we plan to address as per the project requirements:&lt;br /&gt;
* Code Refactoring and Best Practices&lt;br /&gt;
** '''Utilize Authentication Utilities''': Modify the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; function in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to use existing authentication utilities, enhancing code reusability and adhering to the Single Responsibility Principle (SRP) and Separation of Concerns (SoC).&lt;br /&gt;
** '''Eliminate Code Duplication''': Refactor &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; to comply with the Don't Repeat Yourself (DRY) principle.&lt;br /&gt;
** '''Rename Functions for Clarity''': Change &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; to better reflect its purpose.&lt;br /&gt;
** '''Refactor Methods in '''&amp;lt;code&amp;gt;review_bid.rb&amp;lt;/code&amp;gt;''':''': Convert class methods to instance methods or relocate them to helper classes for improved code organization.&lt;br /&gt;
* Functionality Enhancements&lt;br /&gt;
** '''Handle Missing Bids Gracefully''': Adjust the bidding algorithm to accommodate students who do not bid, allowing them to select from the remaining options.&lt;br /&gt;
** '''Support Both Individual and Team Assignments''': Ensure the code functions correctly for participant and team-based assignments when signing up for topics.&lt;br /&gt;
* User Interface Improvements&lt;br /&gt;
** '''Increase Transparency in the Bidding Process''': Display messages indicating how many students are eligible to bid, how many have submitted bids, and the deadline.&lt;br /&gt;
** '''Accurate Bid Status Indicators''': Ensure topic colors change correctly based on the number of outstanding bids, providing clear visual feedback to users.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Exhaustive Edge Case Testing''': Conduct thorough testing for edge cases not previously covered, adding additional test cases as needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iv.-design-goals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Design Goals ==&lt;br /&gt;
&lt;br /&gt;
While addressing the existing defects and enhancing the Reviewer Bidding feature, we plan to adhere to the following design guidelines:&lt;br /&gt;
&lt;br /&gt;
* Code Quality and Maintainability&lt;br /&gt;
** '''Improve Code Structure: '''Implement refactoring strategies to enhance code readability and maintainability, ensuring adherence to best practices like the DRY principle.&lt;br /&gt;
** '''Standardize Authentication Logic: '''Utilize a centralized authentication utility to promote consistency and reusability across the application.&lt;br /&gt;
* System Stability and Flexibility&lt;br /&gt;
** '''Enhance Algorithm Robustness: '''Modify the bidding algorithm to handle scenarios where students do not bid, ensuring the system remains stable under all conditions.&lt;br /&gt;
** '''Ensure Assignment Versatility: '''Design the system to seamlessly support both individual and team assignments without additional configuration.&lt;br /&gt;
* User Experience Enhancements&lt;br /&gt;
** '''Increase Bidding Transparency: '''Develop user interface elements that clearly display bidding status, deadlines, and participation metrics.&lt;br /&gt;
** '''Implement Dynamic Visual Feedback: '''Use real-time visual cues, such as color changes, to inform users about the bidding status instantly.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Develop Comprehensive Test Suites: '''Create extensive automated tests covering edge cases and critical functionalities to guarantee reliable system behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;v.-uml-diagram&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== UML Diagram ==&lt;br /&gt;
&lt;br /&gt;
Below is a visual representation of the models associated with the review bids functionality, depicting the data structures and their relationships.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml class diagram2.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi.-implementation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
'''ReviewBid: '''The &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; manages the bidding process, where participants indicate their preferences for reviewing different topics or assignments. This helps distribute review tasks fairly based on participants' interests.&lt;br /&gt;
&lt;br /&gt;
'''Assignment: '''The &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model represents team or individual assignments in Expertiza. It stores all the essential information required to keep the assignment process organized, like managing participants, setting due dates, forming teams, and overseeing the review process.&lt;br /&gt;
&lt;br /&gt;
'''SignUpTopic: '''The &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt; model identifies specific topics or areas within an assignment for which participants can sign up. It manages the availability of these topics, tracks bids, coordinates team sign-ups, and assigns topics based on both bids and participant availability. &lt;br /&gt;
&lt;br /&gt;
'''Participant: '''The &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model is about the individual users who participate in an assignment. This includes students, instructors, teaching assistants, or other relevant roles. This model is responsible for managing key user information, monitoring interactions with the assignment, keeping track of team memberships, and recording things like badges and review grades.&lt;br /&gt;
&lt;br /&gt;
== Implementation ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1.-decoupling-authorization-logic-from-reviewbidscontroller&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Decoupling Authorization Logic from &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| '''&amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; needs to use the authentication utilities'''&lt;br /&gt;
&lt;br /&gt;
The function &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; should use the available authentication utilities to support the Single Responsibility Principle and Separation of Concerns and to support code reusability.&lt;br /&gt;
&lt;br /&gt;
| '''Authentication Consistency:''' Remove authentication logic in the controller and call logic in the authentication utility class instead to ensure consistent use of the same logic across the application, foster reusability, and eliminate DRY.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
==== Authorization Flow Before Refactoring ====&lt;br /&gt;
The sequence diagram depicts the authorization process within the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;. When a user invokes an action, the controller checks if the action is allowed by verifying the user's role through the &amp;lt;code&amp;gt;current_role_name()&amp;lt;/code&amp;gt; method in the &amp;lt;code&amp;gt;ApplicationController&amp;lt;/code&amp;gt;. Depending on the action and the user's role, the controller either authorizes the user to proceed or denies access, ultimately executing the action logic if authorized and responding to the user.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 1.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We simplified the &amp;lt;code&amp;gt;action_allowed?&amp;lt;/code&amp;gt; method by decoupling authorization logic from the controller to use helper methods in the &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt; module. The &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; should not handle authentication, which is a violation of SRP. &lt;br /&gt;
&lt;br /&gt;
Note: We also made similar updates to the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; methods for &amp;lt;code&amp;gt;SignUpController&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;vertical-align:top;&amp;quot; | Original Code !! style=&amp;quot;vertical-align:top;&amp;quot; | Refactored Code&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index'&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator',&lt;br /&gt;
     'Student'].include? current_role_name and&lt;br /&gt;
        ((%w[list].include? action_name) ? are_needed_authorizations_present?(params[:id], &amp;quot;participant&amp;quot;, &amp;quot;reader&amp;quot;, &amp;quot;submitter&amp;quot;, &amp;quot;reviewer&amp;quot;) : true)&lt;br /&gt;
  else&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator'].include? current_role_name&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index', 'list'&lt;br /&gt;
    current_user_has_student_privileges? &amp;amp;&amp;amp; list_authorization_check&lt;br /&gt;
  else&lt;br /&gt;
    current_user_has_ta_privileges?&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; action ===&lt;br /&gt;
Previously, the &amp;lt;code&amp;gt;ReviewBidsController's index&amp;lt;/code&amp;gt; action was overloaded with responsibilities—it handled everything from locating and validating assignments to fetching review mappings and counting completed reviews. This all-in-one approach made the controller action messy. &lt;br /&gt;
&lt;br /&gt;
We have moved the business logic from the controller action and into dedicated service objects. With this change, we introduced two key service objects: &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; focuses on retrieving the participant along with their assignments, while the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; takes on the task of managing review mappings and calculating review-related stats, like how many reviews have been completed. &lt;br /&gt;
&lt;br /&gt;
This separation of responsibilities aligns with the Single Responsibility Principle. The logic for fetching assignments, retrieving review mappings, and counting reviews is now more flexible and reusable throughout the application. For example, we used methods in the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; in both the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; and the &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;, helping to avoid code duplication.The article https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial served as a guide on when and how to use service objects in Rails.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
  return unless current_user_id?(@participant.user_id)&lt;br /&gt;
  @assignment = @participant.assignment&lt;br /&gt;
  @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
  # Finding how many reviews have been completed&lt;br /&gt;
  @num_reviews_completed = 0&lt;br /&gt;
  @review_mappings.each do |map|&lt;br /&gt;
    @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # render view for completing reviews after review bidding has been completed&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @assignment = participant_service.assignment&lt;br /&gt;
  unless @assignment.is_a?(Assignment)&lt;br /&gt;
    flash[:error] = 'Assignment not found.'&lt;br /&gt;
    redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  @review_mappings = review_service.review_mappings&lt;br /&gt;
  @num_reviews_completed = review_service.review_counts[:completed]&lt;br /&gt;
&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Optimize &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Action functionality ===&lt;br /&gt;
In the earlier version of the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, the server only needed a couple of key pieces of information: an array of topic IDs (params[:topic]) and the participant ID (params[:id}). From there, it would figure out the assignment_id by using the first topic as a reference. This is risky because the &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt; associated with the topic may not be a valid &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
With the new implementation, we structured the parameters to include the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt;, an array of topic IDs, and &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;. This change ensures that the server always receives complete and reliable information, preventing issues caused by missing or inconsistent data. It also removes the need for the server to perform unnecessary lookups, as in the case of &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;, allowing the controller to focus on receiving requests and rendering appropriate responses.&lt;br /&gt;
&lt;br /&gt;
While these updates have cleaned up the set_priority action, they still contain a lot of business logic, like validations and priority updates. To make the code even more maintainable, there’s room for further refactoring—perhaps moving some of this logic into a service class or model method to streamline the controller even more.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | Old Code&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    if params[:topic].nil?&lt;br /&gt;
      ReviewBid.where(participant_id: params[:id]).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      participant = AssignmentParticipant.find_by(id: params[:id])&lt;br /&gt;
      assignment_id = SignUpTopic.find(params[:topic].first).assignment.id&lt;br /&gt;
      @bids = ReviewBid.where(participant_id: params[:id])&lt;br /&gt;
      signed_up_topics = ReviewBid.where(participant_id: params[:id]).map(&amp;amp;:signuptopic_id)&lt;br /&gt;
      signed_up_topics -= params[:topic].map(&amp;amp;:to_i)&lt;br /&gt;
      signed_up_topics.each do |topic|&lt;br /&gt;
        ReviewBid.where(signuptopic_id: topic, participant_id: params[:id]).destroy_all&lt;br /&gt;
      end&lt;br /&gt;
      params[:topic].each_with_index do |topic_id, index|&lt;br /&gt;
        bid_existence = ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id])&lt;br /&gt;
        if bid_existence.empty?&lt;br /&gt;
          ReviewBid.create(priority: index + 1, signuptopic_id: topic_id, participant_id: params[:id], assignment_id: assignment_id)&lt;br /&gt;
        else&lt;br /&gt;
          ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id]).update_all(priority: index + 1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to action: 'show', assignment_id: params[:assignment_id], id: params[:id]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    participant_id = params[:participant_id].to_i&lt;br /&gt;
    selected_topic_ids = params[:topic]&amp;amp;.map(&amp;amp;:to_i) || []&lt;br /&gt;
    assignment_id = params[:assignment_id].to_i&lt;br /&gt;
&lt;br /&gt;
    unless current_user_can_modify_participant?(participant_id)&lt;br /&gt;
      flash[:error] = 'You are not authorized to modify these bids.'&lt;br /&gt;
      respond_to do |format|&lt;br /&gt;
        format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
        format.json { render json: { status: 'unauthorized' }, status: :unauthorized }&lt;br /&gt;
      end&lt;br /&gt;
      return&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    if selected_topic_ids.empty?&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      assignment = Assignment.find_by(id: assignment_id)&lt;br /&gt;
      if assignment.nil?&lt;br /&gt;
        flash[:error] = &amp;quot;Invalid assignment.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_assignment' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      unless SignUpTopic.where(id: selected_topic_ids, assignment_id: assignment_id).count == selected_topic_ids.size&lt;br /&gt;
        flash[:error] = &amp;quot;One or more selected topics are invalid.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_topics' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).where.not(signuptopic_id: selected_topic_ids).destroy_all&lt;br /&gt;
&lt;br /&gt;
      selected_topic_ids.each_with_index do |topic_id, index|&lt;br /&gt;
        bid = ReviewBid.find_or_initialize_by(signuptopic_id: topic_id, participant_id: participant_id)&lt;br /&gt;
        bid.priority = index + 1&lt;br /&gt;
        bid.assignment_id = assignment_id&lt;br /&gt;
        bid.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_to action: 'show', assignment_id: assignment_id, id: participant_id, notice: 'Review bids updated successfully.' }&lt;br /&gt;
      format.json { render json: { status: 'success' }, status: :ok }&lt;br /&gt;
    end&lt;br /&gt;
  rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
    flash[:error] = &amp;quot;Failed to update priorities: #{e.message}&amp;quot;&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
      format.json { render json: { status: 'error', message: e.message }, status: :unprocessable_entity }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Client Side Changes to Support &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Changes ====&lt;br /&gt;
We refactored the client-side code to ensure it passed the correct information from the view to the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action. On the client side, we now grab the topicIDs and send them as &amp;lt;code&amp;gt;selected_topic_ids&amp;lt;/code&amp;gt;, allowing the controller to figure out which topics were selected and in what order.  &lt;br /&gt;
&lt;br /&gt;
When working with jQuery, AJAX is awesome because it lets us update the server without reloading the page, making the user experience smooth and responsive. When we pull the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, we can grab it along with other data, like the list of topics, and include it as a single request. This way, we don’t have to rely on the URL to know which participant we’re working with, which makes the code cleaner.&lt;br /&gt;
&lt;br /&gt;
We also added an update handler so that any time the order of the selected topic changes in the view, information is sent to the appropriate controller action. This ensures that the controller and the view remain in sync, with real-time updates, and includes a mechanism for providing feedback if something goes wrong.&lt;br /&gt;
&lt;br /&gt;
Note: One interesting aspect of the application was the mix of JavaScript and CoffeeScript code. It wasn't clear why both types of code were used.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery -&amp;gt;&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: -&amp;gt;&lt;br /&gt;
      $.post($(this).data('update-url'), $(this).sortable('serialize'))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery(document).ready ($) -&amp;gt;&lt;br /&gt;
  sendUpdatedOrder = -&amp;gt;&lt;br /&gt;
    sortedIDs = $(this).sortable('toArray')&lt;br /&gt;
&lt;br /&gt;
    topicIDs = sortedIDs.map (id) -&amp;gt;&lt;br /&gt;
      id.replace 'topic_', ''&lt;br /&gt;
&lt;br /&gt;
    participant_id = $(this).data('participant-id')&lt;br /&gt;
&lt;br /&gt;
    assignment_id = new URL($(this).data('update-url'), window.location.origin).searchParams.get('assignment_id')&lt;br /&gt;
&lt;br /&gt;
    $.ajax&lt;br /&gt;
      type: 'POST'&lt;br /&gt;
      url: $(this).data('update-url')&lt;br /&gt;
      data:&lt;br /&gt;
        topic: topicIDs&lt;br /&gt;
        participant_id: participant_id&lt;br /&gt;
        assignment_id: assignment_id&lt;br /&gt;
      success: (response) -&amp;gt;&lt;br /&gt;
        console.log 'Selections updated successfully.'&lt;br /&gt;
      error: (xhr, status, error) -&amp;gt;&lt;br /&gt;
        alert 'An error occurred while saving your selections. Please try again.'&lt;br /&gt;
        console.error error&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: sendUpdatedOrder&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-1.-signupsheetcontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; Updates ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; method renders the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; action However, the view is associated with the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # provides variables for reviewing page located at views/review_bids/others_work.html.erb&lt;br /&gt;
  def index&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
    return unless current_user_id?(@participant.user_id)&lt;br /&gt;
&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
    # Finding how many reviews have been completed&lt;br /&gt;
    @num_reviews_completed = 0&lt;br /&gt;
    @review_mappings.each do |map|&lt;br /&gt;
      @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # render view for completing reviews after review bidding has been completed&lt;br /&gt;
    render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
  end  &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-2.-reviewbidsotherswork-view-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;review_bids_others_work.html.erb&amp;lt;/code&amp;gt; View Updates ====&lt;br /&gt;
The view is technically part of the SignUpSheetController, but it's being rendered by the ReviewBidsController, which initially threw us off. After chatting with the professor, we decided not to change this view. He indicated that the plan is to reuse these views for both assignment and review bidding.&lt;br /&gt;
&lt;br /&gt;
We suggest building the assignment bidding and review bidding features separately first to ensure each one works independently. Once that’s done, we can clean things up by combining any shared functionality. This could include creating shared views or even introducing an abstract model to handle both types of bidding more efficiently. It could also mean replacing the ReviewBidsController with something more generic, like a BidsController, to keep things reusable and easier to maintain down the line.&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Rename &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; method ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; should be &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt;&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:''' Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The primary activity here is to refactor the method name &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; and fix the hardcoded URL. However, the match topic bidding algorithm no longer works since Heroku is no longer free, per the professor. The reference link below has detailed information about the web service that we can leverage to gain that understanding.&lt;br /&gt;
&lt;br /&gt;
Reference Link for Webservice Details: [https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Updated Implementation ====&lt;br /&gt;
The original requirement was to rename the run_bidding_algorithm method to assign_reviewers, but after digging into the code, we realized that renaming it didn’t make sense. The method runs the bidding algorithm to match topics, and there’s already a separate method to handle assigning review topics.&lt;br /&gt;
&lt;br /&gt;
However, we improved the logic in the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; method (which calls the private &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt;) by introducing a separate &amp;lt;code&amp;gt;BiddingAlgorithmService&amp;lt;/code&amp;gt; object. This keeps the business logic out of the controller action, which makes things cleaner and more maintainable.&lt;br /&gt;
&lt;br /&gt;
==== review_bids_controller ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def run_bidding_algorithm&lt;br /&gt;
    review_bid = ReviewBid.new&lt;br /&gt;
    bidding_data = review_bid.bidding_data&lt;br /&gt;
    matched_topics = BiddingAlgorithmService.new(bidding_data).run&lt;br /&gt;
    raise ArgumentError, 'Failed to assign reviewers. Please try again later.' unless matched_topics&lt;br /&gt;
&lt;br /&gt;
    matched_topics&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== bidding_algorithm_service ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# frozen_string_literal: true&lt;br /&gt;
&lt;br /&gt;
# The `BiddingAlgorithmService` run the bid assignment algorithm&lt;br /&gt;
# Sends student IDs, topic IDs, student preferences, and timestamps to the web service&lt;br /&gt;
# The web service returns the matched assignments in the JSON response body&lt;br /&gt;
class BiddingAlgorithmService&lt;br /&gt;
  SERVICE_URL = 'http://app-csc517.herokuapp.com/match_topics'.freeze&lt;br /&gt;
&lt;br /&gt;
  # MOCK_DATA is based on the documentation detailing how the webservice behaves.&lt;br /&gt;
  # Reference: https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review#Webservice&lt;br /&gt;
  MOCK_DATA = {&lt;br /&gt;
    36239 =&amp;gt; [3970, 3972, 3975],&lt;br /&gt;
    36240 =&amp;gt; [3973, 3974, 3972],&lt;br /&gt;
    36241 =&amp;gt; [3969, 3971, 3972],&lt;br /&gt;
    36242 =&amp;gt; [3969, 3971, 3973],&lt;br /&gt;
    36243 =&amp;gt; [3969, 3970, 3971]&lt;br /&gt;
  }.freeze&lt;br /&gt;
&lt;br /&gt;
  def initialize(bidding_data)&lt;br /&gt;
    @bidding_data = bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def run&lt;br /&gt;
    return MOCK_DATA if Rails.application.config.use_mock_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
    perform_request&lt;br /&gt;
  rescue RestClient::ExceptionWithResponse, JSON::ParserError&lt;br /&gt;
    false&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def perform_request&lt;br /&gt;
    response = RestClient.post(&lt;br /&gt;
      SERVICE_URL,&lt;br /&gt;
      @bidding_data.to_json,&lt;br /&gt;
      content_type: :json,&lt;br /&gt;
      accept: :json&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    validate_response(response)&lt;br /&gt;
    JSON.parse(response.body)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def validate_response(response)&lt;br /&gt;
    raise 'Invalid response format' unless response.headers[:content_type] &amp;amp;&amp;amp; response.headers[:content_type].include?('application/json')&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Resolve Functionality Issues for Non Bidders ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Currently it doesn't work if some student does not bid. In this case, algorithm needs to be fixed to ignore anyone who didn’t bid, and let them choose from what’s left over.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to do several things within this controller. We will set a default state for non-bidders that we can point to to give them options to select topics to review from the leftovers. We will consolidate &amp;lt;code&amp;gt;@sign_up_topics&amp;lt;/code&amp;gt; to resolve the DRY violation. Lastly, we will remove business logic from the show method and push it to a new helper to conform to the SRP.&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # Additional method&lt;br /&gt;
&lt;br /&gt;
  # Provides variables for review bidding page&lt;br /&gt;
  def show&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id].to_i)&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @sign_up_topics = SignUpTopic.where(assignment_id: @assignment.id, private_to: nil)&lt;br /&gt;
    my_topic = SignedUpTeam.topic_id(@participant.parent_id, @participant.user_id)&lt;br /&gt;
    @sign_up_topics -= SignUpTopic.where(assignment_id: @assignment.id, id: my_topic)&lt;br /&gt;
    @num_participants = AssignmentParticipant.where(parent_id: @assignment.id).count&lt;br /&gt;
    @selected_topics = nil # This is used to list the topics assigned to review (i.e., select == assigned)&lt;br /&gt;
    @bids = ReviewBid.where(participant_id: @participant, assignment_id: @assignment.id)&lt;br /&gt;
    signed_up_topics = []&lt;br /&gt;
    &lt;br /&gt;
    @bids.each do |bid|&lt;br /&gt;
      sign_up_topic = SignUpTopic.find_by(id: bid.signuptopic_id)&lt;br /&gt;
      signed_up_topics &amp;lt;&amp;lt; sign_up_topic if sign_up_topic&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    signed_up_topics &amp;amp;= @sign_up_topics&lt;br /&gt;
    @sign_up_topics -= signed_up_topics&lt;br /&gt;
    @bids = signed_up_topics&lt;br /&gt;
    @num_of_topics = @sign_up_topics.size&lt;br /&gt;
    @assigned_review_maps = []&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: @assignment.id, reviewer_id: @participant.id).each do |review_map|&lt;br /&gt;
      @assigned_review_maps &amp;lt;&amp;lt; review_map&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Explicitly render view since it's in the sign up sheet views&lt;br /&gt;
    render 'sign_up_sheet/review_bids_show'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Additional methods&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-2.-reviewbidshelper-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
 # Assigns bidding topics to reviewers&lt;br /&gt;
  def assign_bidding&lt;br /&gt;
      assignment = validate_assignment(params[:assignment_id])&lt;br /&gt;
&lt;br /&gt;
      reviewers = validate_reviewers(assignment.id)&lt;br /&gt;
      reviewer_ids = reviewers.map(&amp;amp;:id)&lt;br /&gt;
&lt;br /&gt;
      matched_topics = run_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
      if matched_topics.blank?&lt;br /&gt;
        flash[:alert] = 'Topic or assignment is missing'&lt;br /&gt;
        redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      leftover_topics = find_leftover_topics(assignment.id, matched_topics)&lt;br /&gt;
      assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.new.assign_review_topics(matched_topics)&lt;br /&gt;
      assignment.update!(can_choose_topic_to_review: false)&lt;br /&gt;
&lt;br /&gt;
      flash[:notice] = 'Reviewers were successfully assigned to topics.'&lt;br /&gt;
      redirect_back fallback_location: root_path&lt;br /&gt;
    rescue ArgumentError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ArgumentError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: e.message&lt;br /&gt;
    rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::RecordInvalid: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue ActiveRecord::ActiveRecordError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::ActiveRecordError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue StandardError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;StandardError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers. Please try again later.'&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  #service methods here ...&lt;br /&gt;
&lt;br /&gt;
  def find_leftover_topics(assignment_id, matched_topics)&lt;br /&gt;
    all_topic_ids = SignUpTopic.where(assignment_id: assignment_id).pluck(:id)&lt;br /&gt;
    assigned_topic_ids = matched_topics.map { |match| match[:topic_id] }&lt;br /&gt;
&lt;br /&gt;
    # Calculate leftover topics&lt;br /&gt;
    all_topic_ids - assigned_topic_ids&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
    return if leftover_topics.blank?&lt;br /&gt;
&lt;br /&gt;
    # Find non-bidders by excluding already matched reviewers&lt;br /&gt;
    non_bidders = reviewer_ids - matched_topics.keys&lt;br /&gt;
&lt;br /&gt;
    # Assign leftover topics to non-bidders in a round-robin fashion&lt;br /&gt;
    non_bidders.each_with_index do |reviewer_id, index|&lt;br /&gt;
      topic_id = leftover_topics[index % leftover_topics.length]&lt;br /&gt;
      ReviewBid.create(priority: 1, signuptopic_id: topic_id, participant_id: reviewer_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirm Functionality for Single-person Teams ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Make sure your code works on individual assignments, i.e., assignments where participants sign up for topics instead of teams. In this case, a team is created for each participant when they sign up. So the code should work for assignments to which either individuals or teams submit.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to make an update to focus on topic rather than team or have it where teams are updated based on topic id.We will probably utilize sign up topic or a similar object to accomplish this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-5-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bid Status Messaging ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| To make the functionality more intuitive, include a message to say how many students are eligible to submit bids, how many have submitted their bids, when the deadline for submitting bids is.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
The logic for these messages will go into the helper file. It is effectively the same reasoning as our migration of other business/functionality code from the controller to the helper to conform to the SRP. It will go hand in hand with the SignUpTopic portion of the code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-6-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Why are the methods in review_bid.rb class methods? Can we change them to instance methods or move it to helpers?&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-1.-reviewbid-model-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model Updates ====&lt;br /&gt;
Since the current class methods are tied to &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model instances, we will change them to instance methods. There is no need to move them to helpers.&lt;br /&gt;
&lt;br /&gt;
===== Review Bid Model Flow Before Refactoring =====&lt;br /&gt;
This sequence diagram describes how the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; interacts with the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model in assigning review topics to reviewers according to their bids. Once the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; action is initiated by the user, the controller gathers the IDs of reviewers and calls &amp;lt;code&amp;gt;ReviewBid.bidding_data&amp;lt;/code&amp;gt; to gather bidding information. That is sent out to an external bidding algorithm, and the matched topics that come back are used by &amp;lt;code&amp;gt;ReviewBid.assign_review_topics&amp;lt;/code&amp;gt; to create &amp;lt;code&amp;gt;ReviewResponseMap&amp;lt;/code&amp;gt; entries, assigning topics to reviewers for the assignment.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
  belongs_to :assignment, class_name: 'Assignment'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  def self.bidding_data(assignment_id, reviewer_ids)&lt;br /&gt;
    # create basic hash and set basic hash data&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'users' =&amp;gt; {}, 'max_accepted_proposals' =&amp;gt; [] }&lt;br /&gt;
    bidding_data['tid'] = SignUpTopic.where(assignment_id: assignment_id).ids&lt;br /&gt;
    bidding_data['max_accepted_proposals'] = Assignment.where(id: assignment_id).pluck(:num_reviews_allowed).first&lt;br /&gt;
&lt;br /&gt;
    # loop through reviewer_ids to get reviewer specific bidding data&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      bidding_data['users'][reviewer_id] = reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # assigns topics to reviews as matched by the webservice algorithm&lt;br /&gt;
  def self.assign_review_topics(assignment_id, reviewer_ids, matched_topics, _min_num_reviews = 2)&lt;br /&gt;
    # if review response map already created, delete it&lt;br /&gt;
    if ReviewResponseMap.where(reviewed_object_id: assignment_id)&lt;br /&gt;
      ReviewResponseMap.where(reviewed_object_id: assignment_id).destroy_all&lt;br /&gt;
    end&lt;br /&gt;
    # loop through reviewer_ids to assign reviews to each reviewer&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      topics_to_assign = matched_topics[reviewer_id.to_s]&lt;br /&gt;
      topics_to_assign.each do |topic|&lt;br /&gt;
        assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method to assign a single topic to a reviewer&lt;br /&gt;
  def self.assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic).pluck(:team_id).first&lt;br /&gt;
    team_to_review.nil? ? [] : ReviewResponseMap.create(reviewed_object_id: assignment_id, reviewer_id: reviewer_id, reviewee_id: team_to_review, type: 'ReviewResponseMap')&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method for getting individual reviewer_ids bidding data&lt;br /&gt;
  # returns user's bidding data hash&lt;br /&gt;
  def self.reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    reviewer_user_id = AssignmentParticipant.find(reviewer_id).user_id&lt;br /&gt;
    self_topic = SignedUpTeam.topic_id(assignment_id, reviewer_user_id)&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'otid' =&amp;gt; self_topic, 'priority' =&amp;gt; [], 'time' =&amp;gt; [] }&lt;br /&gt;
    bids = ReviewBid.where(participant_id: reviewer_id)&lt;br /&gt;
&lt;br /&gt;
    # loop through each bid for a topic to get specific data&lt;br /&gt;
    bids.each do |bid|&lt;br /&gt;
      bidding_data['tid'] &amp;lt;&amp;lt; bid.signuptopic_id&lt;br /&gt;
      bidding_data['priority'] &amp;lt;&amp;lt; bid.priority&lt;br /&gt;
      bidding_data['time'] &amp;lt;&amp;lt; bid.updated_at&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  # Instance method to get bidding data for this review bid&lt;br /&gt;
  def bidding_data&lt;br /&gt;
    {&lt;br /&gt;
      'tid' =&amp;gt; topic_ids,&lt;br /&gt;
      'otid' =&amp;gt; self_topic,&lt;br /&gt;
      'priority' =&amp;gt; priorities,&lt;br /&gt;
      'time' =&amp;gt; updated_times&lt;br /&gt;
    }&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # bid object to limit db touches for similar data acquisitions&lt;br /&gt;
  def bids&lt;br /&gt;
    @bids ||= ReviewBid.where(participant_id: participant_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def topic_ids&lt;br /&gt;
    bids.pluck(:signuptopic_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def priorities&lt;br /&gt;
    bids.pluck(:priority)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def updated_times&lt;br /&gt;
    bids.pluck(:updated_at)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self_topic&lt;br /&gt;
    return nil unless topic &amp;amp;&amp;amp; topic.assignment_id&lt;br /&gt;
&lt;br /&gt;
    SignedUpTeam.topic_id(topic.assignment_id, participant.user_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign topics to reviews&lt;br /&gt;
  def assign_review_topics(matched_topics)&lt;br /&gt;
    validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: topic.assignment_id).destroy_all&lt;br /&gt;
&lt;br /&gt;
    matched_topics.each do |reviewer_id, topics|&lt;br /&gt;
      Array(topics).each { |topic_id| assign_topic_to_reviewer(reviewer_id, topic_id) }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign a single topic to a reviewer&lt;br /&gt;
  def assign_topic_to_reviewer(reviewer_id, topic_id)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic_id).pluck(:team_id).first&lt;br /&gt;
    return if team_to_review.nil?&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.create(&lt;br /&gt;
      reviewed_object_id: topic.assignment_id,&lt;br /&gt;
      reviewer_id: reviewer_id,&lt;br /&gt;
      reviewee_id: team_to_review,&lt;br /&gt;
      type: 'ReviewResponseMap'&lt;br /&gt;
    )&lt;br /&gt;
    # error handling&lt;br /&gt;
  rescue StandardError =&amp;gt; e&lt;br /&gt;
    Rails.logger.error(&amp;quot;Failed to assign topic #{topic_id} to reviewer #{reviewer_id}: #{e.message}&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
    raise ArgumentError, 'Topic or assignment is missing' if topic.nil? || topic.assignment_id.nil?&lt;br /&gt;
    raise ArgumentError, 'Matched topics must be a Hash' unless matched_topics.is_a?(Hash)&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
Once we changed them to instance methods, we modified their use in the controller code to instantiate the object first.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-3.-schema.db-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;Schema.db&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We also need to remove the foreign key reference to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model since the &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model already has access to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;, and that is the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; we should be referencing. This will also involve modifying the database to remove the foreign key reference to the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Steps required:&lt;br /&gt;
&lt;br /&gt;
# Review the records in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table in the database to see if there are any records and if those records have values for &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;&lt;br /&gt;
# Create a migration file using the command: &amp;lt;code&amp;gt;rails generate migration RemoveAssignmentFromReviewBids&amp;lt;/code&amp;gt;&lt;br /&gt;
# In the generated file, indicate the change that needs to be made using the change method and migration columns &amp;lt;code&amp;gt;remove_foreign_key&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;remove_column&amp;lt;/code&amp;gt;. Migration should be straightforward since there is no cascading behavior (i.e., &amp;lt;code&amp;gt;dependent:destroy&amp;lt;/code&amp;gt;) to worry about.&lt;br /&gt;
# Once comfortable with the updates to the generated file, run the migration using the &amp;lt;code&amp;gt;rails db:migrate&amp;lt;/code&amp;gt; command.&lt;br /&gt;
# Run a query in the DB to ensure no orphaned records are in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table after the updates.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-4.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implement Edge Case Testing ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| In the previous implementation wiki, there are edge cases which are not exhaustively tested. Should test those edge cases thoroughly and add more edge case testing - link&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span class=&amp;quot;mark&amp;quot;&amp;gt;TODO&amp;lt;/span&amp;gt;:We just got initial test results back. We still need to figure out why tests are failing and what may be slipping through the cracks.[[File:Test expertiza.png|411x459px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-8-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design Patterns and Principles ===&lt;br /&gt;
Below are the design patterns and principles we plan to use to address the problem statements outlined in the project requirements:&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
** Controller Responsibility&lt;br /&gt;
*** To address the issue of authentication logic residing in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, we will refactor the code to ensure the controller solely handles request and response flows.&lt;br /&gt;
*** Authentication logic will be moved to dedicated methods in &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt;, making the codebase easier to maintain and test.&lt;br /&gt;
** View Simplification&lt;br /&gt;
*** Currently, the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; view contains excessive business logic, violating SRP.&lt;br /&gt;
*** We will move this logic into helper methods or models, ensuring each component has a single responsibility and improving code readability.&lt;br /&gt;
&lt;br /&gt;
* Separation of Concerns (SoC)&lt;br /&gt;
** By relocating authorization logic out of the controller, we separate authentication from request handling.&lt;br /&gt;
** Adhering to SoC enhances readability and maintainability by allowing each part of the application to focus on its specific role.&lt;br /&gt;
* Don't Repeat Yourself (DRY)&lt;br /&gt;
** We will inspect the existing implementation for code duplication, particularly in the controller actions.&lt;br /&gt;
** Any repetitive code will be refactored or removed to reduce redundancy and improve clarity.&lt;br /&gt;
&lt;br /&gt;
* Model-View-Controller (MVC) Adherence&lt;br /&gt;
** Moving business logic from the view aligns with MVC best practices.&lt;br /&gt;
** By ensuring the view only presents data without processing it, we maintain clear boundaries between the model, view, and controller layers.&lt;br /&gt;
&lt;br /&gt;
* Dependency Inversion Principle (DIP)&lt;br /&gt;
** We plan to update class methods to instance methods, allowing the controller to depend on abstractions rather than concrete implementations.&lt;br /&gt;
** This change leverages Ruby's duck typing, providing flexibility and making the code easier to maintain and test.&lt;br /&gt;
&lt;br /&gt;
** Clear and intentional naming during refactoring will enhance code clarity and maintainability.&lt;br /&gt;
&lt;br /&gt;
== Files Modified/Added ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-1.-controllers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
review_bids_controller.rb&lt;br /&gt;
&lt;br /&gt;
signup_sheet_controller.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-2.-models&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
review_bid.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-3.-helpers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
&lt;br /&gt;
authorization_helper.rb&lt;br /&gt;
&lt;br /&gt;
Review_bids_helper.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;viii.-test-plan&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
The Excel spreadsheet below details our test plan for testing the code we changed or created to ensure code coverage. The process involved reviewing the code and determining all the possible scenarios that needed to be covered based on what the method was doing, then creating test cases accordingly.&lt;br /&gt;
&lt;br /&gt;
* [https://docs.google.com/spreadsheets/d/1iHzOdSwlTtxOtf5VsNdwOaCP8NzYMmiJ/edit?usp=drive_link Open Test Matrix Spreadsheet]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/review_bids_controller_spec.rb ReviewBidsController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/sign_up_sheet_controller_spec.rb SignUpSheetController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/models/review_bid_spec.rb ReviewBid model updated test cases]&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-1.-mentor&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Ed Gehringer&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-2.-members&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Members ===&lt;br /&gt;
&lt;br /&gt;
* Gavin Teague&lt;br /&gt;
* Janice Uwujaren&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xi.-links&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
Previous Wiki: [https://wiki.expertiza.ncsu.edu/index.php/CSC/ECE_517_Fall_2021_-_E2151._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;Link&amp;lt;/u&amp;gt;]&lt;br /&gt;
Reference Link for Webservice Details: [https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xii.-references&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/document/d/1LHVacylQmB15I6VcYUA5nuC7GRRU9szHzZhjlPcE-PA/edit?tab=t.0#heading=h.u0oycoj6e0ev &amp;lt;u&amp;gt;Project Instructions&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;appendix-a-uml-class-diagram-plantuml-code-for-review-bids&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Juwujar</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=161047</id>
		<title>CSC/ECE 517 Fall 2024 - E2485. Allow reviewers to bid on what to review</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=161047"/>
		<updated>2024-12-12T05:55:27Z</updated>

		<summary type="html">&lt;p&gt;Juwujar: /* SignUpSheetController Updates */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
This project will fix the defects and improve the functionality in Expertiza of the Reviewer Bidding feature. It will make the functionality smoother, more reliable, and align with Expertiza's collaborative learning objectives. &lt;br /&gt;
&lt;br /&gt;
The Review Bidding feature lets reviewers focus on topics they know best or care about most. If someone feels exceptionally qualified or interested in a topic, they can bid to review submissions in that area. This helps ensure reviews are insightful and enjoyable for the reviewer while benefiting the other participants and the overall process.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iii.-problem-statements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Problem Statements ==&lt;br /&gt;
&lt;br /&gt;
Assigning reviews to users on Expertiza involves a complex bidding algorithm that requires optimization before production deployment. Below are key areas we plan to address as per the project requirements:&lt;br /&gt;
* Code Refactoring and Best Practices&lt;br /&gt;
** '''Utilize Authentication Utilities''': Modify the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; function in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to use existing authentication utilities, enhancing code reusability and adhering to the Single Responsibility Principle (SRP) and Separation of Concerns (SoC).&lt;br /&gt;
** '''Eliminate Code Duplication''': Refactor &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; to comply with the Don't Repeat Yourself (DRY) principle.&lt;br /&gt;
** '''Rename Functions for Clarity''': Change &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; to better reflect its purpose.&lt;br /&gt;
** '''Refactor Methods in '''&amp;lt;code&amp;gt;review_bid.rb&amp;lt;/code&amp;gt;''':''': Convert class methods to instance methods or relocate them to helper classes for improved code organization.&lt;br /&gt;
* Functionality Enhancements&lt;br /&gt;
** '''Handle Missing Bids Gracefully''': Adjust the bidding algorithm to accommodate students who do not bid, allowing them to select from the remaining options.&lt;br /&gt;
** '''Support Both Individual and Team Assignments''': Ensure the code functions correctly for participant and team-based assignments when signing up for topics.&lt;br /&gt;
* User Interface Improvements&lt;br /&gt;
** '''Increase Transparency in the Bidding Process''': Display messages indicating how many students are eligible to bid, how many have submitted bids, and the deadline.&lt;br /&gt;
** '''Accurate Bid Status Indicators''': Ensure topic colors change correctly based on the number of outstanding bids, providing clear visual feedback to users.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Exhaustive Edge Case Testing''': Conduct thorough testing for edge cases not previously covered, adding additional test cases as needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iv.-design-goals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Design Goals ==&lt;br /&gt;
&lt;br /&gt;
While addressing the existing defects and enhancing the Reviewer Bidding feature, we plan to adhere to the following design guidelines:&lt;br /&gt;
&lt;br /&gt;
* Code Quality and Maintainability&lt;br /&gt;
** '''Improve Code Structure: '''Implement refactoring strategies to enhance code readability and maintainability, ensuring adherence to best practices like the DRY principle.&lt;br /&gt;
** '''Standardize Authentication Logic: '''Utilize a centralized authentication utility to promote consistency and reusability across the application.&lt;br /&gt;
* System Stability and Flexibility&lt;br /&gt;
** '''Enhance Algorithm Robustness: '''Modify the bidding algorithm to handle scenarios where students do not bid, ensuring the system remains stable under all conditions.&lt;br /&gt;
** '''Ensure Assignment Versatility: '''Design the system to seamlessly support both individual and team assignments without additional configuration.&lt;br /&gt;
* User Experience Enhancements&lt;br /&gt;
** '''Increase Bidding Transparency: '''Develop user interface elements that clearly display bidding status, deadlines, and participation metrics.&lt;br /&gt;
** '''Implement Dynamic Visual Feedback: '''Use real-time visual cues, such as color changes, to inform users about the bidding status instantly.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Develop Comprehensive Test Suites: '''Create extensive automated tests covering edge cases and critical functionalities to guarantee reliable system behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;v.-uml-diagram&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== UML Diagram ==&lt;br /&gt;
&lt;br /&gt;
Below is a visual representation of the models associated with the review bids functionality, depicting the data structures and their relationships.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml class diagram2.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi.-implementation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
'''ReviewBid: '''The &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; manages the bidding process, where participants indicate their preferences for reviewing different topics or assignments. This helps distribute review tasks fairly based on participants' interests.&lt;br /&gt;
&lt;br /&gt;
'''Assignment: '''The &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model represents team or individual assignments in Expertiza. It stores all the essential information required to keep the assignment process organized, like managing participants, setting due dates, forming teams, and overseeing the review process.&lt;br /&gt;
&lt;br /&gt;
'''SignUpTopic: '''The &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt; model identifies specific topics or areas within an assignment for which participants can sign up. It manages the availability of these topics, tracks bids, coordinates team sign-ups, and assigns topics based on both bids and participant availability. &lt;br /&gt;
&lt;br /&gt;
'''Participant: '''The &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model is about the individual users who participate in an assignment. This includes students, instructors, teaching assistants, or other relevant roles. This model is responsible for managing key user information, monitoring interactions with the assignment, keeping track of team memberships, and recording things like badges and review grades.&lt;br /&gt;
&lt;br /&gt;
== Implementation ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1.-decoupling-authorization-logic-from-reviewbidscontroller&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Decoupling Authorization Logic from &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| '''&amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; needs to use the authentication utilities'''&lt;br /&gt;
&lt;br /&gt;
The function &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; should use the available authentication utilities to support the Single Responsibility Principle and Separation of Concerns and to support code reusability.&lt;br /&gt;
&lt;br /&gt;
| '''Authentication Consistency:''' Remove authentication logic in the controller and call logic in the authentication utility class instead to ensure consistent use of the same logic across the application, foster reusability, and eliminate DRY.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
==== Authorization Flow Before Refactoring ====&lt;br /&gt;
The sequence diagram depicts the authorization process within the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;. When a user invokes an action, the controller checks if the action is allowed by verifying the user's role through the &amp;lt;code&amp;gt;current_role_name()&amp;lt;/code&amp;gt; method in the &amp;lt;code&amp;gt;ApplicationController&amp;lt;/code&amp;gt;. Depending on the action and the user's role, the controller either authorizes the user to proceed or denies access, ultimately executing the action logic if authorized and responding to the user.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 1.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We simplified the &amp;lt;code&amp;gt;action_allowed?&amp;lt;/code&amp;gt; method by decoupling authorization logic from the controller to use helper methods in the &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt; module. The &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; should not handle authentication, which is a violation of SRP. &lt;br /&gt;
&lt;br /&gt;
Note: We also made similar updates to the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; methods for &amp;lt;code&amp;gt;SignUpController&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;vertical-align:top;&amp;quot; | Original Code !! style=&amp;quot;vertical-align:top;&amp;quot; | Refactored Code&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index'&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator',&lt;br /&gt;
     'Student'].include? current_role_name and&lt;br /&gt;
        ((%w[list].include? action_name) ? are_needed_authorizations_present?(params[:id], &amp;quot;participant&amp;quot;, &amp;quot;reader&amp;quot;, &amp;quot;submitter&amp;quot;, &amp;quot;reviewer&amp;quot;) : true)&lt;br /&gt;
  else&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator'].include? current_role_name&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index', 'list'&lt;br /&gt;
    current_user_has_student_privileges? &amp;amp;&amp;amp; list_authorization_check&lt;br /&gt;
  else&lt;br /&gt;
    current_user_has_ta_privileges?&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; action ===&lt;br /&gt;
Previously, the &amp;lt;code&amp;gt;ReviewBidsController's index&amp;lt;/code&amp;gt; action was overloaded with responsibilities—it handled everything from locating and validating assignments to fetching review mappings and counting completed reviews. This all-in-one approach made the controller action messy. &lt;br /&gt;
&lt;br /&gt;
We have moved the business logic from the controller action and into dedicated service objects. With this change, we introduced two key service objects: &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; focuses on retrieving the participant along with their assignments, while the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; takes on the task of managing review mappings and calculating review-related stats, like how many reviews have been completed. &lt;br /&gt;
&lt;br /&gt;
This separation of responsibilities aligns with the Single Responsibility Principle. The logic for fetching assignments, retrieving review mappings, and counting reviews is now more flexible and reusable throughout the application. For example, we used methods in the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; in both the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; and the &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;, helping to avoid code duplication.The article https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial served as a guide on when and how to use service objects in Rails.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
  return unless current_user_id?(@participant.user_id)&lt;br /&gt;
  @assignment = @participant.assignment&lt;br /&gt;
  @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
  # Finding how many reviews have been completed&lt;br /&gt;
  @num_reviews_completed = 0&lt;br /&gt;
  @review_mappings.each do |map|&lt;br /&gt;
    @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # render view for completing reviews after review bidding has been completed&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @assignment = participant_service.assignment&lt;br /&gt;
  unless @assignment.is_a?(Assignment)&lt;br /&gt;
    flash[:error] = 'Assignment not found.'&lt;br /&gt;
    redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  @review_mappings = review_service.review_mappings&lt;br /&gt;
  @num_reviews_completed = review_service.review_counts[:completed]&lt;br /&gt;
&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Optimize &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Action functionality ===&lt;br /&gt;
In the earlier version of the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, the server only needed a couple of key pieces of information: an array of topic IDs (params[:topic]) and the participant ID (params[:id}). From there, it would figure out the assignment_id by using the first topic as a reference. This is risky because the &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt; associated with the topic may not be a valid &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
With the new implementation, we structured the parameters to include the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt;, an array of topic IDs, and &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;. This change ensures that the server always receives complete and reliable information, preventing issues caused by missing or inconsistent data. It also removes the need for the server to perform unnecessary lookups, as in the case of &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;, allowing the controller to focus on receiving requests and rendering appropriate responses.&lt;br /&gt;
&lt;br /&gt;
While these updates have cleaned up the set_priority action, they still contain a lot of business logic, like validations and priority updates. To make the code even more maintainable, there’s room for further refactoring—perhaps moving some of this logic into a service class or model method to streamline the controller even more.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | Old Code&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    if params[:topic].nil?&lt;br /&gt;
      ReviewBid.where(participant_id: params[:id]).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      participant = AssignmentParticipant.find_by(id: params[:id])&lt;br /&gt;
      assignment_id = SignUpTopic.find(params[:topic].first).assignment.id&lt;br /&gt;
      @bids = ReviewBid.where(participant_id: params[:id])&lt;br /&gt;
      signed_up_topics = ReviewBid.where(participant_id: params[:id]).map(&amp;amp;:signuptopic_id)&lt;br /&gt;
      signed_up_topics -= params[:topic].map(&amp;amp;:to_i)&lt;br /&gt;
      signed_up_topics.each do |topic|&lt;br /&gt;
        ReviewBid.where(signuptopic_id: topic, participant_id: params[:id]).destroy_all&lt;br /&gt;
      end&lt;br /&gt;
      params[:topic].each_with_index do |topic_id, index|&lt;br /&gt;
        bid_existence = ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id])&lt;br /&gt;
        if bid_existence.empty?&lt;br /&gt;
          ReviewBid.create(priority: index + 1, signuptopic_id: topic_id, participant_id: params[:id], assignment_id: assignment_id)&lt;br /&gt;
        else&lt;br /&gt;
          ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id]).update_all(priority: index + 1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to action: 'show', assignment_id: params[:assignment_id], id: params[:id]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    participant_id = params[:participant_id].to_i&lt;br /&gt;
    selected_topic_ids = params[:topic]&amp;amp;.map(&amp;amp;:to_i) || []&lt;br /&gt;
    assignment_id = params[:assignment_id].to_i&lt;br /&gt;
&lt;br /&gt;
    unless current_user_can_modify_participant?(participant_id)&lt;br /&gt;
      flash[:error] = 'You are not authorized to modify these bids.'&lt;br /&gt;
      respond_to do |format|&lt;br /&gt;
        format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
        format.json { render json: { status: 'unauthorized' }, status: :unauthorized }&lt;br /&gt;
      end&lt;br /&gt;
      return&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    if selected_topic_ids.empty?&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      assignment = Assignment.find_by(id: assignment_id)&lt;br /&gt;
      if assignment.nil?&lt;br /&gt;
        flash[:error] = &amp;quot;Invalid assignment.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_assignment' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      unless SignUpTopic.where(id: selected_topic_ids, assignment_id: assignment_id).count == selected_topic_ids.size&lt;br /&gt;
        flash[:error] = &amp;quot;One or more selected topics are invalid.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_topics' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).where.not(signuptopic_id: selected_topic_ids).destroy_all&lt;br /&gt;
&lt;br /&gt;
      selected_topic_ids.each_with_index do |topic_id, index|&lt;br /&gt;
        bid = ReviewBid.find_or_initialize_by(signuptopic_id: topic_id, participant_id: participant_id)&lt;br /&gt;
        bid.priority = index + 1&lt;br /&gt;
        bid.assignment_id = assignment_id&lt;br /&gt;
        bid.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_to action: 'show', assignment_id: assignment_id, id: participant_id, notice: 'Review bids updated successfully.' }&lt;br /&gt;
      format.json { render json: { status: 'success' }, status: :ok }&lt;br /&gt;
    end&lt;br /&gt;
  rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
    flash[:error] = &amp;quot;Failed to update priorities: #{e.message}&amp;quot;&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
      format.json { render json: { status: 'error', message: e.message }, status: :unprocessable_entity }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Client Side Changes to Support &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Changes ====&lt;br /&gt;
We refactored the client-side code to ensure it passed the correct information from the view to the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action. On the client side, we now grab the topicIDs and send them as &amp;lt;code&amp;gt;selected_topic_ids&amp;lt;/code&amp;gt;, allowing the controller to figure out which topics were selected and in what order.  &lt;br /&gt;
&lt;br /&gt;
When working with jQuery, AJAX is awesome because it lets us update the server without reloading the page, making the user experience smooth and responsive. When we pull the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, we can grab it along with other data, like the list of topics, and include it as a single request. This way, we don’t have to rely on the URL to know which participant we’re working with, which makes the code cleaner.&lt;br /&gt;
&lt;br /&gt;
We also added an update handler so that any time the order of the selected topic changes in the view, information is sent to the appropriate controller action. This ensures that the controller and the view remain in sync, with real-time updates, and includes a mechanism for providing feedback if something goes wrong.&lt;br /&gt;
&lt;br /&gt;
Note: One interesting aspect of the application was the mix of JavaScript and CoffeeScript code. It wasn't clear why both types of code were used.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery -&amp;gt;&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: -&amp;gt;&lt;br /&gt;
      $.post($(this).data('update-url'), $(this).sortable('serialize'))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery(document).ready ($) -&amp;gt;&lt;br /&gt;
  sendUpdatedOrder = -&amp;gt;&lt;br /&gt;
    sortedIDs = $(this).sortable('toArray')&lt;br /&gt;
&lt;br /&gt;
    topicIDs = sortedIDs.map (id) -&amp;gt;&lt;br /&gt;
      id.replace 'topic_', ''&lt;br /&gt;
&lt;br /&gt;
    participant_id = $(this).data('participant-id')&lt;br /&gt;
&lt;br /&gt;
    assignment_id = new URL($(this).data('update-url'), window.location.origin).searchParams.get('assignment_id')&lt;br /&gt;
&lt;br /&gt;
    $.ajax&lt;br /&gt;
      type: 'POST'&lt;br /&gt;
      url: $(this).data('update-url')&lt;br /&gt;
      data:&lt;br /&gt;
        topic: topicIDs&lt;br /&gt;
        participant_id: participant_id&lt;br /&gt;
        assignment_id: assignment_id&lt;br /&gt;
      success: (response) -&amp;gt;&lt;br /&gt;
        console.log 'Selections updated successfully.'&lt;br /&gt;
      error: (xhr, status, error) -&amp;gt;&lt;br /&gt;
        alert 'An error occurred while saving your selections. Please try again.'&lt;br /&gt;
        console.error error&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: sendUpdatedOrder&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-1.-signupsheetcontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; Updates ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; method renders the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; action However, the view is associated with the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # provides variables for reviewing page located at views/review_bids/others_work.html.erb&lt;br /&gt;
  def index&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
    return unless current_user_id?(@participant.user_id)&lt;br /&gt;
&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
    # Finding how many reviews have been completed&lt;br /&gt;
    @num_reviews_completed = 0&lt;br /&gt;
    @review_mappings.each do |map|&lt;br /&gt;
      @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # render view for completing reviews after review bidding has been completed&lt;br /&gt;
    render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
  end  &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-2.-reviewbidsotherswork-view-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;review_bids_others_work.html.erb&amp;lt;/code&amp;gt; View Updates ====&lt;br /&gt;
The view is technically part of the SignUpSheetController, but it's being rendered by the ReviewBidsController, which initially threw us off. After chatting with the professor, we decided not to change this view. He indicated that the plan is to reuse these views for both assignment and review bidding.&lt;br /&gt;
&lt;br /&gt;
We suggest building the assignment bidding and review bidding features separately first to ensure each one works independently. Once that’s done, we can clean things up by combining any shared functionality. This could include creating shared views or even introducing an abstract model to handle both types of bidding more efficiently. It could also mean replacing the ReviewBidsController with something more generic, like a BidsController, to keep things reusable and easier to maintain down the line.&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Rename &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; method ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; should be &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt;&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:''' Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The primary activity here is to refactor the method name &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; and fix the hardcoded URL. However, the match topic bidding algorithm no longer works since Heroku is no longer free, per the professor. The reference link below has detailed information about the web service that we can leverage to gain that understanding.&lt;br /&gt;
&lt;br /&gt;
Reference Link for Webservice Details: [https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Updated Implementation ====&lt;br /&gt;
The original requirement was to rename the run_bidding_algorithm method to assign_reviewers, but after digging into the code, we realized that renaming it didn’t make sense. The method runs the bidding algorithm to match topics, and there’s already a separate method to handle assigning review topics.&lt;br /&gt;
&lt;br /&gt;
However, we improved the logic in the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; method (which calls the private &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt;) by introducing a separate &amp;lt;code&amp;gt;BiddingAlgorithmService&amp;lt;/code&amp;gt; object. This keeps the business logic out of the controller action, which makes things cleaner and more maintainable.&lt;br /&gt;
&lt;br /&gt;
==== review_bids_controller ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def run_bidding_algorithm&lt;br /&gt;
    review_bid = ReviewBid.new&lt;br /&gt;
    bidding_data = review_bid.bidding_data&lt;br /&gt;
    matched_topics = BiddingAlgorithmService.new(bidding_data).run&lt;br /&gt;
    raise ArgumentError, 'Failed to assign reviewers. Please try again later.' unless matched_topics&lt;br /&gt;
&lt;br /&gt;
    matched_topics&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== bidding_algorithm_service ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# frozen_string_literal: true&lt;br /&gt;
&lt;br /&gt;
# The `BiddingAlgorithmService` run the bid assignment algorithm&lt;br /&gt;
# Sends student IDs, topic IDs, student preferences, and timestamps to the web service&lt;br /&gt;
# The web service returns the matched assignments in the JSON response body&lt;br /&gt;
class BiddingAlgorithmService&lt;br /&gt;
  SERVICE_URL = 'http://app-csc517.herokuapp.com/match_topics'.freeze&lt;br /&gt;
&lt;br /&gt;
  # MOCK_DATA is based on the documentation detailing how the webservice behaves.&lt;br /&gt;
  # Reference: https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review#Webservice&lt;br /&gt;
  MOCK_DATA = {&lt;br /&gt;
    36239 =&amp;gt; [3970, 3972, 3975],&lt;br /&gt;
    36240 =&amp;gt; [3973, 3974, 3972],&lt;br /&gt;
    36241 =&amp;gt; [3969, 3971, 3972],&lt;br /&gt;
    36242 =&amp;gt; [3969, 3971, 3973],&lt;br /&gt;
    36243 =&amp;gt; [3969, 3970, 3971]&lt;br /&gt;
  }.freeze&lt;br /&gt;
&lt;br /&gt;
  def initialize(bidding_data)&lt;br /&gt;
    @bidding_data = bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def run&lt;br /&gt;
    return MOCK_DATA if Rails.application.config.use_mock_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
    perform_request&lt;br /&gt;
  rescue RestClient::ExceptionWithResponse, JSON::ParserError&lt;br /&gt;
    false&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def perform_request&lt;br /&gt;
    response = RestClient.post(&lt;br /&gt;
      SERVICE_URL,&lt;br /&gt;
      @bidding_data.to_json,&lt;br /&gt;
      content_type: :json,&lt;br /&gt;
      accept: :json&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    validate_response(response)&lt;br /&gt;
    JSON.parse(response.body)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def validate_response(response)&lt;br /&gt;
    raise 'Invalid response format' unless response.headers[:content_type] &amp;amp;&amp;amp; response.headers[:content_type].include?('application/json')&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Resolve Functionality Issues for Non Bidders ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Currently it doesn't work if some student does not bid. In this case, algorithm needs to be fixed to ignore anyone who didn’t bid, and let them choose from what’s left over.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to do several things within this controller. We will set a default state for non-bidders that we can point to to give them options to select topics to review from the leftovers. We will consolidate &amp;lt;code&amp;gt;@sign_up_topics&amp;lt;/code&amp;gt; to resolve the DRY violation. Lastly, we will remove business logic from the show method and push it to a new helper to conform to the SRP.&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # Additional method&lt;br /&gt;
&lt;br /&gt;
  # Provides variables for review bidding page&lt;br /&gt;
  def show&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id].to_i)&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @sign_up_topics = SignUpTopic.where(assignment_id: @assignment.id, private_to: nil)&lt;br /&gt;
    my_topic = SignedUpTeam.topic_id(@participant.parent_id, @participant.user_id)&lt;br /&gt;
    @sign_up_topics -= SignUpTopic.where(assignment_id: @assignment.id, id: my_topic)&lt;br /&gt;
    @num_participants = AssignmentParticipant.where(parent_id: @assignment.id).count&lt;br /&gt;
    @selected_topics = nil # This is used to list the topics assigned to review (i.e., select == assigned)&lt;br /&gt;
    @bids = ReviewBid.where(participant_id: @participant, assignment_id: @assignment.id)&lt;br /&gt;
    signed_up_topics = []&lt;br /&gt;
    &lt;br /&gt;
    @bids.each do |bid|&lt;br /&gt;
      sign_up_topic = SignUpTopic.find_by(id: bid.signuptopic_id)&lt;br /&gt;
      signed_up_topics &amp;lt;&amp;lt; sign_up_topic if sign_up_topic&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    signed_up_topics &amp;amp;= @sign_up_topics&lt;br /&gt;
    @sign_up_topics -= signed_up_topics&lt;br /&gt;
    @bids = signed_up_topics&lt;br /&gt;
    @num_of_topics = @sign_up_topics.size&lt;br /&gt;
    @assigned_review_maps = []&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: @assignment.id, reviewer_id: @participant.id).each do |review_map|&lt;br /&gt;
      @assigned_review_maps &amp;lt;&amp;lt; review_map&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Explicitly render view since it's in the sign up sheet views&lt;br /&gt;
    render 'sign_up_sheet/review_bids_show'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Additional methods&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-2.-reviewbidshelper-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
 # Assigns bidding topics to reviewers&lt;br /&gt;
  def assign_bidding&lt;br /&gt;
      assignment = validate_assignment(params[:assignment_id])&lt;br /&gt;
&lt;br /&gt;
      reviewers = validate_reviewers(assignment.id)&lt;br /&gt;
      reviewer_ids = reviewers.map(&amp;amp;:id)&lt;br /&gt;
&lt;br /&gt;
      matched_topics = run_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
      if matched_topics.blank?&lt;br /&gt;
        flash[:alert] = 'Topic or assignment is missing'&lt;br /&gt;
        redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      leftover_topics = find_leftover_topics(assignment.id, matched_topics)&lt;br /&gt;
      assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.new.assign_review_topics(matched_topics)&lt;br /&gt;
      assignment.update!(can_choose_topic_to_review: false)&lt;br /&gt;
&lt;br /&gt;
      flash[:notice] = 'Reviewers were successfully assigned to topics.'&lt;br /&gt;
      redirect_back fallback_location: root_path&lt;br /&gt;
    rescue ArgumentError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ArgumentError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: e.message&lt;br /&gt;
    rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::RecordInvalid: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue ActiveRecord::ActiveRecordError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::ActiveRecordError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue StandardError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;StandardError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers. Please try again later.'&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  #service methods here ...&lt;br /&gt;
&lt;br /&gt;
  def find_leftover_topics(assignment_id, matched_topics)&lt;br /&gt;
    all_topic_ids = SignUpTopic.where(assignment_id: assignment_id).pluck(:id)&lt;br /&gt;
    assigned_topic_ids = matched_topics.map { |match| match[:topic_id] }&lt;br /&gt;
&lt;br /&gt;
    # Calculate leftover topics&lt;br /&gt;
    all_topic_ids - assigned_topic_ids&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
    return if leftover_topics.blank?&lt;br /&gt;
&lt;br /&gt;
    # Find non-bidders by excluding already matched reviewers&lt;br /&gt;
    non_bidders = reviewer_ids - matched_topics.keys&lt;br /&gt;
&lt;br /&gt;
    # Assign leftover topics to non-bidders in a round-robin fashion&lt;br /&gt;
    non_bidders.each_with_index do |reviewer_id, index|&lt;br /&gt;
      topic_id = leftover_topics[index % leftover_topics.length]&lt;br /&gt;
      ReviewBid.create(priority: 1, signuptopic_id: topic_id, participant_id: reviewer_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirm Functionality for Single-person Teams ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Make sure your code works on individual assignments, i.e., assignments where participants sign up for topics instead of teams. In this case, a team is created for each participant when they sign up. So the code should work for assignments to which either individuals or teams submit.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to make an update to focus on topic rather than team or have it where teams are updated based on topic id.We will probably utilize sign up topic or a similar object to accomplish this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-5-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bid Status Messaging ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| To make the functionality more intuitive, include a message to say how many students are eligible to submit bids, how many have submitted their bids, when the deadline for submitting bids is.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
The logic for these messages will go into the helper file. It is effectively the same reasoning as our migration of other business/functionality code from the controller to the helper to conform to the SRP. It will go hand in hand with the SignUpTopic portion of the code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-6-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Why are the methods in review_bid.rb class methods? Can we change them to instance methods or move it to helpers?&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-1.-reviewbid-model-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model Updates ====&lt;br /&gt;
Since the current class methods are tied to &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model instances, we will change them to instance methods. There is no need to move them to helpers.&lt;br /&gt;
&lt;br /&gt;
===== Review Bid Model Flow Before Refactoring =====&lt;br /&gt;
This sequence diagram describes how the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; interacts with the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model in assigning review topics to reviewers according to their bids. Once the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; action is initiated by the user, the controller gathers the IDs of reviewers and calls &amp;lt;code&amp;gt;ReviewBid.bidding_data&amp;lt;/code&amp;gt; to gather bidding information. That is sent out to an external bidding algorithm, and the matched topics that come back are used by &amp;lt;code&amp;gt;ReviewBid.assign_review_topics&amp;lt;/code&amp;gt; to create &amp;lt;code&amp;gt;ReviewResponseMap&amp;lt;/code&amp;gt; entries, assigning topics to reviewers for the assignment.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
  belongs_to :assignment, class_name: 'Assignment'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  def self.bidding_data(assignment_id, reviewer_ids)&lt;br /&gt;
    # create basic hash and set basic hash data&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'users' =&amp;gt; {}, 'max_accepted_proposals' =&amp;gt; [] }&lt;br /&gt;
    bidding_data['tid'] = SignUpTopic.where(assignment_id: assignment_id).ids&lt;br /&gt;
    bidding_data['max_accepted_proposals'] = Assignment.where(id: assignment_id).pluck(:num_reviews_allowed).first&lt;br /&gt;
&lt;br /&gt;
    # loop through reviewer_ids to get reviewer specific bidding data&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      bidding_data['users'][reviewer_id] = reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # assigns topics to reviews as matched by the webservice algorithm&lt;br /&gt;
  def self.assign_review_topics(assignment_id, reviewer_ids, matched_topics, _min_num_reviews = 2)&lt;br /&gt;
    # if review response map already created, delete it&lt;br /&gt;
    if ReviewResponseMap.where(reviewed_object_id: assignment_id)&lt;br /&gt;
      ReviewResponseMap.where(reviewed_object_id: assignment_id).destroy_all&lt;br /&gt;
    end&lt;br /&gt;
    # loop through reviewer_ids to assign reviews to each reviewer&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      topics_to_assign = matched_topics[reviewer_id.to_s]&lt;br /&gt;
      topics_to_assign.each do |topic|&lt;br /&gt;
        assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method to assign a single topic to a reviewer&lt;br /&gt;
  def self.assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic).pluck(:team_id).first&lt;br /&gt;
    team_to_review.nil? ? [] : ReviewResponseMap.create(reviewed_object_id: assignment_id, reviewer_id: reviewer_id, reviewee_id: team_to_review, type: 'ReviewResponseMap')&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method for getting individual reviewer_ids bidding data&lt;br /&gt;
  # returns user's bidding data hash&lt;br /&gt;
  def self.reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    reviewer_user_id = AssignmentParticipant.find(reviewer_id).user_id&lt;br /&gt;
    self_topic = SignedUpTeam.topic_id(assignment_id, reviewer_user_id)&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'otid' =&amp;gt; self_topic, 'priority' =&amp;gt; [], 'time' =&amp;gt; [] }&lt;br /&gt;
    bids = ReviewBid.where(participant_id: reviewer_id)&lt;br /&gt;
&lt;br /&gt;
    # loop through each bid for a topic to get specific data&lt;br /&gt;
    bids.each do |bid|&lt;br /&gt;
      bidding_data['tid'] &amp;lt;&amp;lt; bid.signuptopic_id&lt;br /&gt;
      bidding_data['priority'] &amp;lt;&amp;lt; bid.priority&lt;br /&gt;
      bidding_data['time'] &amp;lt;&amp;lt; bid.updated_at&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  # Instance method to get bidding data for this review bid&lt;br /&gt;
  def bidding_data&lt;br /&gt;
    {&lt;br /&gt;
      'tid' =&amp;gt; topic_ids,&lt;br /&gt;
      'otid' =&amp;gt; self_topic,&lt;br /&gt;
      'priority' =&amp;gt; priorities,&lt;br /&gt;
      'time' =&amp;gt; updated_times&lt;br /&gt;
    }&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # bid object to limit db touches for similar data acquisitions&lt;br /&gt;
  def bids&lt;br /&gt;
    @bids ||= ReviewBid.where(participant_id: participant_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def topic_ids&lt;br /&gt;
    bids.pluck(:signuptopic_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def priorities&lt;br /&gt;
    bids.pluck(:priority)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def updated_times&lt;br /&gt;
    bids.pluck(:updated_at)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self_topic&lt;br /&gt;
    return nil unless topic &amp;amp;&amp;amp; topic.assignment_id&lt;br /&gt;
&lt;br /&gt;
    SignedUpTeam.topic_id(topic.assignment_id, participant.user_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign topics to reviews&lt;br /&gt;
  def assign_review_topics(matched_topics)&lt;br /&gt;
    validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: topic.assignment_id).destroy_all&lt;br /&gt;
&lt;br /&gt;
    matched_topics.each do |reviewer_id, topics|&lt;br /&gt;
      Array(topics).each { |topic_id| assign_topic_to_reviewer(reviewer_id, topic_id) }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign a single topic to a reviewer&lt;br /&gt;
  def assign_topic_to_reviewer(reviewer_id, topic_id)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic_id).pluck(:team_id).first&lt;br /&gt;
    return if team_to_review.nil?&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.create(&lt;br /&gt;
      reviewed_object_id: topic.assignment_id,&lt;br /&gt;
      reviewer_id: reviewer_id,&lt;br /&gt;
      reviewee_id: team_to_review,&lt;br /&gt;
      type: 'ReviewResponseMap'&lt;br /&gt;
    )&lt;br /&gt;
    # error handling&lt;br /&gt;
  rescue StandardError =&amp;gt; e&lt;br /&gt;
    Rails.logger.error(&amp;quot;Failed to assign topic #{topic_id} to reviewer #{reviewer_id}: #{e.message}&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
    raise ArgumentError, 'Topic or assignment is missing' if topic.nil? || topic.assignment_id.nil?&lt;br /&gt;
    raise ArgumentError, 'Matched topics must be a Hash' unless matched_topics.is_a?(Hash)&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
Once we changed them to instance methods, we modified their use in the controller code to instantiate the object first.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-3.-schema.db-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;Schema.db&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We also need to remove the foreign key reference to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model since the &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model already has access to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;, and that is the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; we should be referencing. This will also involve modifying the database to remove the foreign key reference to the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Steps required:&lt;br /&gt;
&lt;br /&gt;
# Review the records in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table in the database to see if there are any records and if those records have values for &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;&lt;br /&gt;
# Create a migration file using the command: &amp;lt;code&amp;gt;rails generate migration RemoveAssignmentFromReviewBids&amp;lt;/code&amp;gt;&lt;br /&gt;
# In the generated file, indicate the change that needs to be made using the change method and migration columns &amp;lt;code&amp;gt;remove_foreign_key&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;remove_column&amp;lt;/code&amp;gt;. Migration should be straightforward since there is no cascading behavior (i.e., &amp;lt;code&amp;gt;dependent:destroy&amp;lt;/code&amp;gt;) to worry about.&lt;br /&gt;
# Once comfortable with the updates to the generated file, run the migration using the &amp;lt;code&amp;gt;rails db:migrate&amp;lt;/code&amp;gt; command.&lt;br /&gt;
# Run a query in the DB to ensure no orphaned records are in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table after the updates.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-4.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implement Edge Case Testing ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| In the previous implementation wiki, there are edge cases which are not exhaustively tested. Should test those edge cases thoroughly and add more edge case testing - link&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span class=&amp;quot;mark&amp;quot;&amp;gt;TODO&amp;lt;/span&amp;gt;:We just got initial test results back. We still need to figure out why tests are failing and what may be slipping through the cracks.[[File:Test expertiza.png|411x459px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-8-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design Patterns and Principles ===&lt;br /&gt;
Below are the design patterns and principles we plan to use to address the problem statements outlined in the project requirements:&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
** Controller Responsibility&lt;br /&gt;
*** To address the issue of authentication logic residing in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, we will refactor the code to ensure the controller solely handles request and response flows.&lt;br /&gt;
*** Authentication logic will be moved to dedicated methods in &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt;, making the codebase easier to maintain and test.&lt;br /&gt;
** View Simplification&lt;br /&gt;
*** Currently, the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; view contains excessive business logic, violating SRP.&lt;br /&gt;
*** We will move this logic into helper methods or models, ensuring each component has a single responsibility and improving code readability.&lt;br /&gt;
&lt;br /&gt;
* Separation of Concerns (SoC)&lt;br /&gt;
** By relocating authorization logic out of the controller, we separate authentication from request handling.&lt;br /&gt;
** Adhering to SoC enhances readability and maintainability by allowing each part of the application to focus on its specific role.&lt;br /&gt;
* Don't Repeat Yourself (DRY)&lt;br /&gt;
** We will inspect the existing implementation for code duplication, particularly in the controller actions.&lt;br /&gt;
** Any repetitive code will be refactored or removed to reduce redundancy and improve clarity.&lt;br /&gt;
&lt;br /&gt;
* Model-View-Controller (MVC) Adherence&lt;br /&gt;
** Moving business logic from the view aligns with MVC best practices.&lt;br /&gt;
** By ensuring the view only presents data without processing it, we maintain clear boundaries between the model, view, and controller layers.&lt;br /&gt;
&lt;br /&gt;
* Dependency Inversion Principle (DIP)&lt;br /&gt;
** We plan to update class methods to instance methods, allowing the controller to depend on abstractions rather than concrete implementations.&lt;br /&gt;
** This change leverages Ruby's duck typing, providing flexibility and making the code easier to maintain and test.&lt;br /&gt;
&lt;br /&gt;
** Clear and intentional naming during refactoring will enhance code clarity and maintainability.&lt;br /&gt;
&lt;br /&gt;
== Files Modified/Added ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-1.-controllers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
review_bids_controller.rb&lt;br /&gt;
&lt;br /&gt;
signup_sheet_controller.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-2.-models&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
review_bid.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-3.-helpers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
&lt;br /&gt;
authorization_helper.rb&lt;br /&gt;
&lt;br /&gt;
Review_bids_helper.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;viii.-test-plan&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
The Excel spreadsheet below details our test plan for testing the code we changed or created to ensure code coverage. The process involved reviewing the code and determining all the possible scenarios that needed to be covered based on what the method was doing, then creating test cases accordingly.&lt;br /&gt;
&lt;br /&gt;
* [https://docs.google.com/spreadsheets/d/1iHzOdSwlTtxOtf5VsNdwOaCP8NzYMmiJ/edit?usp=drive_link Open Test Matrix Spreadsheet]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/review_bids_controller_spec.rb ReviewBidsController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/sign_up_sheet_controller_spec.rb SignUpSheetController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/models/review_bid_spec.rb ReviewBid model updated test cases]&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-1.-mentor&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Ed Gehringer&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-2.-members&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Members ===&lt;br /&gt;
&lt;br /&gt;
* Gavin Teague&lt;br /&gt;
* Janice Uwujaren&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xi.-links&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
Previous Wiki: [https://wiki.expertiza.ncsu.edu/index.php/CSC/ECE_517_Fall_2021_-_E2151._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;Link&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xii.-references&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/document/d/1LHVacylQmB15I6VcYUA5nuC7GRRU9szHzZhjlPcE-PA/edit?tab=t.0#heading=h.u0oycoj6e0ev &amp;lt;u&amp;gt;Project Instructions&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;appendix-a-uml-class-diagram-plantuml-code-for-review-bids&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Juwujar</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=161046</id>
		<title>CSC/ECE 517 Fall 2024 - E2485. Allow reviewers to bid on what to review</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=161046"/>
		<updated>2024-12-12T05:51:13Z</updated>

		<summary type="html">&lt;p&gt;Juwujar: /* Client Side Changes to Support ReviewBidsController#set_priority Changes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
This project will fix the defects and improve the functionality in Expertiza of the Reviewer Bidding feature. It will make the functionality smoother, more reliable, and align with Expertiza's collaborative learning objectives. &lt;br /&gt;
&lt;br /&gt;
The Review Bidding feature lets reviewers focus on topics they know best or care about most. If someone feels exceptionally qualified or interested in a topic, they can bid to review submissions in that area. This helps ensure reviews are insightful and enjoyable for the reviewer while benefiting the other participants and the overall process.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iii.-problem-statements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Problem Statements ==&lt;br /&gt;
&lt;br /&gt;
Assigning reviews to users on Expertiza involves a complex bidding algorithm that requires optimization before production deployment. Below are key areas we plan to address as per the project requirements:&lt;br /&gt;
* Code Refactoring and Best Practices&lt;br /&gt;
** '''Utilize Authentication Utilities''': Modify the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; function in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to use existing authentication utilities, enhancing code reusability and adhering to the Single Responsibility Principle (SRP) and Separation of Concerns (SoC).&lt;br /&gt;
** '''Eliminate Code Duplication''': Refactor &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; to comply with the Don't Repeat Yourself (DRY) principle.&lt;br /&gt;
** '''Rename Functions for Clarity''': Change &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; to better reflect its purpose.&lt;br /&gt;
** '''Refactor Methods in '''&amp;lt;code&amp;gt;review_bid.rb&amp;lt;/code&amp;gt;''':''': Convert class methods to instance methods or relocate them to helper classes for improved code organization.&lt;br /&gt;
* Functionality Enhancements&lt;br /&gt;
** '''Handle Missing Bids Gracefully''': Adjust the bidding algorithm to accommodate students who do not bid, allowing them to select from the remaining options.&lt;br /&gt;
** '''Support Both Individual and Team Assignments''': Ensure the code functions correctly for participant and team-based assignments when signing up for topics.&lt;br /&gt;
* User Interface Improvements&lt;br /&gt;
** '''Increase Transparency in the Bidding Process''': Display messages indicating how many students are eligible to bid, how many have submitted bids, and the deadline.&lt;br /&gt;
** '''Accurate Bid Status Indicators''': Ensure topic colors change correctly based on the number of outstanding bids, providing clear visual feedback to users.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Exhaustive Edge Case Testing''': Conduct thorough testing for edge cases not previously covered, adding additional test cases as needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iv.-design-goals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Design Goals ==&lt;br /&gt;
&lt;br /&gt;
While addressing the existing defects and enhancing the Reviewer Bidding feature, we plan to adhere to the following design guidelines:&lt;br /&gt;
&lt;br /&gt;
* Code Quality and Maintainability&lt;br /&gt;
** '''Improve Code Structure: '''Implement refactoring strategies to enhance code readability and maintainability, ensuring adherence to best practices like the DRY principle.&lt;br /&gt;
** '''Standardize Authentication Logic: '''Utilize a centralized authentication utility to promote consistency and reusability across the application.&lt;br /&gt;
* System Stability and Flexibility&lt;br /&gt;
** '''Enhance Algorithm Robustness: '''Modify the bidding algorithm to handle scenarios where students do not bid, ensuring the system remains stable under all conditions.&lt;br /&gt;
** '''Ensure Assignment Versatility: '''Design the system to seamlessly support both individual and team assignments without additional configuration.&lt;br /&gt;
* User Experience Enhancements&lt;br /&gt;
** '''Increase Bidding Transparency: '''Develop user interface elements that clearly display bidding status, deadlines, and participation metrics.&lt;br /&gt;
** '''Implement Dynamic Visual Feedback: '''Use real-time visual cues, such as color changes, to inform users about the bidding status instantly.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Develop Comprehensive Test Suites: '''Create extensive automated tests covering edge cases and critical functionalities to guarantee reliable system behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;v.-uml-diagram&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== UML Diagram ==&lt;br /&gt;
&lt;br /&gt;
Below is a visual representation of the models associated with the review bids functionality, depicting the data structures and their relationships.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml class diagram2.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi.-implementation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
'''ReviewBid: '''The &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; manages the bidding process, where participants indicate their preferences for reviewing different topics or assignments. This helps distribute review tasks fairly based on participants' interests.&lt;br /&gt;
&lt;br /&gt;
'''Assignment: '''The &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model represents team or individual assignments in Expertiza. It stores all the essential information required to keep the assignment process organized, like managing participants, setting due dates, forming teams, and overseeing the review process.&lt;br /&gt;
&lt;br /&gt;
'''SignUpTopic: '''The &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt; model identifies specific topics or areas within an assignment for which participants can sign up. It manages the availability of these topics, tracks bids, coordinates team sign-ups, and assigns topics based on both bids and participant availability. &lt;br /&gt;
&lt;br /&gt;
'''Participant: '''The &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model is about the individual users who participate in an assignment. This includes students, instructors, teaching assistants, or other relevant roles. This model is responsible for managing key user information, monitoring interactions with the assignment, keeping track of team memberships, and recording things like badges and review grades.&lt;br /&gt;
&lt;br /&gt;
== Implementation ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1.-decoupling-authorization-logic-from-reviewbidscontroller&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Decoupling Authorization Logic from &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| '''&amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; needs to use the authentication utilities'''&lt;br /&gt;
&lt;br /&gt;
The function &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; should use the available authentication utilities to support the Single Responsibility Principle and Separation of Concerns and to support code reusability.&lt;br /&gt;
&lt;br /&gt;
| '''Authentication Consistency:''' Remove authentication logic in the controller and call logic in the authentication utility class instead to ensure consistent use of the same logic across the application, foster reusability, and eliminate DRY.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
==== Authorization Flow Before Refactoring ====&lt;br /&gt;
The sequence diagram depicts the authorization process within the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;. When a user invokes an action, the controller checks if the action is allowed by verifying the user's role through the &amp;lt;code&amp;gt;current_role_name()&amp;lt;/code&amp;gt; method in the &amp;lt;code&amp;gt;ApplicationController&amp;lt;/code&amp;gt;. Depending on the action and the user's role, the controller either authorizes the user to proceed or denies access, ultimately executing the action logic if authorized and responding to the user.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 1.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We simplified the &amp;lt;code&amp;gt;action_allowed?&amp;lt;/code&amp;gt; method by decoupling authorization logic from the controller to use helper methods in the &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt; module. The &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; should not handle authentication, which is a violation of SRP. &lt;br /&gt;
&lt;br /&gt;
Note: We also made similar updates to the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; methods for &amp;lt;code&amp;gt;SignUpController&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;vertical-align:top;&amp;quot; | Original Code !! style=&amp;quot;vertical-align:top;&amp;quot; | Refactored Code&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index'&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator',&lt;br /&gt;
     'Student'].include? current_role_name and&lt;br /&gt;
        ((%w[list].include? action_name) ? are_needed_authorizations_present?(params[:id], &amp;quot;participant&amp;quot;, &amp;quot;reader&amp;quot;, &amp;quot;submitter&amp;quot;, &amp;quot;reviewer&amp;quot;) : true)&lt;br /&gt;
  else&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator'].include? current_role_name&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index', 'list'&lt;br /&gt;
    current_user_has_student_privileges? &amp;amp;&amp;amp; list_authorization_check&lt;br /&gt;
  else&lt;br /&gt;
    current_user_has_ta_privileges?&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; action ===&lt;br /&gt;
Previously, the &amp;lt;code&amp;gt;ReviewBidsController's index&amp;lt;/code&amp;gt; action was overloaded with responsibilities—it handled everything from locating and validating assignments to fetching review mappings and counting completed reviews. This all-in-one approach made the controller action messy. &lt;br /&gt;
&lt;br /&gt;
We have moved the business logic from the controller action and into dedicated service objects. With this change, we introduced two key service objects: &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; focuses on retrieving the participant along with their assignments, while the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; takes on the task of managing review mappings and calculating review-related stats, like how many reviews have been completed. &lt;br /&gt;
&lt;br /&gt;
This separation of responsibilities aligns with the Single Responsibility Principle. The logic for fetching assignments, retrieving review mappings, and counting reviews is now more flexible and reusable throughout the application. For example, we used methods in the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; in both the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; and the &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;, helping to avoid code duplication.The article https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial served as a guide on when and how to use service objects in Rails.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
  return unless current_user_id?(@participant.user_id)&lt;br /&gt;
  @assignment = @participant.assignment&lt;br /&gt;
  @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
  # Finding how many reviews have been completed&lt;br /&gt;
  @num_reviews_completed = 0&lt;br /&gt;
  @review_mappings.each do |map|&lt;br /&gt;
    @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # render view for completing reviews after review bidding has been completed&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @assignment = participant_service.assignment&lt;br /&gt;
  unless @assignment.is_a?(Assignment)&lt;br /&gt;
    flash[:error] = 'Assignment not found.'&lt;br /&gt;
    redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  @review_mappings = review_service.review_mappings&lt;br /&gt;
  @num_reviews_completed = review_service.review_counts[:completed]&lt;br /&gt;
&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Optimize &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Action functionality ===&lt;br /&gt;
In the earlier version of the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, the server only needed a couple of key pieces of information: an array of topic IDs (params[:topic]) and the participant ID (params[:id}). From there, it would figure out the assignment_id by using the first topic as a reference. This is risky because the &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt; associated with the topic may not be a valid &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
With the new implementation, we structured the parameters to include the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt;, an array of topic IDs, and &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;. This change ensures that the server always receives complete and reliable information, preventing issues caused by missing or inconsistent data. It also removes the need for the server to perform unnecessary lookups, as in the case of &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;, allowing the controller to focus on receiving requests and rendering appropriate responses.&lt;br /&gt;
&lt;br /&gt;
While these updates have cleaned up the set_priority action, they still contain a lot of business logic, like validations and priority updates. To make the code even more maintainable, there’s room for further refactoring—perhaps moving some of this logic into a service class or model method to streamline the controller even more.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | Old Code&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    if params[:topic].nil?&lt;br /&gt;
      ReviewBid.where(participant_id: params[:id]).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      participant = AssignmentParticipant.find_by(id: params[:id])&lt;br /&gt;
      assignment_id = SignUpTopic.find(params[:topic].first).assignment.id&lt;br /&gt;
      @bids = ReviewBid.where(participant_id: params[:id])&lt;br /&gt;
      signed_up_topics = ReviewBid.where(participant_id: params[:id]).map(&amp;amp;:signuptopic_id)&lt;br /&gt;
      signed_up_topics -= params[:topic].map(&amp;amp;:to_i)&lt;br /&gt;
      signed_up_topics.each do |topic|&lt;br /&gt;
        ReviewBid.where(signuptopic_id: topic, participant_id: params[:id]).destroy_all&lt;br /&gt;
      end&lt;br /&gt;
      params[:topic].each_with_index do |topic_id, index|&lt;br /&gt;
        bid_existence = ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id])&lt;br /&gt;
        if bid_existence.empty?&lt;br /&gt;
          ReviewBid.create(priority: index + 1, signuptopic_id: topic_id, participant_id: params[:id], assignment_id: assignment_id)&lt;br /&gt;
        else&lt;br /&gt;
          ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id]).update_all(priority: index + 1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to action: 'show', assignment_id: params[:assignment_id], id: params[:id]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    participant_id = params[:participant_id].to_i&lt;br /&gt;
    selected_topic_ids = params[:topic]&amp;amp;.map(&amp;amp;:to_i) || []&lt;br /&gt;
    assignment_id = params[:assignment_id].to_i&lt;br /&gt;
&lt;br /&gt;
    unless current_user_can_modify_participant?(participant_id)&lt;br /&gt;
      flash[:error] = 'You are not authorized to modify these bids.'&lt;br /&gt;
      respond_to do |format|&lt;br /&gt;
        format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
        format.json { render json: { status: 'unauthorized' }, status: :unauthorized }&lt;br /&gt;
      end&lt;br /&gt;
      return&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    if selected_topic_ids.empty?&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      assignment = Assignment.find_by(id: assignment_id)&lt;br /&gt;
      if assignment.nil?&lt;br /&gt;
        flash[:error] = &amp;quot;Invalid assignment.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_assignment' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      unless SignUpTopic.where(id: selected_topic_ids, assignment_id: assignment_id).count == selected_topic_ids.size&lt;br /&gt;
        flash[:error] = &amp;quot;One or more selected topics are invalid.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_topics' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).where.not(signuptopic_id: selected_topic_ids).destroy_all&lt;br /&gt;
&lt;br /&gt;
      selected_topic_ids.each_with_index do |topic_id, index|&lt;br /&gt;
        bid = ReviewBid.find_or_initialize_by(signuptopic_id: topic_id, participant_id: participant_id)&lt;br /&gt;
        bid.priority = index + 1&lt;br /&gt;
        bid.assignment_id = assignment_id&lt;br /&gt;
        bid.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_to action: 'show', assignment_id: assignment_id, id: participant_id, notice: 'Review bids updated successfully.' }&lt;br /&gt;
      format.json { render json: { status: 'success' }, status: :ok }&lt;br /&gt;
    end&lt;br /&gt;
  rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
    flash[:error] = &amp;quot;Failed to update priorities: #{e.message}&amp;quot;&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
      format.json { render json: { status: 'error', message: e.message }, status: :unprocessable_entity }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Client Side Changes to Support &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Changes ====&lt;br /&gt;
We refactored the client-side code to ensure it passed the correct information from the view to the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action. On the client side, we now grab the topicIDs and send them as &amp;lt;code&amp;gt;selected_topic_ids&amp;lt;/code&amp;gt;, allowing the controller to figure out which topics were selected and in what order.  &lt;br /&gt;
&lt;br /&gt;
When working with jQuery, AJAX is awesome because it lets us update the server without reloading the page, making the user experience smooth and responsive. When we pull the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, we can grab it along with other data, like the list of topics, and include it as a single request. This way, we don’t have to rely on the URL to know which participant we’re working with, which makes the code cleaner.&lt;br /&gt;
&lt;br /&gt;
We also added an update handler so that any time the order of the selected topic changes in the view, information is sent to the appropriate controller action. This ensures that the controller and the view remain in sync, with real-time updates, and includes a mechanism for providing feedback if something goes wrong.&lt;br /&gt;
&lt;br /&gt;
Note: One interesting aspect of the application was the mix of JavaScript and CoffeeScript code. It wasn't clear why both types of code were used.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery -&amp;gt;&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: -&amp;gt;&lt;br /&gt;
      $.post($(this).data('update-url'), $(this).sortable('serialize'))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery(document).ready ($) -&amp;gt;&lt;br /&gt;
  sendUpdatedOrder = -&amp;gt;&lt;br /&gt;
    sortedIDs = $(this).sortable('toArray')&lt;br /&gt;
&lt;br /&gt;
    topicIDs = sortedIDs.map (id) -&amp;gt;&lt;br /&gt;
      id.replace 'topic_', ''&lt;br /&gt;
&lt;br /&gt;
    participant_id = $(this).data('participant-id')&lt;br /&gt;
&lt;br /&gt;
    assignment_id = new URL($(this).data('update-url'), window.location.origin).searchParams.get('assignment_id')&lt;br /&gt;
&lt;br /&gt;
    $.ajax&lt;br /&gt;
      type: 'POST'&lt;br /&gt;
      url: $(this).data('update-url')&lt;br /&gt;
      data:&lt;br /&gt;
        topic: topicIDs&lt;br /&gt;
        participant_id: participant_id&lt;br /&gt;
        assignment_id: assignment_id&lt;br /&gt;
      success: (response) -&amp;gt;&lt;br /&gt;
        console.log 'Selections updated successfully.'&lt;br /&gt;
      error: (xhr, status, error) -&amp;gt;&lt;br /&gt;
        alert 'An error occurred while saving your selections. Please try again.'&lt;br /&gt;
        console.error error&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: sendUpdatedOrder&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-1.-signupsheetcontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; Updates ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; method renders the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; action in &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt;. However, the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; does not include the controller action for this view. We need to find the old implementation of the controller action, add it back, and review and update it for any DRY violations.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # provides variables for reviewing page located at views/review_bids/others_work.html.erb&lt;br /&gt;
  def index&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
    return unless current_user_id?(@participant.user_id)&lt;br /&gt;
&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
    # Finding how many reviews have been completed&lt;br /&gt;
    @num_reviews_completed = 0&lt;br /&gt;
    @review_mappings.each do |map|&lt;br /&gt;
      @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # render view for completing reviews after review bidding has been completed&lt;br /&gt;
    render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
  end  &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-2.-reviewbidsotherswork-view-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;review_bids_others_work.html.erb&amp;lt;/code&amp;gt; View Updates ====&lt;br /&gt;
&lt;br /&gt;
The code only contains a view with this name and no associated controller action. Reviewing the code in the view, we noticed some SRP violations where there is much logic in the presentation layer. Set up helper methods in the controller to handle some of that logic or see if the model can handle some of that logic.&lt;br /&gt;
&lt;br /&gt;
Note: The view is technically part of the SignUpSheetController, but it's being rendered by the ReviewBidsController, which initially threw us off. After chatting with the professor, we decided not to change this view. He indicated that the plan is to reuse these views for both assignment bidding and review bidding.&lt;br /&gt;
&lt;br /&gt;
We would suggest building the assignment bidding and review bidding features separately first to make sure each one works on its own. Once that’s done, we can go back and clean things up by combining any shared functionality. This could include creating shared views or even introducing an abstract model to handle both types of bidding in a cleaner way. It could also mean replacing the ReviewBidsController with something more generic, like a BidsController, to keep things reusable and easier to maintain down the line.&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Rename &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; method ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; should be &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt;&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:''' Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The primary activity here is to refactor the method name &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; and fix the hardcoded URL. However, the match topic bidding algorithm no longer works since Heroku is no longer free, per the professor. The reference link below has detailed information about the web service that we can leverage to gain that understanding.&lt;br /&gt;
&lt;br /&gt;
Reference Link for Webservice Details: [https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Updated Implementation ====&lt;br /&gt;
The original requirement was to rename the run_bidding_algorithm method to assign_reviewers, but after digging into the code, we realized that renaming it didn’t make sense. The method runs the bidding algorithm to match topics, and there’s already a separate method to handle assigning review topics.&lt;br /&gt;
&lt;br /&gt;
However, we improved the logic in the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; method (which calls the private &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt;) by introducing a separate &amp;lt;code&amp;gt;BiddingAlgorithmService&amp;lt;/code&amp;gt; object. This keeps the business logic out of the controller action, which makes things cleaner and more maintainable.&lt;br /&gt;
&lt;br /&gt;
==== review_bids_controller ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def run_bidding_algorithm&lt;br /&gt;
    review_bid = ReviewBid.new&lt;br /&gt;
    bidding_data = review_bid.bidding_data&lt;br /&gt;
    matched_topics = BiddingAlgorithmService.new(bidding_data).run&lt;br /&gt;
    raise ArgumentError, 'Failed to assign reviewers. Please try again later.' unless matched_topics&lt;br /&gt;
&lt;br /&gt;
    matched_topics&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== bidding_algorithm_service ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# frozen_string_literal: true&lt;br /&gt;
&lt;br /&gt;
# The `BiddingAlgorithmService` run the bid assignment algorithm&lt;br /&gt;
# Sends student IDs, topic IDs, student preferences, and timestamps to the web service&lt;br /&gt;
# The web service returns the matched assignments in the JSON response body&lt;br /&gt;
class BiddingAlgorithmService&lt;br /&gt;
  SERVICE_URL = 'http://app-csc517.herokuapp.com/match_topics'.freeze&lt;br /&gt;
&lt;br /&gt;
  # MOCK_DATA is based on the documentation detailing how the webservice behaves.&lt;br /&gt;
  # Reference: https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review#Webservice&lt;br /&gt;
  MOCK_DATA = {&lt;br /&gt;
    36239 =&amp;gt; [3970, 3972, 3975],&lt;br /&gt;
    36240 =&amp;gt; [3973, 3974, 3972],&lt;br /&gt;
    36241 =&amp;gt; [3969, 3971, 3972],&lt;br /&gt;
    36242 =&amp;gt; [3969, 3971, 3973],&lt;br /&gt;
    36243 =&amp;gt; [3969, 3970, 3971]&lt;br /&gt;
  }.freeze&lt;br /&gt;
&lt;br /&gt;
  def initialize(bidding_data)&lt;br /&gt;
    @bidding_data = bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def run&lt;br /&gt;
    return MOCK_DATA if Rails.application.config.use_mock_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
    perform_request&lt;br /&gt;
  rescue RestClient::ExceptionWithResponse, JSON::ParserError&lt;br /&gt;
    false&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def perform_request&lt;br /&gt;
    response = RestClient.post(&lt;br /&gt;
      SERVICE_URL,&lt;br /&gt;
      @bidding_data.to_json,&lt;br /&gt;
      content_type: :json,&lt;br /&gt;
      accept: :json&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    validate_response(response)&lt;br /&gt;
    JSON.parse(response.body)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def validate_response(response)&lt;br /&gt;
    raise 'Invalid response format' unless response.headers[:content_type] &amp;amp;&amp;amp; response.headers[:content_type].include?('application/json')&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Resolve Functionality Issues for Non Bidders ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Currently it doesn't work if some student does not bid. In this case, algorithm needs to be fixed to ignore anyone who didn’t bid, and let them choose from what’s left over.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to do several things within this controller. We will set a default state for non-bidders that we can point to to give them options to select topics to review from the leftovers. We will consolidate &amp;lt;code&amp;gt;@sign_up_topics&amp;lt;/code&amp;gt; to resolve the DRY violation. Lastly, we will remove business logic from the show method and push it to a new helper to conform to the SRP.&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # Additional method&lt;br /&gt;
&lt;br /&gt;
  # Provides variables for review bidding page&lt;br /&gt;
  def show&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id].to_i)&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @sign_up_topics = SignUpTopic.where(assignment_id: @assignment.id, private_to: nil)&lt;br /&gt;
    my_topic = SignedUpTeam.topic_id(@participant.parent_id, @participant.user_id)&lt;br /&gt;
    @sign_up_topics -= SignUpTopic.where(assignment_id: @assignment.id, id: my_topic)&lt;br /&gt;
    @num_participants = AssignmentParticipant.where(parent_id: @assignment.id).count&lt;br /&gt;
    @selected_topics = nil # This is used to list the topics assigned to review (i.e., select == assigned)&lt;br /&gt;
    @bids = ReviewBid.where(participant_id: @participant, assignment_id: @assignment.id)&lt;br /&gt;
    signed_up_topics = []&lt;br /&gt;
    &lt;br /&gt;
    @bids.each do |bid|&lt;br /&gt;
      sign_up_topic = SignUpTopic.find_by(id: bid.signuptopic_id)&lt;br /&gt;
      signed_up_topics &amp;lt;&amp;lt; sign_up_topic if sign_up_topic&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    signed_up_topics &amp;amp;= @sign_up_topics&lt;br /&gt;
    @sign_up_topics -= signed_up_topics&lt;br /&gt;
    @bids = signed_up_topics&lt;br /&gt;
    @num_of_topics = @sign_up_topics.size&lt;br /&gt;
    @assigned_review_maps = []&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: @assignment.id, reviewer_id: @participant.id).each do |review_map|&lt;br /&gt;
      @assigned_review_maps &amp;lt;&amp;lt; review_map&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Explicitly render view since it's in the sign up sheet views&lt;br /&gt;
    render 'sign_up_sheet/review_bids_show'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Additional methods&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-2.-reviewbidshelper-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
 # Assigns bidding topics to reviewers&lt;br /&gt;
  def assign_bidding&lt;br /&gt;
      assignment = validate_assignment(params[:assignment_id])&lt;br /&gt;
&lt;br /&gt;
      reviewers = validate_reviewers(assignment.id)&lt;br /&gt;
      reviewer_ids = reviewers.map(&amp;amp;:id)&lt;br /&gt;
&lt;br /&gt;
      matched_topics = run_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
      if matched_topics.blank?&lt;br /&gt;
        flash[:alert] = 'Topic or assignment is missing'&lt;br /&gt;
        redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      leftover_topics = find_leftover_topics(assignment.id, matched_topics)&lt;br /&gt;
      assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.new.assign_review_topics(matched_topics)&lt;br /&gt;
      assignment.update!(can_choose_topic_to_review: false)&lt;br /&gt;
&lt;br /&gt;
      flash[:notice] = 'Reviewers were successfully assigned to topics.'&lt;br /&gt;
      redirect_back fallback_location: root_path&lt;br /&gt;
    rescue ArgumentError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ArgumentError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: e.message&lt;br /&gt;
    rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::RecordInvalid: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue ActiveRecord::ActiveRecordError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::ActiveRecordError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue StandardError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;StandardError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers. Please try again later.'&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  #service methods here ...&lt;br /&gt;
&lt;br /&gt;
  def find_leftover_topics(assignment_id, matched_topics)&lt;br /&gt;
    all_topic_ids = SignUpTopic.where(assignment_id: assignment_id).pluck(:id)&lt;br /&gt;
    assigned_topic_ids = matched_topics.map { |match| match[:topic_id] }&lt;br /&gt;
&lt;br /&gt;
    # Calculate leftover topics&lt;br /&gt;
    all_topic_ids - assigned_topic_ids&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
    return if leftover_topics.blank?&lt;br /&gt;
&lt;br /&gt;
    # Find non-bidders by excluding already matched reviewers&lt;br /&gt;
    non_bidders = reviewer_ids - matched_topics.keys&lt;br /&gt;
&lt;br /&gt;
    # Assign leftover topics to non-bidders in a round-robin fashion&lt;br /&gt;
    non_bidders.each_with_index do |reviewer_id, index|&lt;br /&gt;
      topic_id = leftover_topics[index % leftover_topics.length]&lt;br /&gt;
      ReviewBid.create(priority: 1, signuptopic_id: topic_id, participant_id: reviewer_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirm Functionality for Single-person Teams ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Make sure your code works on individual assignments, i.e., assignments where participants sign up for topics instead of teams. In this case, a team is created for each participant when they sign up. So the code should work for assignments to which either individuals or teams submit.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to make an update to focus on topic rather than team or have it where teams are updated based on topic id.We will probably utilize sign up topic or a similar object to accomplish this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-5-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bid Status Messaging ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| To make the functionality more intuitive, include a message to say how many students are eligible to submit bids, how many have submitted their bids, when the deadline for submitting bids is.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
The logic for these messages will go into the helper file. It is effectively the same reasoning as our migration of other business/functionality code from the controller to the helper to conform to the SRP. It will go hand in hand with the SignUpTopic portion of the code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-6-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Why are the methods in review_bid.rb class methods? Can we change them to instance methods or move it to helpers?&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-1.-reviewbid-model-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model Updates ====&lt;br /&gt;
Since the current class methods are tied to &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model instances, we will change them to instance methods. There is no need to move them to helpers.&lt;br /&gt;
&lt;br /&gt;
===== Review Bid Model Flow Before Refactoring =====&lt;br /&gt;
This sequence diagram describes how the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; interacts with the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model in assigning review topics to reviewers according to their bids. Once the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; action is initiated by the user, the controller gathers the IDs of reviewers and calls &amp;lt;code&amp;gt;ReviewBid.bidding_data&amp;lt;/code&amp;gt; to gather bidding information. That is sent out to an external bidding algorithm, and the matched topics that come back are used by &amp;lt;code&amp;gt;ReviewBid.assign_review_topics&amp;lt;/code&amp;gt; to create &amp;lt;code&amp;gt;ReviewResponseMap&amp;lt;/code&amp;gt; entries, assigning topics to reviewers for the assignment.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
  belongs_to :assignment, class_name: 'Assignment'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  def self.bidding_data(assignment_id, reviewer_ids)&lt;br /&gt;
    # create basic hash and set basic hash data&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'users' =&amp;gt; {}, 'max_accepted_proposals' =&amp;gt; [] }&lt;br /&gt;
    bidding_data['tid'] = SignUpTopic.where(assignment_id: assignment_id).ids&lt;br /&gt;
    bidding_data['max_accepted_proposals'] = Assignment.where(id: assignment_id).pluck(:num_reviews_allowed).first&lt;br /&gt;
&lt;br /&gt;
    # loop through reviewer_ids to get reviewer specific bidding data&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      bidding_data['users'][reviewer_id] = reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # assigns topics to reviews as matched by the webservice algorithm&lt;br /&gt;
  def self.assign_review_topics(assignment_id, reviewer_ids, matched_topics, _min_num_reviews = 2)&lt;br /&gt;
    # if review response map already created, delete it&lt;br /&gt;
    if ReviewResponseMap.where(reviewed_object_id: assignment_id)&lt;br /&gt;
      ReviewResponseMap.where(reviewed_object_id: assignment_id).destroy_all&lt;br /&gt;
    end&lt;br /&gt;
    # loop through reviewer_ids to assign reviews to each reviewer&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      topics_to_assign = matched_topics[reviewer_id.to_s]&lt;br /&gt;
      topics_to_assign.each do |topic|&lt;br /&gt;
        assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method to assign a single topic to a reviewer&lt;br /&gt;
  def self.assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic).pluck(:team_id).first&lt;br /&gt;
    team_to_review.nil? ? [] : ReviewResponseMap.create(reviewed_object_id: assignment_id, reviewer_id: reviewer_id, reviewee_id: team_to_review, type: 'ReviewResponseMap')&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method for getting individual reviewer_ids bidding data&lt;br /&gt;
  # returns user's bidding data hash&lt;br /&gt;
  def self.reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    reviewer_user_id = AssignmentParticipant.find(reviewer_id).user_id&lt;br /&gt;
    self_topic = SignedUpTeam.topic_id(assignment_id, reviewer_user_id)&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'otid' =&amp;gt; self_topic, 'priority' =&amp;gt; [], 'time' =&amp;gt; [] }&lt;br /&gt;
    bids = ReviewBid.where(participant_id: reviewer_id)&lt;br /&gt;
&lt;br /&gt;
    # loop through each bid for a topic to get specific data&lt;br /&gt;
    bids.each do |bid|&lt;br /&gt;
      bidding_data['tid'] &amp;lt;&amp;lt; bid.signuptopic_id&lt;br /&gt;
      bidding_data['priority'] &amp;lt;&amp;lt; bid.priority&lt;br /&gt;
      bidding_data['time'] &amp;lt;&amp;lt; bid.updated_at&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  # Instance method to get bidding data for this review bid&lt;br /&gt;
  def bidding_data&lt;br /&gt;
    {&lt;br /&gt;
      'tid' =&amp;gt; topic_ids,&lt;br /&gt;
      'otid' =&amp;gt; self_topic,&lt;br /&gt;
      'priority' =&amp;gt; priorities,&lt;br /&gt;
      'time' =&amp;gt; updated_times&lt;br /&gt;
    }&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # bid object to limit db touches for similar data acquisitions&lt;br /&gt;
  def bids&lt;br /&gt;
    @bids ||= ReviewBid.where(participant_id: participant_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def topic_ids&lt;br /&gt;
    bids.pluck(:signuptopic_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def priorities&lt;br /&gt;
    bids.pluck(:priority)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def updated_times&lt;br /&gt;
    bids.pluck(:updated_at)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self_topic&lt;br /&gt;
    return nil unless topic &amp;amp;&amp;amp; topic.assignment_id&lt;br /&gt;
&lt;br /&gt;
    SignedUpTeam.topic_id(topic.assignment_id, participant.user_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign topics to reviews&lt;br /&gt;
  def assign_review_topics(matched_topics)&lt;br /&gt;
    validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: topic.assignment_id).destroy_all&lt;br /&gt;
&lt;br /&gt;
    matched_topics.each do |reviewer_id, topics|&lt;br /&gt;
      Array(topics).each { |topic_id| assign_topic_to_reviewer(reviewer_id, topic_id) }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign a single topic to a reviewer&lt;br /&gt;
  def assign_topic_to_reviewer(reviewer_id, topic_id)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic_id).pluck(:team_id).first&lt;br /&gt;
    return if team_to_review.nil?&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.create(&lt;br /&gt;
      reviewed_object_id: topic.assignment_id,&lt;br /&gt;
      reviewer_id: reviewer_id,&lt;br /&gt;
      reviewee_id: team_to_review,&lt;br /&gt;
      type: 'ReviewResponseMap'&lt;br /&gt;
    )&lt;br /&gt;
    # error handling&lt;br /&gt;
  rescue StandardError =&amp;gt; e&lt;br /&gt;
    Rails.logger.error(&amp;quot;Failed to assign topic #{topic_id} to reviewer #{reviewer_id}: #{e.message}&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
    raise ArgumentError, 'Topic or assignment is missing' if topic.nil? || topic.assignment_id.nil?&lt;br /&gt;
    raise ArgumentError, 'Matched topics must be a Hash' unless matched_topics.is_a?(Hash)&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
Once we changed them to instance methods, we modified their use in the controller code to instantiate the object first.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-3.-schema.db-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;Schema.db&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We also need to remove the foreign key reference to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model since the &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model already has access to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;, and that is the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; we should be referencing. This will also involve modifying the database to remove the foreign key reference to the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Steps required:&lt;br /&gt;
&lt;br /&gt;
# Review the records in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table in the database to see if there are any records and if those records have values for &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;&lt;br /&gt;
# Create a migration file using the command: &amp;lt;code&amp;gt;rails generate migration RemoveAssignmentFromReviewBids&amp;lt;/code&amp;gt;&lt;br /&gt;
# In the generated file, indicate the change that needs to be made using the change method and migration columns &amp;lt;code&amp;gt;remove_foreign_key&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;remove_column&amp;lt;/code&amp;gt;. Migration should be straightforward since there is no cascading behavior (i.e., &amp;lt;code&amp;gt;dependent:destroy&amp;lt;/code&amp;gt;) to worry about.&lt;br /&gt;
# Once comfortable with the updates to the generated file, run the migration using the &amp;lt;code&amp;gt;rails db:migrate&amp;lt;/code&amp;gt; command.&lt;br /&gt;
# Run a query in the DB to ensure no orphaned records are in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table after the updates.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-4.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implement Edge Case Testing ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| In the previous implementation wiki, there are edge cases which are not exhaustively tested. Should test those edge cases thoroughly and add more edge case testing - link&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span class=&amp;quot;mark&amp;quot;&amp;gt;TODO&amp;lt;/span&amp;gt;:We just got initial test results back. We still need to figure out why tests are failing and what may be slipping through the cracks.[[File:Test expertiza.png|411x459px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-8-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design Patterns and Principles ===&lt;br /&gt;
Below are the design patterns and principles we plan to use to address the problem statements outlined in the project requirements:&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
** Controller Responsibility&lt;br /&gt;
*** To address the issue of authentication logic residing in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, we will refactor the code to ensure the controller solely handles request and response flows.&lt;br /&gt;
*** Authentication logic will be moved to dedicated methods in &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt;, making the codebase easier to maintain and test.&lt;br /&gt;
** View Simplification&lt;br /&gt;
*** Currently, the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; view contains excessive business logic, violating SRP.&lt;br /&gt;
*** We will move this logic into helper methods or models, ensuring each component has a single responsibility and improving code readability.&lt;br /&gt;
&lt;br /&gt;
* Separation of Concerns (SoC)&lt;br /&gt;
** By relocating authorization logic out of the controller, we separate authentication from request handling.&lt;br /&gt;
** Adhering to SoC enhances readability and maintainability by allowing each part of the application to focus on its specific role.&lt;br /&gt;
* Don't Repeat Yourself (DRY)&lt;br /&gt;
** We will inspect the existing implementation for code duplication, particularly in the controller actions.&lt;br /&gt;
** Any repetitive code will be refactored or removed to reduce redundancy and improve clarity.&lt;br /&gt;
&lt;br /&gt;
* Model-View-Controller (MVC) Adherence&lt;br /&gt;
** Moving business logic from the view aligns with MVC best practices.&lt;br /&gt;
** By ensuring the view only presents data without processing it, we maintain clear boundaries between the model, view, and controller layers.&lt;br /&gt;
&lt;br /&gt;
* Dependency Inversion Principle (DIP)&lt;br /&gt;
** We plan to update class methods to instance methods, allowing the controller to depend on abstractions rather than concrete implementations.&lt;br /&gt;
** This change leverages Ruby's duck typing, providing flexibility and making the code easier to maintain and test.&lt;br /&gt;
&lt;br /&gt;
** Clear and intentional naming during refactoring will enhance code clarity and maintainability.&lt;br /&gt;
&lt;br /&gt;
== Files Modified/Added ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-1.-controllers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
review_bids_controller.rb&lt;br /&gt;
&lt;br /&gt;
signup_sheet_controller.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-2.-models&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
review_bid.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-3.-helpers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
&lt;br /&gt;
authorization_helper.rb&lt;br /&gt;
&lt;br /&gt;
Review_bids_helper.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;viii.-test-plan&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
The Excel spreadsheet below details our test plan for testing the code we changed or created to ensure code coverage. The process involved reviewing the code and determining all the possible scenarios that needed to be covered based on what the method was doing, then creating test cases accordingly.&lt;br /&gt;
&lt;br /&gt;
* [https://docs.google.com/spreadsheets/d/1iHzOdSwlTtxOtf5VsNdwOaCP8NzYMmiJ/edit?usp=drive_link Open Test Matrix Spreadsheet]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/review_bids_controller_spec.rb ReviewBidsController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/sign_up_sheet_controller_spec.rb SignUpSheetController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/models/review_bid_spec.rb ReviewBid model updated test cases]&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-1.-mentor&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Ed Gehringer&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-2.-members&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Members ===&lt;br /&gt;
&lt;br /&gt;
* Gavin Teague&lt;br /&gt;
* Janice Uwujaren&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xi.-links&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
Previous Wiki: [https://wiki.expertiza.ncsu.edu/index.php/CSC/ECE_517_Fall_2021_-_E2151._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;Link&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xii.-references&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/document/d/1LHVacylQmB15I6VcYUA5nuC7GRRU9szHzZhjlPcE-PA/edit?tab=t.0#heading=h.u0oycoj6e0ev &amp;lt;u&amp;gt;Project Instructions&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;appendix-a-uml-class-diagram-plantuml-code-for-review-bids&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Juwujar</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=161045</id>
		<title>CSC/ECE 517 Fall 2024 - E2485. Allow reviewers to bid on what to review</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=161045"/>
		<updated>2024-12-12T05:48:24Z</updated>

		<summary type="html">&lt;p&gt;Juwujar: /* Client Side Changes to Support ReviewBidsController#set_priority Changes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
This project will fix the defects and improve the functionality in Expertiza of the Reviewer Bidding feature. It will make the functionality smoother, more reliable, and align with Expertiza's collaborative learning objectives. &lt;br /&gt;
&lt;br /&gt;
The Review Bidding feature lets reviewers focus on topics they know best or care about most. If someone feels exceptionally qualified or interested in a topic, they can bid to review submissions in that area. This helps ensure reviews are insightful and enjoyable for the reviewer while benefiting the other participants and the overall process.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iii.-problem-statements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Problem Statements ==&lt;br /&gt;
&lt;br /&gt;
Assigning reviews to users on Expertiza involves a complex bidding algorithm that requires optimization before production deployment. Below are key areas we plan to address as per the project requirements:&lt;br /&gt;
* Code Refactoring and Best Practices&lt;br /&gt;
** '''Utilize Authentication Utilities''': Modify the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; function in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to use existing authentication utilities, enhancing code reusability and adhering to the Single Responsibility Principle (SRP) and Separation of Concerns (SoC).&lt;br /&gt;
** '''Eliminate Code Duplication''': Refactor &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; to comply with the Don't Repeat Yourself (DRY) principle.&lt;br /&gt;
** '''Rename Functions for Clarity''': Change &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; to better reflect its purpose.&lt;br /&gt;
** '''Refactor Methods in '''&amp;lt;code&amp;gt;review_bid.rb&amp;lt;/code&amp;gt;''':''': Convert class methods to instance methods or relocate them to helper classes for improved code organization.&lt;br /&gt;
* Functionality Enhancements&lt;br /&gt;
** '''Handle Missing Bids Gracefully''': Adjust the bidding algorithm to accommodate students who do not bid, allowing them to select from the remaining options.&lt;br /&gt;
** '''Support Both Individual and Team Assignments''': Ensure the code functions correctly for participant and team-based assignments when signing up for topics.&lt;br /&gt;
* User Interface Improvements&lt;br /&gt;
** '''Increase Transparency in the Bidding Process''': Display messages indicating how many students are eligible to bid, how many have submitted bids, and the deadline.&lt;br /&gt;
** '''Accurate Bid Status Indicators''': Ensure topic colors change correctly based on the number of outstanding bids, providing clear visual feedback to users.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Exhaustive Edge Case Testing''': Conduct thorough testing for edge cases not previously covered, adding additional test cases as needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iv.-design-goals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Design Goals ==&lt;br /&gt;
&lt;br /&gt;
While addressing the existing defects and enhancing the Reviewer Bidding feature, we plan to adhere to the following design guidelines:&lt;br /&gt;
&lt;br /&gt;
* Code Quality and Maintainability&lt;br /&gt;
** '''Improve Code Structure: '''Implement refactoring strategies to enhance code readability and maintainability, ensuring adherence to best practices like the DRY principle.&lt;br /&gt;
** '''Standardize Authentication Logic: '''Utilize a centralized authentication utility to promote consistency and reusability across the application.&lt;br /&gt;
* System Stability and Flexibility&lt;br /&gt;
** '''Enhance Algorithm Robustness: '''Modify the bidding algorithm to handle scenarios where students do not bid, ensuring the system remains stable under all conditions.&lt;br /&gt;
** '''Ensure Assignment Versatility: '''Design the system to seamlessly support both individual and team assignments without additional configuration.&lt;br /&gt;
* User Experience Enhancements&lt;br /&gt;
** '''Increase Bidding Transparency: '''Develop user interface elements that clearly display bidding status, deadlines, and participation metrics.&lt;br /&gt;
** '''Implement Dynamic Visual Feedback: '''Use real-time visual cues, such as color changes, to inform users about the bidding status instantly.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Develop Comprehensive Test Suites: '''Create extensive automated tests covering edge cases and critical functionalities to guarantee reliable system behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;v.-uml-diagram&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== UML Diagram ==&lt;br /&gt;
&lt;br /&gt;
Below is a visual representation of the models associated with the review bids functionality, depicting the data structures and their relationships.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml class diagram2.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi.-implementation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
'''ReviewBid: '''The &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; manages the bidding process, where participants indicate their preferences for reviewing different topics or assignments. This helps distribute review tasks fairly based on participants' interests.&lt;br /&gt;
&lt;br /&gt;
'''Assignment: '''The &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model represents team or individual assignments in Expertiza. It stores all the essential information required to keep the assignment process organized, like managing participants, setting due dates, forming teams, and overseeing the review process.&lt;br /&gt;
&lt;br /&gt;
'''SignUpTopic: '''The &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt; model identifies specific topics or areas within an assignment for which participants can sign up. It manages the availability of these topics, tracks bids, coordinates team sign-ups, and assigns topics based on both bids and participant availability. &lt;br /&gt;
&lt;br /&gt;
'''Participant: '''The &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model is about the individual users who participate in an assignment. This includes students, instructors, teaching assistants, or other relevant roles. This model is responsible for managing key user information, monitoring interactions with the assignment, keeping track of team memberships, and recording things like badges and review grades.&lt;br /&gt;
&lt;br /&gt;
== Implementation ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1.-decoupling-authorization-logic-from-reviewbidscontroller&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Decoupling Authorization Logic from &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| '''&amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; needs to use the authentication utilities'''&lt;br /&gt;
&lt;br /&gt;
The function &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; should use the available authentication utilities to support the Single Responsibility Principle and Separation of Concerns and to support code reusability.&lt;br /&gt;
&lt;br /&gt;
| '''Authentication Consistency:''' Remove authentication logic in the controller and call logic in the authentication utility class instead to ensure consistent use of the same logic across the application, foster reusability, and eliminate DRY.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
==== Authorization Flow Before Refactoring ====&lt;br /&gt;
The sequence diagram depicts the authorization process within the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;. When a user invokes an action, the controller checks if the action is allowed by verifying the user's role through the &amp;lt;code&amp;gt;current_role_name()&amp;lt;/code&amp;gt; method in the &amp;lt;code&amp;gt;ApplicationController&amp;lt;/code&amp;gt;. Depending on the action and the user's role, the controller either authorizes the user to proceed or denies access, ultimately executing the action logic if authorized and responding to the user.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 1.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We simplified the &amp;lt;code&amp;gt;action_allowed?&amp;lt;/code&amp;gt; method by decoupling authorization logic from the controller to use helper methods in the &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt; module. The &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; should not handle authentication, which is a violation of SRP. &lt;br /&gt;
&lt;br /&gt;
Note: We also made similar updates to the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; methods for &amp;lt;code&amp;gt;SignUpController&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;vertical-align:top;&amp;quot; | Original Code !! style=&amp;quot;vertical-align:top;&amp;quot; | Refactored Code&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index'&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator',&lt;br /&gt;
     'Student'].include? current_role_name and&lt;br /&gt;
        ((%w[list].include? action_name) ? are_needed_authorizations_present?(params[:id], &amp;quot;participant&amp;quot;, &amp;quot;reader&amp;quot;, &amp;quot;submitter&amp;quot;, &amp;quot;reviewer&amp;quot;) : true)&lt;br /&gt;
  else&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator'].include? current_role_name&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index', 'list'&lt;br /&gt;
    current_user_has_student_privileges? &amp;amp;&amp;amp; list_authorization_check&lt;br /&gt;
  else&lt;br /&gt;
    current_user_has_ta_privileges?&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; action ===&lt;br /&gt;
Previously, the &amp;lt;code&amp;gt;ReviewBidsController's index&amp;lt;/code&amp;gt; action was overloaded with responsibilities—it handled everything from locating and validating assignments to fetching review mappings and counting completed reviews. This all-in-one approach made the controller action messy. &lt;br /&gt;
&lt;br /&gt;
We have moved the business logic from the controller action and into dedicated service objects. With this change, we introduced two key service objects: &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; focuses on retrieving the participant along with their assignments, while the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; takes on the task of managing review mappings and calculating review-related stats, like how many reviews have been completed. &lt;br /&gt;
&lt;br /&gt;
This separation of responsibilities aligns with the Single Responsibility Principle. The logic for fetching assignments, retrieving review mappings, and counting reviews is now more flexible and reusable throughout the application. For example, we used methods in the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; in both the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; and the &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;, helping to avoid code duplication.The article https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial served as a guide on when and how to use service objects in Rails.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
  return unless current_user_id?(@participant.user_id)&lt;br /&gt;
  @assignment = @participant.assignment&lt;br /&gt;
  @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
  # Finding how many reviews have been completed&lt;br /&gt;
  @num_reviews_completed = 0&lt;br /&gt;
  @review_mappings.each do |map|&lt;br /&gt;
    @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # render view for completing reviews after review bidding has been completed&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @assignment = participant_service.assignment&lt;br /&gt;
  unless @assignment.is_a?(Assignment)&lt;br /&gt;
    flash[:error] = 'Assignment not found.'&lt;br /&gt;
    redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  @review_mappings = review_service.review_mappings&lt;br /&gt;
  @num_reviews_completed = review_service.review_counts[:completed]&lt;br /&gt;
&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Optimize &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Action functionality ===&lt;br /&gt;
In the earlier version of the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, the server only needed a couple of key pieces of information: an array of topic IDs (params[:topic]) and the participant ID (params[:id}). From there, it would figure out the assignment_id by using the first topic as a reference. This is risky because the &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt; associated with the topic may not be a valid &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
With the new implementation, we structured the parameters to include the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt;, an array of topic IDs, and &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;. This change ensures that the server always receives complete and reliable information, preventing issues caused by missing or inconsistent data. It also removes the need for the server to perform unnecessary lookups, as in the case of &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;, allowing the controller to focus on receiving requests and rendering appropriate responses.&lt;br /&gt;
&lt;br /&gt;
While these updates have cleaned up the set_priority action, they still contain a lot of business logic, like validations and priority updates. To make the code even more maintainable, there’s room for further refactoring—perhaps moving some of this logic into a service class or model method to streamline the controller even more.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | Old Code&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    if params[:topic].nil?&lt;br /&gt;
      ReviewBid.where(participant_id: params[:id]).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      participant = AssignmentParticipant.find_by(id: params[:id])&lt;br /&gt;
      assignment_id = SignUpTopic.find(params[:topic].first).assignment.id&lt;br /&gt;
      @bids = ReviewBid.where(participant_id: params[:id])&lt;br /&gt;
      signed_up_topics = ReviewBid.where(participant_id: params[:id]).map(&amp;amp;:signuptopic_id)&lt;br /&gt;
      signed_up_topics -= params[:topic].map(&amp;amp;:to_i)&lt;br /&gt;
      signed_up_topics.each do |topic|&lt;br /&gt;
        ReviewBid.where(signuptopic_id: topic, participant_id: params[:id]).destroy_all&lt;br /&gt;
      end&lt;br /&gt;
      params[:topic].each_with_index do |topic_id, index|&lt;br /&gt;
        bid_existence = ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id])&lt;br /&gt;
        if bid_existence.empty?&lt;br /&gt;
          ReviewBid.create(priority: index + 1, signuptopic_id: topic_id, participant_id: params[:id], assignment_id: assignment_id)&lt;br /&gt;
        else&lt;br /&gt;
          ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id]).update_all(priority: index + 1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to action: 'show', assignment_id: params[:assignment_id], id: params[:id]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    participant_id = params[:participant_id].to_i&lt;br /&gt;
    selected_topic_ids = params[:topic]&amp;amp;.map(&amp;amp;:to_i) || []&lt;br /&gt;
    assignment_id = params[:assignment_id].to_i&lt;br /&gt;
&lt;br /&gt;
    unless current_user_can_modify_participant?(participant_id)&lt;br /&gt;
      flash[:error] = 'You are not authorized to modify these bids.'&lt;br /&gt;
      respond_to do |format|&lt;br /&gt;
        format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
        format.json { render json: { status: 'unauthorized' }, status: :unauthorized }&lt;br /&gt;
      end&lt;br /&gt;
      return&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    if selected_topic_ids.empty?&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      assignment = Assignment.find_by(id: assignment_id)&lt;br /&gt;
      if assignment.nil?&lt;br /&gt;
        flash[:error] = &amp;quot;Invalid assignment.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_assignment' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      unless SignUpTopic.where(id: selected_topic_ids, assignment_id: assignment_id).count == selected_topic_ids.size&lt;br /&gt;
        flash[:error] = &amp;quot;One or more selected topics are invalid.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_topics' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).where.not(signuptopic_id: selected_topic_ids).destroy_all&lt;br /&gt;
&lt;br /&gt;
      selected_topic_ids.each_with_index do |topic_id, index|&lt;br /&gt;
        bid = ReviewBid.find_or_initialize_by(signuptopic_id: topic_id, participant_id: participant_id)&lt;br /&gt;
        bid.priority = index + 1&lt;br /&gt;
        bid.assignment_id = assignment_id&lt;br /&gt;
        bid.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_to action: 'show', assignment_id: assignment_id, id: participant_id, notice: 'Review bids updated successfully.' }&lt;br /&gt;
      format.json { render json: { status: 'success' }, status: :ok }&lt;br /&gt;
    end&lt;br /&gt;
  rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
    flash[:error] = &amp;quot;Failed to update priorities: #{e.message}&amp;quot;&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
      format.json { render json: { status: 'error', message: e.message }, status: :unprocessable_entity }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Client Side Changes to Support &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Changes ====&lt;br /&gt;
After tidying up the server-side logic, we needed to refactor the client-side code to ensure it passed the correct information from the view to the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action. On the client side, we now grab the topicIDs and send them as &amp;lt;code&amp;gt;selected_topic_ids&amp;lt;/code&amp;gt;, allowing the controller to figure out which topics were selected and in what order.  &lt;br /&gt;
&lt;br /&gt;
We also extracted the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, making it a lot easier to send data with AJAX and keeping things flexible. When working with jQuery, AJAX is awesome because it lets us update the server without reloading the page, making the user experience smooth and responsive. When we pull the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, we can grab it along with other data, like the list of topics, and include it as a single request. This way, we don’t have to rely on the URL to know which participant we’re working with, which makes the code cleaner.&lt;br /&gt;
&lt;br /&gt;
We also added an update handler so that any time the order of the selected topic changes in the view, information is sent to the appropriate controller action without reloading the page. This ensures that the controller and the view remain in sync, reflecting updates in real time, and includes a mechanism for providing feedback if something goes wrong.&lt;br /&gt;
&lt;br /&gt;
Note: One interesting aspect of the application was the mix of JavaScript and CoffeeScript code. It wasn't clear why both types of code were used.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery -&amp;gt;&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: -&amp;gt;&lt;br /&gt;
      $.post($(this).data('update-url'), $(this).sortable('serialize'))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery(document).ready ($) -&amp;gt;&lt;br /&gt;
  sendUpdatedOrder = -&amp;gt;&lt;br /&gt;
    sortedIDs = $(this).sortable('toArray')&lt;br /&gt;
&lt;br /&gt;
    topicIDs = sortedIDs.map (id) -&amp;gt;&lt;br /&gt;
      id.replace 'topic_', ''&lt;br /&gt;
&lt;br /&gt;
    participant_id = $(this).data('participant-id')&lt;br /&gt;
&lt;br /&gt;
    assignment_id = new URL($(this).data('update-url'), window.location.origin).searchParams.get('assignment_id')&lt;br /&gt;
&lt;br /&gt;
    $.ajax&lt;br /&gt;
      type: 'POST'&lt;br /&gt;
      url: $(this).data('update-url')&lt;br /&gt;
      data:&lt;br /&gt;
        topic: topicIDs&lt;br /&gt;
        participant_id: participant_id&lt;br /&gt;
        assignment_id: assignment_id&lt;br /&gt;
      success: (response) -&amp;gt;&lt;br /&gt;
        console.log 'Selections updated successfully.'&lt;br /&gt;
      error: (xhr, status, error) -&amp;gt;&lt;br /&gt;
        alert 'An error occurred while saving your selections. Please try again.'&lt;br /&gt;
        console.error error&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: sendUpdatedOrder&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-1.-signupsheetcontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; Updates ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; method renders the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; action in &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt;. However, the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; does not include the controller action for this view. We need to find the old implementation of the controller action, add it back, and review and update it for any DRY violations.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # provides variables for reviewing page located at views/review_bids/others_work.html.erb&lt;br /&gt;
  def index&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
    return unless current_user_id?(@participant.user_id)&lt;br /&gt;
&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
    # Finding how many reviews have been completed&lt;br /&gt;
    @num_reviews_completed = 0&lt;br /&gt;
    @review_mappings.each do |map|&lt;br /&gt;
      @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # render view for completing reviews after review bidding has been completed&lt;br /&gt;
    render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
  end  &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-2.-reviewbidsotherswork-view-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;review_bids_others_work.html.erb&amp;lt;/code&amp;gt; View Updates ====&lt;br /&gt;
&lt;br /&gt;
The code only contains a view with this name and no associated controller action. Reviewing the code in the view, we noticed some SRP violations where there is much logic in the presentation layer. Set up helper methods in the controller to handle some of that logic or see if the model can handle some of that logic.&lt;br /&gt;
&lt;br /&gt;
Note: The view is technically part of the SignUpSheetController, but it's being rendered by the ReviewBidsController, which initially threw us off. After chatting with the professor, we decided not to change this view. He indicated that the plan is to reuse these views for both assignment bidding and review bidding.&lt;br /&gt;
&lt;br /&gt;
We would suggest building the assignment bidding and review bidding features separately first to make sure each one works on its own. Once that’s done, we can go back and clean things up by combining any shared functionality. This could include creating shared views or even introducing an abstract model to handle both types of bidding in a cleaner way. It could also mean replacing the ReviewBidsController with something more generic, like a BidsController, to keep things reusable and easier to maintain down the line.&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Rename &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; method ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; should be &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt;&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:''' Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The primary activity here is to refactor the method name &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; and fix the hardcoded URL. However, the match topic bidding algorithm no longer works since Heroku is no longer free, per the professor. The reference link below has detailed information about the web service that we can leverage to gain that understanding.&lt;br /&gt;
&lt;br /&gt;
Reference Link for Webservice Details: [https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Updated Implementation ====&lt;br /&gt;
The original requirement was to rename the run_bidding_algorithm method to assign_reviewers, but after digging into the code, we realized that renaming it didn’t make sense. The method runs the bidding algorithm to match topics, and there’s already a separate method to handle assigning review topics.&lt;br /&gt;
&lt;br /&gt;
However, we improved the logic in the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; method (which calls the private &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt;) by introducing a separate &amp;lt;code&amp;gt;BiddingAlgorithmService&amp;lt;/code&amp;gt; object. This keeps the business logic out of the controller action, which makes things cleaner and more maintainable.&lt;br /&gt;
&lt;br /&gt;
==== review_bids_controller ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def run_bidding_algorithm&lt;br /&gt;
    review_bid = ReviewBid.new&lt;br /&gt;
    bidding_data = review_bid.bidding_data&lt;br /&gt;
    matched_topics = BiddingAlgorithmService.new(bidding_data).run&lt;br /&gt;
    raise ArgumentError, 'Failed to assign reviewers. Please try again later.' unless matched_topics&lt;br /&gt;
&lt;br /&gt;
    matched_topics&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== bidding_algorithm_service ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# frozen_string_literal: true&lt;br /&gt;
&lt;br /&gt;
# The `BiddingAlgorithmService` run the bid assignment algorithm&lt;br /&gt;
# Sends student IDs, topic IDs, student preferences, and timestamps to the web service&lt;br /&gt;
# The web service returns the matched assignments in the JSON response body&lt;br /&gt;
class BiddingAlgorithmService&lt;br /&gt;
  SERVICE_URL = 'http://app-csc517.herokuapp.com/match_topics'.freeze&lt;br /&gt;
&lt;br /&gt;
  # MOCK_DATA is based on the documentation detailing how the webservice behaves.&lt;br /&gt;
  # Reference: https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review#Webservice&lt;br /&gt;
  MOCK_DATA = {&lt;br /&gt;
    36239 =&amp;gt; [3970, 3972, 3975],&lt;br /&gt;
    36240 =&amp;gt; [3973, 3974, 3972],&lt;br /&gt;
    36241 =&amp;gt; [3969, 3971, 3972],&lt;br /&gt;
    36242 =&amp;gt; [3969, 3971, 3973],&lt;br /&gt;
    36243 =&amp;gt; [3969, 3970, 3971]&lt;br /&gt;
  }.freeze&lt;br /&gt;
&lt;br /&gt;
  def initialize(bidding_data)&lt;br /&gt;
    @bidding_data = bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def run&lt;br /&gt;
    return MOCK_DATA if Rails.application.config.use_mock_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
    perform_request&lt;br /&gt;
  rescue RestClient::ExceptionWithResponse, JSON::ParserError&lt;br /&gt;
    false&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def perform_request&lt;br /&gt;
    response = RestClient.post(&lt;br /&gt;
      SERVICE_URL,&lt;br /&gt;
      @bidding_data.to_json,&lt;br /&gt;
      content_type: :json,&lt;br /&gt;
      accept: :json&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    validate_response(response)&lt;br /&gt;
    JSON.parse(response.body)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def validate_response(response)&lt;br /&gt;
    raise 'Invalid response format' unless response.headers[:content_type] &amp;amp;&amp;amp; response.headers[:content_type].include?('application/json')&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Resolve Functionality Issues for Non Bidders ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Currently it doesn't work if some student does not bid. In this case, algorithm needs to be fixed to ignore anyone who didn’t bid, and let them choose from what’s left over.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to do several things within this controller. We will set a default state for non-bidders that we can point to to give them options to select topics to review from the leftovers. We will consolidate &amp;lt;code&amp;gt;@sign_up_topics&amp;lt;/code&amp;gt; to resolve the DRY violation. Lastly, we will remove business logic from the show method and push it to a new helper to conform to the SRP.&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # Additional method&lt;br /&gt;
&lt;br /&gt;
  # Provides variables for review bidding page&lt;br /&gt;
  def show&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id].to_i)&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @sign_up_topics = SignUpTopic.where(assignment_id: @assignment.id, private_to: nil)&lt;br /&gt;
    my_topic = SignedUpTeam.topic_id(@participant.parent_id, @participant.user_id)&lt;br /&gt;
    @sign_up_topics -= SignUpTopic.where(assignment_id: @assignment.id, id: my_topic)&lt;br /&gt;
    @num_participants = AssignmentParticipant.where(parent_id: @assignment.id).count&lt;br /&gt;
    @selected_topics = nil # This is used to list the topics assigned to review (i.e., select == assigned)&lt;br /&gt;
    @bids = ReviewBid.where(participant_id: @participant, assignment_id: @assignment.id)&lt;br /&gt;
    signed_up_topics = []&lt;br /&gt;
    &lt;br /&gt;
    @bids.each do |bid|&lt;br /&gt;
      sign_up_topic = SignUpTopic.find_by(id: bid.signuptopic_id)&lt;br /&gt;
      signed_up_topics &amp;lt;&amp;lt; sign_up_topic if sign_up_topic&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    signed_up_topics &amp;amp;= @sign_up_topics&lt;br /&gt;
    @sign_up_topics -= signed_up_topics&lt;br /&gt;
    @bids = signed_up_topics&lt;br /&gt;
    @num_of_topics = @sign_up_topics.size&lt;br /&gt;
    @assigned_review_maps = []&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: @assignment.id, reviewer_id: @participant.id).each do |review_map|&lt;br /&gt;
      @assigned_review_maps &amp;lt;&amp;lt; review_map&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Explicitly render view since it's in the sign up sheet views&lt;br /&gt;
    render 'sign_up_sheet/review_bids_show'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Additional methods&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-2.-reviewbidshelper-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
 # Assigns bidding topics to reviewers&lt;br /&gt;
  def assign_bidding&lt;br /&gt;
      assignment = validate_assignment(params[:assignment_id])&lt;br /&gt;
&lt;br /&gt;
      reviewers = validate_reviewers(assignment.id)&lt;br /&gt;
      reviewer_ids = reviewers.map(&amp;amp;:id)&lt;br /&gt;
&lt;br /&gt;
      matched_topics = run_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
      if matched_topics.blank?&lt;br /&gt;
        flash[:alert] = 'Topic or assignment is missing'&lt;br /&gt;
        redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      leftover_topics = find_leftover_topics(assignment.id, matched_topics)&lt;br /&gt;
      assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.new.assign_review_topics(matched_topics)&lt;br /&gt;
      assignment.update!(can_choose_topic_to_review: false)&lt;br /&gt;
&lt;br /&gt;
      flash[:notice] = 'Reviewers were successfully assigned to topics.'&lt;br /&gt;
      redirect_back fallback_location: root_path&lt;br /&gt;
    rescue ArgumentError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ArgumentError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: e.message&lt;br /&gt;
    rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::RecordInvalid: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue ActiveRecord::ActiveRecordError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::ActiveRecordError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue StandardError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;StandardError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers. Please try again later.'&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  #service methods here ...&lt;br /&gt;
&lt;br /&gt;
  def find_leftover_topics(assignment_id, matched_topics)&lt;br /&gt;
    all_topic_ids = SignUpTopic.where(assignment_id: assignment_id).pluck(:id)&lt;br /&gt;
    assigned_topic_ids = matched_topics.map { |match| match[:topic_id] }&lt;br /&gt;
&lt;br /&gt;
    # Calculate leftover topics&lt;br /&gt;
    all_topic_ids - assigned_topic_ids&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
    return if leftover_topics.blank?&lt;br /&gt;
&lt;br /&gt;
    # Find non-bidders by excluding already matched reviewers&lt;br /&gt;
    non_bidders = reviewer_ids - matched_topics.keys&lt;br /&gt;
&lt;br /&gt;
    # Assign leftover topics to non-bidders in a round-robin fashion&lt;br /&gt;
    non_bidders.each_with_index do |reviewer_id, index|&lt;br /&gt;
      topic_id = leftover_topics[index % leftover_topics.length]&lt;br /&gt;
      ReviewBid.create(priority: 1, signuptopic_id: topic_id, participant_id: reviewer_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirm Functionality for Single-person Teams ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Make sure your code works on individual assignments, i.e., assignments where participants sign up for topics instead of teams. In this case, a team is created for each participant when they sign up. So the code should work for assignments to which either individuals or teams submit.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to make an update to focus on topic rather than team or have it where teams are updated based on topic id.We will probably utilize sign up topic or a similar object to accomplish this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-5-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bid Status Messaging ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| To make the functionality more intuitive, include a message to say how many students are eligible to submit bids, how many have submitted their bids, when the deadline for submitting bids is.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
The logic for these messages will go into the helper file. It is effectively the same reasoning as our migration of other business/functionality code from the controller to the helper to conform to the SRP. It will go hand in hand with the SignUpTopic portion of the code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-6-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Why are the methods in review_bid.rb class methods? Can we change them to instance methods or move it to helpers?&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-1.-reviewbid-model-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model Updates ====&lt;br /&gt;
Since the current class methods are tied to &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model instances, we will change them to instance methods. There is no need to move them to helpers.&lt;br /&gt;
&lt;br /&gt;
===== Review Bid Model Flow Before Refactoring =====&lt;br /&gt;
This sequence diagram describes how the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; interacts with the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model in assigning review topics to reviewers according to their bids. Once the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; action is initiated by the user, the controller gathers the IDs of reviewers and calls &amp;lt;code&amp;gt;ReviewBid.bidding_data&amp;lt;/code&amp;gt; to gather bidding information. That is sent out to an external bidding algorithm, and the matched topics that come back are used by &amp;lt;code&amp;gt;ReviewBid.assign_review_topics&amp;lt;/code&amp;gt; to create &amp;lt;code&amp;gt;ReviewResponseMap&amp;lt;/code&amp;gt; entries, assigning topics to reviewers for the assignment.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
  belongs_to :assignment, class_name: 'Assignment'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  def self.bidding_data(assignment_id, reviewer_ids)&lt;br /&gt;
    # create basic hash and set basic hash data&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'users' =&amp;gt; {}, 'max_accepted_proposals' =&amp;gt; [] }&lt;br /&gt;
    bidding_data['tid'] = SignUpTopic.where(assignment_id: assignment_id).ids&lt;br /&gt;
    bidding_data['max_accepted_proposals'] = Assignment.where(id: assignment_id).pluck(:num_reviews_allowed).first&lt;br /&gt;
&lt;br /&gt;
    # loop through reviewer_ids to get reviewer specific bidding data&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      bidding_data['users'][reviewer_id] = reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # assigns topics to reviews as matched by the webservice algorithm&lt;br /&gt;
  def self.assign_review_topics(assignment_id, reviewer_ids, matched_topics, _min_num_reviews = 2)&lt;br /&gt;
    # if review response map already created, delete it&lt;br /&gt;
    if ReviewResponseMap.where(reviewed_object_id: assignment_id)&lt;br /&gt;
      ReviewResponseMap.where(reviewed_object_id: assignment_id).destroy_all&lt;br /&gt;
    end&lt;br /&gt;
    # loop through reviewer_ids to assign reviews to each reviewer&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      topics_to_assign = matched_topics[reviewer_id.to_s]&lt;br /&gt;
      topics_to_assign.each do |topic|&lt;br /&gt;
        assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method to assign a single topic to a reviewer&lt;br /&gt;
  def self.assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic).pluck(:team_id).first&lt;br /&gt;
    team_to_review.nil? ? [] : ReviewResponseMap.create(reviewed_object_id: assignment_id, reviewer_id: reviewer_id, reviewee_id: team_to_review, type: 'ReviewResponseMap')&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method for getting individual reviewer_ids bidding data&lt;br /&gt;
  # returns user's bidding data hash&lt;br /&gt;
  def self.reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    reviewer_user_id = AssignmentParticipant.find(reviewer_id).user_id&lt;br /&gt;
    self_topic = SignedUpTeam.topic_id(assignment_id, reviewer_user_id)&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'otid' =&amp;gt; self_topic, 'priority' =&amp;gt; [], 'time' =&amp;gt; [] }&lt;br /&gt;
    bids = ReviewBid.where(participant_id: reviewer_id)&lt;br /&gt;
&lt;br /&gt;
    # loop through each bid for a topic to get specific data&lt;br /&gt;
    bids.each do |bid|&lt;br /&gt;
      bidding_data['tid'] &amp;lt;&amp;lt; bid.signuptopic_id&lt;br /&gt;
      bidding_data['priority'] &amp;lt;&amp;lt; bid.priority&lt;br /&gt;
      bidding_data['time'] &amp;lt;&amp;lt; bid.updated_at&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  # Instance method to get bidding data for this review bid&lt;br /&gt;
  def bidding_data&lt;br /&gt;
    {&lt;br /&gt;
      'tid' =&amp;gt; topic_ids,&lt;br /&gt;
      'otid' =&amp;gt; self_topic,&lt;br /&gt;
      'priority' =&amp;gt; priorities,&lt;br /&gt;
      'time' =&amp;gt; updated_times&lt;br /&gt;
    }&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # bid object to limit db touches for similar data acquisitions&lt;br /&gt;
  def bids&lt;br /&gt;
    @bids ||= ReviewBid.where(participant_id: participant_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def topic_ids&lt;br /&gt;
    bids.pluck(:signuptopic_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def priorities&lt;br /&gt;
    bids.pluck(:priority)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def updated_times&lt;br /&gt;
    bids.pluck(:updated_at)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self_topic&lt;br /&gt;
    return nil unless topic &amp;amp;&amp;amp; topic.assignment_id&lt;br /&gt;
&lt;br /&gt;
    SignedUpTeam.topic_id(topic.assignment_id, participant.user_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign topics to reviews&lt;br /&gt;
  def assign_review_topics(matched_topics)&lt;br /&gt;
    validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: topic.assignment_id).destroy_all&lt;br /&gt;
&lt;br /&gt;
    matched_topics.each do |reviewer_id, topics|&lt;br /&gt;
      Array(topics).each { |topic_id| assign_topic_to_reviewer(reviewer_id, topic_id) }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign a single topic to a reviewer&lt;br /&gt;
  def assign_topic_to_reviewer(reviewer_id, topic_id)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic_id).pluck(:team_id).first&lt;br /&gt;
    return if team_to_review.nil?&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.create(&lt;br /&gt;
      reviewed_object_id: topic.assignment_id,&lt;br /&gt;
      reviewer_id: reviewer_id,&lt;br /&gt;
      reviewee_id: team_to_review,&lt;br /&gt;
      type: 'ReviewResponseMap'&lt;br /&gt;
    )&lt;br /&gt;
    # error handling&lt;br /&gt;
  rescue StandardError =&amp;gt; e&lt;br /&gt;
    Rails.logger.error(&amp;quot;Failed to assign topic #{topic_id} to reviewer #{reviewer_id}: #{e.message}&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
    raise ArgumentError, 'Topic or assignment is missing' if topic.nil? || topic.assignment_id.nil?&lt;br /&gt;
    raise ArgumentError, 'Matched topics must be a Hash' unless matched_topics.is_a?(Hash)&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
Once we changed them to instance methods, we modified their use in the controller code to instantiate the object first.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-3.-schema.db-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;Schema.db&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We also need to remove the foreign key reference to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model since the &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model already has access to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;, and that is the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; we should be referencing. This will also involve modifying the database to remove the foreign key reference to the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Steps required:&lt;br /&gt;
&lt;br /&gt;
# Review the records in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table in the database to see if there are any records and if those records have values for &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;&lt;br /&gt;
# Create a migration file using the command: &amp;lt;code&amp;gt;rails generate migration RemoveAssignmentFromReviewBids&amp;lt;/code&amp;gt;&lt;br /&gt;
# In the generated file, indicate the change that needs to be made using the change method and migration columns &amp;lt;code&amp;gt;remove_foreign_key&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;remove_column&amp;lt;/code&amp;gt;. Migration should be straightforward since there is no cascading behavior (i.e., &amp;lt;code&amp;gt;dependent:destroy&amp;lt;/code&amp;gt;) to worry about.&lt;br /&gt;
# Once comfortable with the updates to the generated file, run the migration using the &amp;lt;code&amp;gt;rails db:migrate&amp;lt;/code&amp;gt; command.&lt;br /&gt;
# Run a query in the DB to ensure no orphaned records are in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table after the updates.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-4.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implement Edge Case Testing ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| In the previous implementation wiki, there are edge cases which are not exhaustively tested. Should test those edge cases thoroughly and add more edge case testing - link&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span class=&amp;quot;mark&amp;quot;&amp;gt;TODO&amp;lt;/span&amp;gt;:We just got initial test results back. We still need to figure out why tests are failing and what may be slipping through the cracks.[[File:Test expertiza.png|411x459px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-8-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design Patterns and Principles ===&lt;br /&gt;
Below are the design patterns and principles we plan to use to address the problem statements outlined in the project requirements:&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
** Controller Responsibility&lt;br /&gt;
*** To address the issue of authentication logic residing in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, we will refactor the code to ensure the controller solely handles request and response flows.&lt;br /&gt;
*** Authentication logic will be moved to dedicated methods in &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt;, making the codebase easier to maintain and test.&lt;br /&gt;
** View Simplification&lt;br /&gt;
*** Currently, the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; view contains excessive business logic, violating SRP.&lt;br /&gt;
*** We will move this logic into helper methods or models, ensuring each component has a single responsibility and improving code readability.&lt;br /&gt;
&lt;br /&gt;
* Separation of Concerns (SoC)&lt;br /&gt;
** By relocating authorization logic out of the controller, we separate authentication from request handling.&lt;br /&gt;
** Adhering to SoC enhances readability and maintainability by allowing each part of the application to focus on its specific role.&lt;br /&gt;
* Don't Repeat Yourself (DRY)&lt;br /&gt;
** We will inspect the existing implementation for code duplication, particularly in the controller actions.&lt;br /&gt;
** Any repetitive code will be refactored or removed to reduce redundancy and improve clarity.&lt;br /&gt;
&lt;br /&gt;
* Model-View-Controller (MVC) Adherence&lt;br /&gt;
** Moving business logic from the view aligns with MVC best practices.&lt;br /&gt;
** By ensuring the view only presents data without processing it, we maintain clear boundaries between the model, view, and controller layers.&lt;br /&gt;
&lt;br /&gt;
* Dependency Inversion Principle (DIP)&lt;br /&gt;
** We plan to update class methods to instance methods, allowing the controller to depend on abstractions rather than concrete implementations.&lt;br /&gt;
** This change leverages Ruby's duck typing, providing flexibility and making the code easier to maintain and test.&lt;br /&gt;
&lt;br /&gt;
** Clear and intentional naming during refactoring will enhance code clarity and maintainability.&lt;br /&gt;
&lt;br /&gt;
== Files Modified/Added ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-1.-controllers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
review_bids_controller.rb&lt;br /&gt;
&lt;br /&gt;
signup_sheet_controller.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-2.-models&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
review_bid.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-3.-helpers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
&lt;br /&gt;
authorization_helper.rb&lt;br /&gt;
&lt;br /&gt;
Review_bids_helper.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;viii.-test-plan&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
The Excel spreadsheet below details our test plan for testing the code we changed or created to ensure code coverage. The process involved reviewing the code and determining all the possible scenarios that needed to be covered based on what the method was doing, then creating test cases accordingly.&lt;br /&gt;
&lt;br /&gt;
* [https://docs.google.com/spreadsheets/d/1iHzOdSwlTtxOtf5VsNdwOaCP8NzYMmiJ/edit?usp=drive_link Open Test Matrix Spreadsheet]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/review_bids_controller_spec.rb ReviewBidsController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/sign_up_sheet_controller_spec.rb SignUpSheetController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/models/review_bid_spec.rb ReviewBid model updated test cases]&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-1.-mentor&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Ed Gehringer&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-2.-members&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Members ===&lt;br /&gt;
&lt;br /&gt;
* Gavin Teague&lt;br /&gt;
* Janice Uwujaren&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xi.-links&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
Previous Wiki: [https://wiki.expertiza.ncsu.edu/index.php/CSC/ECE_517_Fall_2021_-_E2151._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;Link&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xii.-references&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/document/d/1LHVacylQmB15I6VcYUA5nuC7GRRU9szHzZhjlPcE-PA/edit?tab=t.0#heading=h.u0oycoj6e0ev &amp;lt;u&amp;gt;Project Instructions&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;appendix-a-uml-class-diagram-plantuml-code-for-review-bids&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Juwujar</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=161044</id>
		<title>CSC/ECE 517 Fall 2024 - E2485. Allow reviewers to bid on what to review</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=161044"/>
		<updated>2024-12-12T05:40:20Z</updated>

		<summary type="html">&lt;p&gt;Juwujar: /* Client Side Changes to Support ReviewBidsController#set_priority Changes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
This project will fix the defects and improve the functionality in Expertiza of the Reviewer Bidding feature. It will make the functionality smoother, more reliable, and align with Expertiza's collaborative learning objectives. &lt;br /&gt;
&lt;br /&gt;
The Review Bidding feature lets reviewers focus on topics they know best or care about most. If someone feels exceptionally qualified or interested in a topic, they can bid to review submissions in that area. This helps ensure reviews are insightful and enjoyable for the reviewer while benefiting the other participants and the overall process.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iii.-problem-statements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Problem Statements ==&lt;br /&gt;
&lt;br /&gt;
Assigning reviews to users on Expertiza involves a complex bidding algorithm that requires optimization before production deployment. Below are key areas we plan to address as per the project requirements:&lt;br /&gt;
* Code Refactoring and Best Practices&lt;br /&gt;
** '''Utilize Authentication Utilities''': Modify the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; function in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to use existing authentication utilities, enhancing code reusability and adhering to the Single Responsibility Principle (SRP) and Separation of Concerns (SoC).&lt;br /&gt;
** '''Eliminate Code Duplication''': Refactor &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; to comply with the Don't Repeat Yourself (DRY) principle.&lt;br /&gt;
** '''Rename Functions for Clarity''': Change &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; to better reflect its purpose.&lt;br /&gt;
** '''Refactor Methods in '''&amp;lt;code&amp;gt;review_bid.rb&amp;lt;/code&amp;gt;''':''': Convert class methods to instance methods or relocate them to helper classes for improved code organization.&lt;br /&gt;
* Functionality Enhancements&lt;br /&gt;
** '''Handle Missing Bids Gracefully''': Adjust the bidding algorithm to accommodate students who do not bid, allowing them to select from the remaining options.&lt;br /&gt;
** '''Support Both Individual and Team Assignments''': Ensure the code functions correctly for participant and team-based assignments when signing up for topics.&lt;br /&gt;
* User Interface Improvements&lt;br /&gt;
** '''Increase Transparency in the Bidding Process''': Display messages indicating how many students are eligible to bid, how many have submitted bids, and the deadline.&lt;br /&gt;
** '''Accurate Bid Status Indicators''': Ensure topic colors change correctly based on the number of outstanding bids, providing clear visual feedback to users.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Exhaustive Edge Case Testing''': Conduct thorough testing for edge cases not previously covered, adding additional test cases as needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iv.-design-goals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Design Goals ==&lt;br /&gt;
&lt;br /&gt;
While addressing the existing defects and enhancing the Reviewer Bidding feature, we plan to adhere to the following design guidelines:&lt;br /&gt;
&lt;br /&gt;
* Code Quality and Maintainability&lt;br /&gt;
** '''Improve Code Structure: '''Implement refactoring strategies to enhance code readability and maintainability, ensuring adherence to best practices like the DRY principle.&lt;br /&gt;
** '''Standardize Authentication Logic: '''Utilize a centralized authentication utility to promote consistency and reusability across the application.&lt;br /&gt;
* System Stability and Flexibility&lt;br /&gt;
** '''Enhance Algorithm Robustness: '''Modify the bidding algorithm to handle scenarios where students do not bid, ensuring the system remains stable under all conditions.&lt;br /&gt;
** '''Ensure Assignment Versatility: '''Design the system to seamlessly support both individual and team assignments without additional configuration.&lt;br /&gt;
* User Experience Enhancements&lt;br /&gt;
** '''Increase Bidding Transparency: '''Develop user interface elements that clearly display bidding status, deadlines, and participation metrics.&lt;br /&gt;
** '''Implement Dynamic Visual Feedback: '''Use real-time visual cues, such as color changes, to inform users about the bidding status instantly.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Develop Comprehensive Test Suites: '''Create extensive automated tests covering edge cases and critical functionalities to guarantee reliable system behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;v.-uml-diagram&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== UML Diagram ==&lt;br /&gt;
&lt;br /&gt;
Below is a visual representation of the models associated with the review bids functionality, depicting the data structures and their relationships.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml class diagram2.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi.-implementation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
'''ReviewBid: '''The &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; manages the bidding process, where participants indicate their preferences for reviewing different topics or assignments. This helps distribute review tasks fairly based on participants' interests.&lt;br /&gt;
&lt;br /&gt;
'''Assignment: '''The &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model represents team or individual assignments in Expertiza. It stores all the essential information required to keep the assignment process organized, like managing participants, setting due dates, forming teams, and overseeing the review process.&lt;br /&gt;
&lt;br /&gt;
'''SignUpTopic: '''The &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt; model identifies specific topics or areas within an assignment for which participants can sign up. It manages the availability of these topics, tracks bids, coordinates team sign-ups, and assigns topics based on both bids and participant availability. &lt;br /&gt;
&lt;br /&gt;
'''Participant: '''The &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model is about the individual users who participate in an assignment. This includes students, instructors, teaching assistants, or other relevant roles. This model is responsible for managing key user information, monitoring interactions with the assignment, keeping track of team memberships, and recording things like badges and review grades.&lt;br /&gt;
&lt;br /&gt;
== Implementation ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1.-decoupling-authorization-logic-from-reviewbidscontroller&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Decoupling Authorization Logic from &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| '''&amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; needs to use the authentication utilities'''&lt;br /&gt;
&lt;br /&gt;
The function &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; should use the available authentication utilities to support the Single Responsibility Principle and Separation of Concerns and to support code reusability.&lt;br /&gt;
&lt;br /&gt;
| '''Authentication Consistency:''' Remove authentication logic in the controller and call logic in the authentication utility class instead to ensure consistent use of the same logic across the application, foster reusability, and eliminate DRY.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
==== Authorization Flow Before Refactoring ====&lt;br /&gt;
The sequence diagram depicts the authorization process within the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;. When a user invokes an action, the controller checks if the action is allowed by verifying the user's role through the &amp;lt;code&amp;gt;current_role_name()&amp;lt;/code&amp;gt; method in the &amp;lt;code&amp;gt;ApplicationController&amp;lt;/code&amp;gt;. Depending on the action and the user's role, the controller either authorizes the user to proceed or denies access, ultimately executing the action logic if authorized and responding to the user.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 1.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We simplified the &amp;lt;code&amp;gt;action_allowed?&amp;lt;/code&amp;gt; method by decoupling authorization logic from the controller to use helper methods in the &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt; module. The &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; should not handle authentication, which is a violation of SRP. &lt;br /&gt;
&lt;br /&gt;
Note: We also made similar updates to the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; methods for &amp;lt;code&amp;gt;SignUpController&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;vertical-align:top;&amp;quot; | Original Code !! style=&amp;quot;vertical-align:top;&amp;quot; | Refactored Code&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index'&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator',&lt;br /&gt;
     'Student'].include? current_role_name and&lt;br /&gt;
        ((%w[list].include? action_name) ? are_needed_authorizations_present?(params[:id], &amp;quot;participant&amp;quot;, &amp;quot;reader&amp;quot;, &amp;quot;submitter&amp;quot;, &amp;quot;reviewer&amp;quot;) : true)&lt;br /&gt;
  else&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator'].include? current_role_name&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index', 'list'&lt;br /&gt;
    current_user_has_student_privileges? &amp;amp;&amp;amp; list_authorization_check&lt;br /&gt;
  else&lt;br /&gt;
    current_user_has_ta_privileges?&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; action ===&lt;br /&gt;
Previously, the &amp;lt;code&amp;gt;ReviewBidsController's index&amp;lt;/code&amp;gt; action was overloaded with responsibilities—it handled everything from locating and validating assignments to fetching review mappings and counting completed reviews. This all-in-one approach made the controller action messy. &lt;br /&gt;
&lt;br /&gt;
We have moved the business logic from the controller action and into dedicated service objects. With this change, we introduced two key service objects: &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; focuses on retrieving the participant along with their assignments, while the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; takes on the task of managing review mappings and calculating review-related stats, like how many reviews have been completed. &lt;br /&gt;
&lt;br /&gt;
This separation of responsibilities aligns with the Single Responsibility Principle. The logic for fetching assignments, retrieving review mappings, and counting reviews is now more flexible and reusable throughout the application. For example, we used methods in the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; in both the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; and the &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;, helping to avoid code duplication.The article https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial served as a guide on when and how to use service objects in Rails.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
  return unless current_user_id?(@participant.user_id)&lt;br /&gt;
  @assignment = @participant.assignment&lt;br /&gt;
  @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
  # Finding how many reviews have been completed&lt;br /&gt;
  @num_reviews_completed = 0&lt;br /&gt;
  @review_mappings.each do |map|&lt;br /&gt;
    @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # render view for completing reviews after review bidding has been completed&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @assignment = participant_service.assignment&lt;br /&gt;
  unless @assignment.is_a?(Assignment)&lt;br /&gt;
    flash[:error] = 'Assignment not found.'&lt;br /&gt;
    redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  @review_mappings = review_service.review_mappings&lt;br /&gt;
  @num_reviews_completed = review_service.review_counts[:completed]&lt;br /&gt;
&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Optimize &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Action functionality ===&lt;br /&gt;
In the earlier version of the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, the server only needed a couple of key pieces of information: an array of topic IDs (params[:topic]) and the participant ID (params[:id}). From there, it would figure out the assignment_id by using the first topic as a reference. This is risky because the &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt; associated with the topic may not be a valid &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
With the new implementation, we structured the parameters to include the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt;, an array of topic IDs, and &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;. This change ensures that the server always receives complete and reliable information, preventing issues caused by missing or inconsistent data. It also removes the need for the server to perform unnecessary lookups, as in the case of &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;, allowing the controller to focus on receiving requests and rendering appropriate responses.&lt;br /&gt;
&lt;br /&gt;
While these updates have cleaned up the set_priority action, they still contain a lot of business logic, like validations and priority updates. To make the code even more maintainable, there’s room for further refactoring—perhaps moving some of this logic into a service class or model method to streamline the controller even more.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | Old Code&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    if params[:topic].nil?&lt;br /&gt;
      ReviewBid.where(participant_id: params[:id]).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      participant = AssignmentParticipant.find_by(id: params[:id])&lt;br /&gt;
      assignment_id = SignUpTopic.find(params[:topic].first).assignment.id&lt;br /&gt;
      @bids = ReviewBid.where(participant_id: params[:id])&lt;br /&gt;
      signed_up_topics = ReviewBid.where(participant_id: params[:id]).map(&amp;amp;:signuptopic_id)&lt;br /&gt;
      signed_up_topics -= params[:topic].map(&amp;amp;:to_i)&lt;br /&gt;
      signed_up_topics.each do |topic|&lt;br /&gt;
        ReviewBid.where(signuptopic_id: topic, participant_id: params[:id]).destroy_all&lt;br /&gt;
      end&lt;br /&gt;
      params[:topic].each_with_index do |topic_id, index|&lt;br /&gt;
        bid_existence = ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id])&lt;br /&gt;
        if bid_existence.empty?&lt;br /&gt;
          ReviewBid.create(priority: index + 1, signuptopic_id: topic_id, participant_id: params[:id], assignment_id: assignment_id)&lt;br /&gt;
        else&lt;br /&gt;
          ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id]).update_all(priority: index + 1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to action: 'show', assignment_id: params[:assignment_id], id: params[:id]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    participant_id = params[:participant_id].to_i&lt;br /&gt;
    selected_topic_ids = params[:topic]&amp;amp;.map(&amp;amp;:to_i) || []&lt;br /&gt;
    assignment_id = params[:assignment_id].to_i&lt;br /&gt;
&lt;br /&gt;
    unless current_user_can_modify_participant?(participant_id)&lt;br /&gt;
      flash[:error] = 'You are not authorized to modify these bids.'&lt;br /&gt;
      respond_to do |format|&lt;br /&gt;
        format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
        format.json { render json: { status: 'unauthorized' }, status: :unauthorized }&lt;br /&gt;
      end&lt;br /&gt;
      return&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    if selected_topic_ids.empty?&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      assignment = Assignment.find_by(id: assignment_id)&lt;br /&gt;
      if assignment.nil?&lt;br /&gt;
        flash[:error] = &amp;quot;Invalid assignment.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_assignment' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      unless SignUpTopic.where(id: selected_topic_ids, assignment_id: assignment_id).count == selected_topic_ids.size&lt;br /&gt;
        flash[:error] = &amp;quot;One or more selected topics are invalid.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_topics' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).where.not(signuptopic_id: selected_topic_ids).destroy_all&lt;br /&gt;
&lt;br /&gt;
      selected_topic_ids.each_with_index do |topic_id, index|&lt;br /&gt;
        bid = ReviewBid.find_or_initialize_by(signuptopic_id: topic_id, participant_id: participant_id)&lt;br /&gt;
        bid.priority = index + 1&lt;br /&gt;
        bid.assignment_id = assignment_id&lt;br /&gt;
        bid.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_to action: 'show', assignment_id: assignment_id, id: participant_id, notice: 'Review bids updated successfully.' }&lt;br /&gt;
      format.json { render json: { status: 'success' }, status: :ok }&lt;br /&gt;
    end&lt;br /&gt;
  rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
    flash[:error] = &amp;quot;Failed to update priorities: #{e.message}&amp;quot;&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
      format.json { render json: { status: 'error', message: e.message }, status: :unprocessable_entity }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Client Side Changes to Support &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Changes ====&lt;br /&gt;
After tidying up the server-side logic, we needed to refactor the client-side code to ensure it passed the correct information from the view to the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action. On the client side, we now grab the topicIDs and send them as &amp;lt;code&amp;gt;selected_topic_ids&amp;lt;/code&amp;gt;, allowing the controller to figure out which topics were selected and in what order.  &lt;br /&gt;
&lt;br /&gt;
We also extracted the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, making it a lot easier to send data with AJAX and keeping things flexible. When working with jQuery, AJAX is awesome because it lets us update the server without reloading the page, making the user experience smooth and responsive. When we pull the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, we can grab it along with other data, like the list of topics, and include it as a single request. This way, we don’t have to rely on the URL to know which participant we’re working with, which makes the code cleaner.&lt;br /&gt;
&lt;br /&gt;
If we were to rely on the route (as opposed to using the DOM) for the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt;, every AJAX request would need to be tied to a specific URL structure. That’s limiting, especially if the route doesn’t typically include the participant ID, as can be the case with collection routes versus member routes in Rails. &lt;br /&gt;
&lt;br /&gt;
Note: One interesting aspect of the application was the mix of JavaScript and CoffeeScript code. It wasn't clear why both types of code were used.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery -&amp;gt;&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: -&amp;gt;&lt;br /&gt;
      $.post($(this).data('update-url'), $(this).sortable('serialize'))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery(document).ready ($) -&amp;gt;&lt;br /&gt;
  sendUpdatedOrder = -&amp;gt;&lt;br /&gt;
    sortedIDs = $(this).sortable('toArray')&lt;br /&gt;
&lt;br /&gt;
    topicIDs = sortedIDs.map (id) -&amp;gt;&lt;br /&gt;
      id.replace 'topic_', ''&lt;br /&gt;
&lt;br /&gt;
    participant_id = $(this).data('participant-id')&lt;br /&gt;
&lt;br /&gt;
    assignment_id = new URL($(this).data('update-url'), window.location.origin).searchParams.get('assignment_id')&lt;br /&gt;
&lt;br /&gt;
    $.ajax&lt;br /&gt;
      type: 'POST'&lt;br /&gt;
      url: $(this).data('update-url')&lt;br /&gt;
      data:&lt;br /&gt;
        topic: topicIDs&lt;br /&gt;
        participant_id: participant_id&lt;br /&gt;
        assignment_id: assignment_id&lt;br /&gt;
      success: (response) -&amp;gt;&lt;br /&gt;
        console.log 'Selections updated successfully.'&lt;br /&gt;
      error: (xhr, status, error) -&amp;gt;&lt;br /&gt;
        alert 'An error occurred while saving your selections. Please try again.'&lt;br /&gt;
        console.error error&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: sendUpdatedOrder&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-1.-signupsheetcontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; Updates ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; method renders the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; action in &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt;. However, the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; does not include the controller action for this view. We need to find the old implementation of the controller action, add it back, and review and update it for any DRY violations.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # provides variables for reviewing page located at views/review_bids/others_work.html.erb&lt;br /&gt;
  def index&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
    return unless current_user_id?(@participant.user_id)&lt;br /&gt;
&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
    # Finding how many reviews have been completed&lt;br /&gt;
    @num_reviews_completed = 0&lt;br /&gt;
    @review_mappings.each do |map|&lt;br /&gt;
      @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # render view for completing reviews after review bidding has been completed&lt;br /&gt;
    render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
  end  &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-2.-reviewbidsotherswork-view-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;review_bids_others_work.html.erb&amp;lt;/code&amp;gt; View Updates ====&lt;br /&gt;
&lt;br /&gt;
The code only contains a view with this name and no associated controller action. Reviewing the code in the view, we noticed some SRP violations where there is much logic in the presentation layer. Set up helper methods in the controller to handle some of that logic or see if the model can handle some of that logic.&lt;br /&gt;
&lt;br /&gt;
Note: The view is technically part of the SignUpSheetController, but it's being rendered by the ReviewBidsController, which initially threw us off. After chatting with the professor, we decided not to change this view. He indicated that the plan is to reuse these views for both assignment bidding and review bidding.&lt;br /&gt;
&lt;br /&gt;
We would suggest building the assignment bidding and review bidding features separately first to make sure each one works on its own. Once that’s done, we can go back and clean things up by combining any shared functionality. This could include creating shared views or even introducing an abstract model to handle both types of bidding in a cleaner way. It could also mean replacing the ReviewBidsController with something more generic, like a BidsController, to keep things reusable and easier to maintain down the line.&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Rename &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; method ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; should be &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt;&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:''' Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The primary activity here is to refactor the method name &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; and fix the hardcoded URL. However, the match topic bidding algorithm no longer works since Heroku is no longer free, per the professor. The reference link below has detailed information about the web service that we can leverage to gain that understanding.&lt;br /&gt;
&lt;br /&gt;
Reference Link for Webservice Details: [https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Updated Implementation ====&lt;br /&gt;
The original requirement was to rename the run_bidding_algorithm method to assign_reviewers, but after digging into the code, we realized that renaming it didn’t make sense. The method runs the bidding algorithm to match topics, and there’s already a separate method to handle assigning review topics.&lt;br /&gt;
&lt;br /&gt;
However, we improved the logic in the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; method (which calls the private &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt;) by introducing a separate &amp;lt;code&amp;gt;BiddingAlgorithmService&amp;lt;/code&amp;gt; object. This keeps the business logic out of the controller action, which makes things cleaner and more maintainable.&lt;br /&gt;
&lt;br /&gt;
==== review_bids_controller ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def run_bidding_algorithm&lt;br /&gt;
    review_bid = ReviewBid.new&lt;br /&gt;
    bidding_data = review_bid.bidding_data&lt;br /&gt;
    matched_topics = BiddingAlgorithmService.new(bidding_data).run&lt;br /&gt;
    raise ArgumentError, 'Failed to assign reviewers. Please try again later.' unless matched_topics&lt;br /&gt;
&lt;br /&gt;
    matched_topics&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== bidding_algorithm_service ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# frozen_string_literal: true&lt;br /&gt;
&lt;br /&gt;
# The `BiddingAlgorithmService` run the bid assignment algorithm&lt;br /&gt;
# Sends student IDs, topic IDs, student preferences, and timestamps to the web service&lt;br /&gt;
# The web service returns the matched assignments in the JSON response body&lt;br /&gt;
class BiddingAlgorithmService&lt;br /&gt;
  SERVICE_URL = 'http://app-csc517.herokuapp.com/match_topics'.freeze&lt;br /&gt;
&lt;br /&gt;
  # MOCK_DATA is based on the documentation detailing how the webservice behaves.&lt;br /&gt;
  # Reference: https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review#Webservice&lt;br /&gt;
  MOCK_DATA = {&lt;br /&gt;
    36239 =&amp;gt; [3970, 3972, 3975],&lt;br /&gt;
    36240 =&amp;gt; [3973, 3974, 3972],&lt;br /&gt;
    36241 =&amp;gt; [3969, 3971, 3972],&lt;br /&gt;
    36242 =&amp;gt; [3969, 3971, 3973],&lt;br /&gt;
    36243 =&amp;gt; [3969, 3970, 3971]&lt;br /&gt;
  }.freeze&lt;br /&gt;
&lt;br /&gt;
  def initialize(bidding_data)&lt;br /&gt;
    @bidding_data = bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def run&lt;br /&gt;
    return MOCK_DATA if Rails.application.config.use_mock_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
    perform_request&lt;br /&gt;
  rescue RestClient::ExceptionWithResponse, JSON::ParserError&lt;br /&gt;
    false&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def perform_request&lt;br /&gt;
    response = RestClient.post(&lt;br /&gt;
      SERVICE_URL,&lt;br /&gt;
      @bidding_data.to_json,&lt;br /&gt;
      content_type: :json,&lt;br /&gt;
      accept: :json&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    validate_response(response)&lt;br /&gt;
    JSON.parse(response.body)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def validate_response(response)&lt;br /&gt;
    raise 'Invalid response format' unless response.headers[:content_type] &amp;amp;&amp;amp; response.headers[:content_type].include?('application/json')&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Resolve Functionality Issues for Non Bidders ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Currently it doesn't work if some student does not bid. In this case, algorithm needs to be fixed to ignore anyone who didn’t bid, and let them choose from what’s left over.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to do several things within this controller. We will set a default state for non-bidders that we can point to to give them options to select topics to review from the leftovers. We will consolidate &amp;lt;code&amp;gt;@sign_up_topics&amp;lt;/code&amp;gt; to resolve the DRY violation. Lastly, we will remove business logic from the show method and push it to a new helper to conform to the SRP.&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # Additional method&lt;br /&gt;
&lt;br /&gt;
  # Provides variables for review bidding page&lt;br /&gt;
  def show&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id].to_i)&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @sign_up_topics = SignUpTopic.where(assignment_id: @assignment.id, private_to: nil)&lt;br /&gt;
    my_topic = SignedUpTeam.topic_id(@participant.parent_id, @participant.user_id)&lt;br /&gt;
    @sign_up_topics -= SignUpTopic.where(assignment_id: @assignment.id, id: my_topic)&lt;br /&gt;
    @num_participants = AssignmentParticipant.where(parent_id: @assignment.id).count&lt;br /&gt;
    @selected_topics = nil # This is used to list the topics assigned to review (i.e., select == assigned)&lt;br /&gt;
    @bids = ReviewBid.where(participant_id: @participant, assignment_id: @assignment.id)&lt;br /&gt;
    signed_up_topics = []&lt;br /&gt;
    &lt;br /&gt;
    @bids.each do |bid|&lt;br /&gt;
      sign_up_topic = SignUpTopic.find_by(id: bid.signuptopic_id)&lt;br /&gt;
      signed_up_topics &amp;lt;&amp;lt; sign_up_topic if sign_up_topic&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    signed_up_topics &amp;amp;= @sign_up_topics&lt;br /&gt;
    @sign_up_topics -= signed_up_topics&lt;br /&gt;
    @bids = signed_up_topics&lt;br /&gt;
    @num_of_topics = @sign_up_topics.size&lt;br /&gt;
    @assigned_review_maps = []&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: @assignment.id, reviewer_id: @participant.id).each do |review_map|&lt;br /&gt;
      @assigned_review_maps &amp;lt;&amp;lt; review_map&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Explicitly render view since it's in the sign up sheet views&lt;br /&gt;
    render 'sign_up_sheet/review_bids_show'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Additional methods&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-2.-reviewbidshelper-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
 # Assigns bidding topics to reviewers&lt;br /&gt;
  def assign_bidding&lt;br /&gt;
      assignment = validate_assignment(params[:assignment_id])&lt;br /&gt;
&lt;br /&gt;
      reviewers = validate_reviewers(assignment.id)&lt;br /&gt;
      reviewer_ids = reviewers.map(&amp;amp;:id)&lt;br /&gt;
&lt;br /&gt;
      matched_topics = run_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
      if matched_topics.blank?&lt;br /&gt;
        flash[:alert] = 'Topic or assignment is missing'&lt;br /&gt;
        redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      leftover_topics = find_leftover_topics(assignment.id, matched_topics)&lt;br /&gt;
      assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.new.assign_review_topics(matched_topics)&lt;br /&gt;
      assignment.update!(can_choose_topic_to_review: false)&lt;br /&gt;
&lt;br /&gt;
      flash[:notice] = 'Reviewers were successfully assigned to topics.'&lt;br /&gt;
      redirect_back fallback_location: root_path&lt;br /&gt;
    rescue ArgumentError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ArgumentError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: e.message&lt;br /&gt;
    rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::RecordInvalid: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue ActiveRecord::ActiveRecordError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::ActiveRecordError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue StandardError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;StandardError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers. Please try again later.'&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  #service methods here ...&lt;br /&gt;
&lt;br /&gt;
  def find_leftover_topics(assignment_id, matched_topics)&lt;br /&gt;
    all_topic_ids = SignUpTopic.where(assignment_id: assignment_id).pluck(:id)&lt;br /&gt;
    assigned_topic_ids = matched_topics.map { |match| match[:topic_id] }&lt;br /&gt;
&lt;br /&gt;
    # Calculate leftover topics&lt;br /&gt;
    all_topic_ids - assigned_topic_ids&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
    return if leftover_topics.blank?&lt;br /&gt;
&lt;br /&gt;
    # Find non-bidders by excluding already matched reviewers&lt;br /&gt;
    non_bidders = reviewer_ids - matched_topics.keys&lt;br /&gt;
&lt;br /&gt;
    # Assign leftover topics to non-bidders in a round-robin fashion&lt;br /&gt;
    non_bidders.each_with_index do |reviewer_id, index|&lt;br /&gt;
      topic_id = leftover_topics[index % leftover_topics.length]&lt;br /&gt;
      ReviewBid.create(priority: 1, signuptopic_id: topic_id, participant_id: reviewer_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirm Functionality for Single-person Teams ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Make sure your code works on individual assignments, i.e., assignments where participants sign up for topics instead of teams. In this case, a team is created for each participant when they sign up. So the code should work for assignments to which either individuals or teams submit.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to make an update to focus on topic rather than team or have it where teams are updated based on topic id.We will probably utilize sign up topic or a similar object to accomplish this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-5-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bid Status Messaging ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| To make the functionality more intuitive, include a message to say how many students are eligible to submit bids, how many have submitted their bids, when the deadline for submitting bids is.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
The logic for these messages will go into the helper file. It is effectively the same reasoning as our migration of other business/functionality code from the controller to the helper to conform to the SRP. It will go hand in hand with the SignUpTopic portion of the code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-6-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Why are the methods in review_bid.rb class methods? Can we change them to instance methods or move it to helpers?&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-1.-reviewbid-model-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model Updates ====&lt;br /&gt;
Since the current class methods are tied to &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model instances, we will change them to instance methods. There is no need to move them to helpers.&lt;br /&gt;
&lt;br /&gt;
===== Review Bid Model Flow Before Refactoring =====&lt;br /&gt;
This sequence diagram describes how the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; interacts with the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model in assigning review topics to reviewers according to their bids. Once the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; action is initiated by the user, the controller gathers the IDs of reviewers and calls &amp;lt;code&amp;gt;ReviewBid.bidding_data&amp;lt;/code&amp;gt; to gather bidding information. That is sent out to an external bidding algorithm, and the matched topics that come back are used by &amp;lt;code&amp;gt;ReviewBid.assign_review_topics&amp;lt;/code&amp;gt; to create &amp;lt;code&amp;gt;ReviewResponseMap&amp;lt;/code&amp;gt; entries, assigning topics to reviewers for the assignment.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
  belongs_to :assignment, class_name: 'Assignment'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  def self.bidding_data(assignment_id, reviewer_ids)&lt;br /&gt;
    # create basic hash and set basic hash data&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'users' =&amp;gt; {}, 'max_accepted_proposals' =&amp;gt; [] }&lt;br /&gt;
    bidding_data['tid'] = SignUpTopic.where(assignment_id: assignment_id).ids&lt;br /&gt;
    bidding_data['max_accepted_proposals'] = Assignment.where(id: assignment_id).pluck(:num_reviews_allowed).first&lt;br /&gt;
&lt;br /&gt;
    # loop through reviewer_ids to get reviewer specific bidding data&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      bidding_data['users'][reviewer_id] = reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # assigns topics to reviews as matched by the webservice algorithm&lt;br /&gt;
  def self.assign_review_topics(assignment_id, reviewer_ids, matched_topics, _min_num_reviews = 2)&lt;br /&gt;
    # if review response map already created, delete it&lt;br /&gt;
    if ReviewResponseMap.where(reviewed_object_id: assignment_id)&lt;br /&gt;
      ReviewResponseMap.where(reviewed_object_id: assignment_id).destroy_all&lt;br /&gt;
    end&lt;br /&gt;
    # loop through reviewer_ids to assign reviews to each reviewer&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      topics_to_assign = matched_topics[reviewer_id.to_s]&lt;br /&gt;
      topics_to_assign.each do |topic|&lt;br /&gt;
        assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method to assign a single topic to a reviewer&lt;br /&gt;
  def self.assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic).pluck(:team_id).first&lt;br /&gt;
    team_to_review.nil? ? [] : ReviewResponseMap.create(reviewed_object_id: assignment_id, reviewer_id: reviewer_id, reviewee_id: team_to_review, type: 'ReviewResponseMap')&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method for getting individual reviewer_ids bidding data&lt;br /&gt;
  # returns user's bidding data hash&lt;br /&gt;
  def self.reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    reviewer_user_id = AssignmentParticipant.find(reviewer_id).user_id&lt;br /&gt;
    self_topic = SignedUpTeam.topic_id(assignment_id, reviewer_user_id)&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'otid' =&amp;gt; self_topic, 'priority' =&amp;gt; [], 'time' =&amp;gt; [] }&lt;br /&gt;
    bids = ReviewBid.where(participant_id: reviewer_id)&lt;br /&gt;
&lt;br /&gt;
    # loop through each bid for a topic to get specific data&lt;br /&gt;
    bids.each do |bid|&lt;br /&gt;
      bidding_data['tid'] &amp;lt;&amp;lt; bid.signuptopic_id&lt;br /&gt;
      bidding_data['priority'] &amp;lt;&amp;lt; bid.priority&lt;br /&gt;
      bidding_data['time'] &amp;lt;&amp;lt; bid.updated_at&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  # Instance method to get bidding data for this review bid&lt;br /&gt;
  def bidding_data&lt;br /&gt;
    {&lt;br /&gt;
      'tid' =&amp;gt; topic_ids,&lt;br /&gt;
      'otid' =&amp;gt; self_topic,&lt;br /&gt;
      'priority' =&amp;gt; priorities,&lt;br /&gt;
      'time' =&amp;gt; updated_times&lt;br /&gt;
    }&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # bid object to limit db touches for similar data acquisitions&lt;br /&gt;
  def bids&lt;br /&gt;
    @bids ||= ReviewBid.where(participant_id: participant_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def topic_ids&lt;br /&gt;
    bids.pluck(:signuptopic_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def priorities&lt;br /&gt;
    bids.pluck(:priority)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def updated_times&lt;br /&gt;
    bids.pluck(:updated_at)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self_topic&lt;br /&gt;
    return nil unless topic &amp;amp;&amp;amp; topic.assignment_id&lt;br /&gt;
&lt;br /&gt;
    SignedUpTeam.topic_id(topic.assignment_id, participant.user_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign topics to reviews&lt;br /&gt;
  def assign_review_topics(matched_topics)&lt;br /&gt;
    validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: topic.assignment_id).destroy_all&lt;br /&gt;
&lt;br /&gt;
    matched_topics.each do |reviewer_id, topics|&lt;br /&gt;
      Array(topics).each { |topic_id| assign_topic_to_reviewer(reviewer_id, topic_id) }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign a single topic to a reviewer&lt;br /&gt;
  def assign_topic_to_reviewer(reviewer_id, topic_id)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic_id).pluck(:team_id).first&lt;br /&gt;
    return if team_to_review.nil?&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.create(&lt;br /&gt;
      reviewed_object_id: topic.assignment_id,&lt;br /&gt;
      reviewer_id: reviewer_id,&lt;br /&gt;
      reviewee_id: team_to_review,&lt;br /&gt;
      type: 'ReviewResponseMap'&lt;br /&gt;
    )&lt;br /&gt;
    # error handling&lt;br /&gt;
  rescue StandardError =&amp;gt; e&lt;br /&gt;
    Rails.logger.error(&amp;quot;Failed to assign topic #{topic_id} to reviewer #{reviewer_id}: #{e.message}&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
    raise ArgumentError, 'Topic or assignment is missing' if topic.nil? || topic.assignment_id.nil?&lt;br /&gt;
    raise ArgumentError, 'Matched topics must be a Hash' unless matched_topics.is_a?(Hash)&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
Once we changed them to instance methods, we modified their use in the controller code to instantiate the object first.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-3.-schema.db-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;Schema.db&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We also need to remove the foreign key reference to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model since the &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model already has access to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;, and that is the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; we should be referencing. This will also involve modifying the database to remove the foreign key reference to the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Steps required:&lt;br /&gt;
&lt;br /&gt;
# Review the records in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table in the database to see if there are any records and if those records have values for &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;&lt;br /&gt;
# Create a migration file using the command: &amp;lt;code&amp;gt;rails generate migration RemoveAssignmentFromReviewBids&amp;lt;/code&amp;gt;&lt;br /&gt;
# In the generated file, indicate the change that needs to be made using the change method and migration columns &amp;lt;code&amp;gt;remove_foreign_key&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;remove_column&amp;lt;/code&amp;gt;. Migration should be straightforward since there is no cascading behavior (i.e., &amp;lt;code&amp;gt;dependent:destroy&amp;lt;/code&amp;gt;) to worry about.&lt;br /&gt;
# Once comfortable with the updates to the generated file, run the migration using the &amp;lt;code&amp;gt;rails db:migrate&amp;lt;/code&amp;gt; command.&lt;br /&gt;
# Run a query in the DB to ensure no orphaned records are in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table after the updates.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-4.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implement Edge Case Testing ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| In the previous implementation wiki, there are edge cases which are not exhaustively tested. Should test those edge cases thoroughly and add more edge case testing - link&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span class=&amp;quot;mark&amp;quot;&amp;gt;TODO&amp;lt;/span&amp;gt;:We just got initial test results back. We still need to figure out why tests are failing and what may be slipping through the cracks.[[File:Test expertiza.png|411x459px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-8-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design Patterns and Principles ===&lt;br /&gt;
Below are the design patterns and principles we plan to use to address the problem statements outlined in the project requirements:&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
** Controller Responsibility&lt;br /&gt;
*** To address the issue of authentication logic residing in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, we will refactor the code to ensure the controller solely handles request and response flows.&lt;br /&gt;
*** Authentication logic will be moved to dedicated methods in &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt;, making the codebase easier to maintain and test.&lt;br /&gt;
** View Simplification&lt;br /&gt;
*** Currently, the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; view contains excessive business logic, violating SRP.&lt;br /&gt;
*** We will move this logic into helper methods or models, ensuring each component has a single responsibility and improving code readability.&lt;br /&gt;
&lt;br /&gt;
* Separation of Concerns (SoC)&lt;br /&gt;
** By relocating authorization logic out of the controller, we separate authentication from request handling.&lt;br /&gt;
** Adhering to SoC enhances readability and maintainability by allowing each part of the application to focus on its specific role.&lt;br /&gt;
* Don't Repeat Yourself (DRY)&lt;br /&gt;
** We will inspect the existing implementation for code duplication, particularly in the controller actions.&lt;br /&gt;
** Any repetitive code will be refactored or removed to reduce redundancy and improve clarity.&lt;br /&gt;
&lt;br /&gt;
* Model-View-Controller (MVC) Adherence&lt;br /&gt;
** Moving business logic from the view aligns with MVC best practices.&lt;br /&gt;
** By ensuring the view only presents data without processing it, we maintain clear boundaries between the model, view, and controller layers.&lt;br /&gt;
&lt;br /&gt;
* Dependency Inversion Principle (DIP)&lt;br /&gt;
** We plan to update class methods to instance methods, allowing the controller to depend on abstractions rather than concrete implementations.&lt;br /&gt;
** This change leverages Ruby's duck typing, providing flexibility and making the code easier to maintain and test.&lt;br /&gt;
&lt;br /&gt;
** Clear and intentional naming during refactoring will enhance code clarity and maintainability.&lt;br /&gt;
&lt;br /&gt;
== Files Modified/Added ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-1.-controllers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
review_bids_controller.rb&lt;br /&gt;
&lt;br /&gt;
signup_sheet_controller.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-2.-models&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
review_bid.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-3.-helpers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
&lt;br /&gt;
authorization_helper.rb&lt;br /&gt;
&lt;br /&gt;
Review_bids_helper.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;viii.-test-plan&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
The Excel spreadsheet below details our test plan for testing the code we changed or created to ensure code coverage. The process involved reviewing the code and determining all the possible scenarios that needed to be covered based on what the method was doing, then creating test cases accordingly.&lt;br /&gt;
&lt;br /&gt;
* [https://docs.google.com/spreadsheets/d/1iHzOdSwlTtxOtf5VsNdwOaCP8NzYMmiJ/edit?usp=drive_link Open Test Matrix Spreadsheet]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/review_bids_controller_spec.rb ReviewBidsController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/sign_up_sheet_controller_spec.rb SignUpSheetController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/models/review_bid_spec.rb ReviewBid model updated test cases]&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-1.-mentor&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Ed Gehringer&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-2.-members&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Members ===&lt;br /&gt;
&lt;br /&gt;
* Gavin Teague&lt;br /&gt;
* Janice Uwujaren&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xi.-links&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
Previous Wiki: [https://wiki.expertiza.ncsu.edu/index.php/CSC/ECE_517_Fall_2021_-_E2151._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;Link&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xii.-references&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/document/d/1LHVacylQmB15I6VcYUA5nuC7GRRU9szHzZhjlPcE-PA/edit?tab=t.0#heading=h.u0oycoj6e0ev &amp;lt;u&amp;gt;Project Instructions&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;appendix-a-uml-class-diagram-plantuml-code-for-review-bids&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Juwujar</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=161043</id>
		<title>CSC/ECE 517 Fall 2024 - E2485. Allow reviewers to bid on what to review</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=161043"/>
		<updated>2024-12-12T05:37:23Z</updated>

		<summary type="html">&lt;p&gt;Juwujar: /* Project Overview */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
This project will fix the defects and improve the functionality in Expertiza of the Reviewer Bidding feature. It will make the functionality smoother, more reliable, and align with Expertiza's collaborative learning objectives. &lt;br /&gt;
&lt;br /&gt;
The Review Bidding feature lets reviewers focus on topics they know best or care about most. If someone feels exceptionally qualified or interested in a topic, they can bid to review submissions in that area. This helps ensure reviews are insightful and enjoyable for the reviewer while benefiting the other participants and the overall process.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iii.-problem-statements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Problem Statements ==&lt;br /&gt;
&lt;br /&gt;
Assigning reviews to users on Expertiza involves a complex bidding algorithm that requires optimization before production deployment. Below are key areas we plan to address as per the project requirements:&lt;br /&gt;
* Code Refactoring and Best Practices&lt;br /&gt;
** '''Utilize Authentication Utilities''': Modify the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; function in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to use existing authentication utilities, enhancing code reusability and adhering to the Single Responsibility Principle (SRP) and Separation of Concerns (SoC).&lt;br /&gt;
** '''Eliminate Code Duplication''': Refactor &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; to comply with the Don't Repeat Yourself (DRY) principle.&lt;br /&gt;
** '''Rename Functions for Clarity''': Change &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; to better reflect its purpose.&lt;br /&gt;
** '''Refactor Methods in '''&amp;lt;code&amp;gt;review_bid.rb&amp;lt;/code&amp;gt;''':''': Convert class methods to instance methods or relocate them to helper classes for improved code organization.&lt;br /&gt;
* Functionality Enhancements&lt;br /&gt;
** '''Handle Missing Bids Gracefully''': Adjust the bidding algorithm to accommodate students who do not bid, allowing them to select from the remaining options.&lt;br /&gt;
** '''Support Both Individual and Team Assignments''': Ensure the code functions correctly for participant and team-based assignments when signing up for topics.&lt;br /&gt;
* User Interface Improvements&lt;br /&gt;
** '''Increase Transparency in the Bidding Process''': Display messages indicating how many students are eligible to bid, how many have submitted bids, and the deadline.&lt;br /&gt;
** '''Accurate Bid Status Indicators''': Ensure topic colors change correctly based on the number of outstanding bids, providing clear visual feedback to users.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Exhaustive Edge Case Testing''': Conduct thorough testing for edge cases not previously covered, adding additional test cases as needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iv.-design-goals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Design Goals ==&lt;br /&gt;
&lt;br /&gt;
While addressing the existing defects and enhancing the Reviewer Bidding feature, we plan to adhere to the following design guidelines:&lt;br /&gt;
&lt;br /&gt;
* Code Quality and Maintainability&lt;br /&gt;
** '''Improve Code Structure: '''Implement refactoring strategies to enhance code readability and maintainability, ensuring adherence to best practices like the DRY principle.&lt;br /&gt;
** '''Standardize Authentication Logic: '''Utilize a centralized authentication utility to promote consistency and reusability across the application.&lt;br /&gt;
* System Stability and Flexibility&lt;br /&gt;
** '''Enhance Algorithm Robustness: '''Modify the bidding algorithm to handle scenarios where students do not bid, ensuring the system remains stable under all conditions.&lt;br /&gt;
** '''Ensure Assignment Versatility: '''Design the system to seamlessly support both individual and team assignments without additional configuration.&lt;br /&gt;
* User Experience Enhancements&lt;br /&gt;
** '''Increase Bidding Transparency: '''Develop user interface elements that clearly display bidding status, deadlines, and participation metrics.&lt;br /&gt;
** '''Implement Dynamic Visual Feedback: '''Use real-time visual cues, such as color changes, to inform users about the bidding status instantly.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Develop Comprehensive Test Suites: '''Create extensive automated tests covering edge cases and critical functionalities to guarantee reliable system behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;v.-uml-diagram&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== UML Diagram ==&lt;br /&gt;
&lt;br /&gt;
Below is a visual representation of the models associated with the review bids functionality, depicting the data structures and their relationships.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml class diagram2.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi.-implementation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
'''ReviewBid: '''The &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; manages the bidding process, where participants indicate their preferences for reviewing different topics or assignments. This helps distribute review tasks fairly based on participants' interests.&lt;br /&gt;
&lt;br /&gt;
'''Assignment: '''The &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model represents team or individual assignments in Expertiza. It stores all the essential information required to keep the assignment process organized, like managing participants, setting due dates, forming teams, and overseeing the review process.&lt;br /&gt;
&lt;br /&gt;
'''SignUpTopic: '''The &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt; model identifies specific topics or areas within an assignment for which participants can sign up. It manages the availability of these topics, tracks bids, coordinates team sign-ups, and assigns topics based on both bids and participant availability. &lt;br /&gt;
&lt;br /&gt;
'''Participant: '''The &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model is about the individual users who participate in an assignment. This includes students, instructors, teaching assistants, or other relevant roles. This model is responsible for managing key user information, monitoring interactions with the assignment, keeping track of team memberships, and recording things like badges and review grades.&lt;br /&gt;
&lt;br /&gt;
== Implementation ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1.-decoupling-authorization-logic-from-reviewbidscontroller&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Decoupling Authorization Logic from &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| '''&amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; needs to use the authentication utilities'''&lt;br /&gt;
&lt;br /&gt;
The function &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; should use the available authentication utilities to support the Single Responsibility Principle and Separation of Concerns and to support code reusability.&lt;br /&gt;
&lt;br /&gt;
| '''Authentication Consistency:''' Remove authentication logic in the controller and call logic in the authentication utility class instead to ensure consistent use of the same logic across the application, foster reusability, and eliminate DRY.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
==== Authorization Flow Before Refactoring ====&lt;br /&gt;
The sequence diagram depicts the authorization process within the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;. When a user invokes an action, the controller checks if the action is allowed by verifying the user's role through the &amp;lt;code&amp;gt;current_role_name()&amp;lt;/code&amp;gt; method in the &amp;lt;code&amp;gt;ApplicationController&amp;lt;/code&amp;gt;. Depending on the action and the user's role, the controller either authorizes the user to proceed or denies access, ultimately executing the action logic if authorized and responding to the user.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 1.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We simplified the &amp;lt;code&amp;gt;action_allowed?&amp;lt;/code&amp;gt; method by decoupling authorization logic from the controller to use helper methods in the &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt; module. The &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; should not handle authentication, which is a violation of SRP. &lt;br /&gt;
&lt;br /&gt;
Note: We also made similar updates to the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; methods for &amp;lt;code&amp;gt;SignUpController&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;vertical-align:top;&amp;quot; | Original Code !! style=&amp;quot;vertical-align:top;&amp;quot; | Refactored Code&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index'&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator',&lt;br /&gt;
     'Student'].include? current_role_name and&lt;br /&gt;
        ((%w[list].include? action_name) ? are_needed_authorizations_present?(params[:id], &amp;quot;participant&amp;quot;, &amp;quot;reader&amp;quot;, &amp;quot;submitter&amp;quot;, &amp;quot;reviewer&amp;quot;) : true)&lt;br /&gt;
  else&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator'].include? current_role_name&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index', 'list'&lt;br /&gt;
    current_user_has_student_privileges? &amp;amp;&amp;amp; list_authorization_check&lt;br /&gt;
  else&lt;br /&gt;
    current_user_has_ta_privileges?&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; action ===&lt;br /&gt;
Previously, the &amp;lt;code&amp;gt;ReviewBidsController's index&amp;lt;/code&amp;gt; action was overloaded with responsibilities—it handled everything from locating and validating assignments to fetching review mappings and counting completed reviews. This all-in-one approach made the controller action messy. &lt;br /&gt;
&lt;br /&gt;
We have moved the business logic from the controller action and into dedicated service objects. With this change, we introduced two key service objects: &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; focuses on retrieving the participant along with their assignments, while the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; takes on the task of managing review mappings and calculating review-related stats, like how many reviews have been completed. &lt;br /&gt;
&lt;br /&gt;
This separation of responsibilities aligns with the Single Responsibility Principle. The logic for fetching assignments, retrieving review mappings, and counting reviews is now more flexible and reusable throughout the application. For example, we used methods in the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; in both the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; and the &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;, helping to avoid code duplication.The article https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial served as a guide on when and how to use service objects in Rails.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
  return unless current_user_id?(@participant.user_id)&lt;br /&gt;
  @assignment = @participant.assignment&lt;br /&gt;
  @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
  # Finding how many reviews have been completed&lt;br /&gt;
  @num_reviews_completed = 0&lt;br /&gt;
  @review_mappings.each do |map|&lt;br /&gt;
    @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # render view for completing reviews after review bidding has been completed&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @assignment = participant_service.assignment&lt;br /&gt;
  unless @assignment.is_a?(Assignment)&lt;br /&gt;
    flash[:error] = 'Assignment not found.'&lt;br /&gt;
    redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  @review_mappings = review_service.review_mappings&lt;br /&gt;
  @num_reviews_completed = review_service.review_counts[:completed]&lt;br /&gt;
&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Optimize &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Action functionality ===&lt;br /&gt;
In the earlier version of the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, the server only needed a couple of key pieces of information: an array of topic IDs (params[:topic]) and the participant ID (params[:id}). From there, it would figure out the assignment_id by using the first topic as a reference. This is risky because the &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt; associated with the topic may not be a valid &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
With the new implementation, we structured the parameters to include the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt;, an array of topic IDs, and &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;. This change ensures that the server always receives complete and reliable information, preventing issues caused by missing or inconsistent data. It also removes the need for the server to perform unnecessary lookups, as in the case of &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;, allowing the controller to focus on receiving requests and rendering appropriate responses.&lt;br /&gt;
&lt;br /&gt;
While these updates have cleaned up the set_priority action, they still contain a lot of business logic, like validations and priority updates. To make the code even more maintainable, there’s room for further refactoring—perhaps moving some of this logic into a service class or model method to streamline the controller even more.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | Old Code&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    if params[:topic].nil?&lt;br /&gt;
      ReviewBid.where(participant_id: params[:id]).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      participant = AssignmentParticipant.find_by(id: params[:id])&lt;br /&gt;
      assignment_id = SignUpTopic.find(params[:topic].first).assignment.id&lt;br /&gt;
      @bids = ReviewBid.where(participant_id: params[:id])&lt;br /&gt;
      signed_up_topics = ReviewBid.where(participant_id: params[:id]).map(&amp;amp;:signuptopic_id)&lt;br /&gt;
      signed_up_topics -= params[:topic].map(&amp;amp;:to_i)&lt;br /&gt;
      signed_up_topics.each do |topic|&lt;br /&gt;
        ReviewBid.where(signuptopic_id: topic, participant_id: params[:id]).destroy_all&lt;br /&gt;
      end&lt;br /&gt;
      params[:topic].each_with_index do |topic_id, index|&lt;br /&gt;
        bid_existence = ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id])&lt;br /&gt;
        if bid_existence.empty?&lt;br /&gt;
          ReviewBid.create(priority: index + 1, signuptopic_id: topic_id, participant_id: params[:id], assignment_id: assignment_id)&lt;br /&gt;
        else&lt;br /&gt;
          ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id]).update_all(priority: index + 1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to action: 'show', assignment_id: params[:assignment_id], id: params[:id]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    participant_id = params[:participant_id].to_i&lt;br /&gt;
    selected_topic_ids = params[:topic]&amp;amp;.map(&amp;amp;:to_i) || []&lt;br /&gt;
    assignment_id = params[:assignment_id].to_i&lt;br /&gt;
&lt;br /&gt;
    unless current_user_can_modify_participant?(participant_id)&lt;br /&gt;
      flash[:error] = 'You are not authorized to modify these bids.'&lt;br /&gt;
      respond_to do |format|&lt;br /&gt;
        format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
        format.json { render json: { status: 'unauthorized' }, status: :unauthorized }&lt;br /&gt;
      end&lt;br /&gt;
      return&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    if selected_topic_ids.empty?&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      assignment = Assignment.find_by(id: assignment_id)&lt;br /&gt;
      if assignment.nil?&lt;br /&gt;
        flash[:error] = &amp;quot;Invalid assignment.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_assignment' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      unless SignUpTopic.where(id: selected_topic_ids, assignment_id: assignment_id).count == selected_topic_ids.size&lt;br /&gt;
        flash[:error] = &amp;quot;One or more selected topics are invalid.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_topics' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).where.not(signuptopic_id: selected_topic_ids).destroy_all&lt;br /&gt;
&lt;br /&gt;
      selected_topic_ids.each_with_index do |topic_id, index|&lt;br /&gt;
        bid = ReviewBid.find_or_initialize_by(signuptopic_id: topic_id, participant_id: participant_id)&lt;br /&gt;
        bid.priority = index + 1&lt;br /&gt;
        bid.assignment_id = assignment_id&lt;br /&gt;
        bid.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_to action: 'show', assignment_id: assignment_id, id: participant_id, notice: 'Review bids updated successfully.' }&lt;br /&gt;
      format.json { render json: { status: 'success' }, status: :ok }&lt;br /&gt;
    end&lt;br /&gt;
  rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
    flash[:error] = &amp;quot;Failed to update priorities: #{e.message}&amp;quot;&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
      format.json { render json: { status: 'error', message: e.message }, status: :unprocessable_entity }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Client Side Changes to Support &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Changes ====&lt;br /&gt;
After tidying up the server-side logic, we needed to refactor the client-side code to ensure it passed the correct information from the view to the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action. On the client side, we now grab the topicIDs and send them as &amp;lt;code&amp;gt;selected_topic_ids&amp;lt;/code&amp;gt;, allowing the controller to figure out which topics were selected and in what order.  &lt;br /&gt;
&lt;br /&gt;
We also extracted the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, making it a lot easier to send data with AJAX and keeping things flexible. When working with jQuery, AJAX is awesome because it lets us update the server without reloading the page, making the user experience smooth and responsive. When we pull the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, we can grab it along with other data, like the list of topics, and include it in the request. This way, we don’t have to rely on the URL to know which participant we’re working with, which makes the code cleaner and more adaptable.&lt;br /&gt;
&lt;br /&gt;
If we were to rely on the route (as opposed to using the DOM) for the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt;, every AJAX request would need to be tied to a specific URL structure. That’s limiting, especially if the route doesn’t naturally include the participant ID, as can be the case with collection routes versus member routes in Rails. &lt;br /&gt;
&lt;br /&gt;
Note: One interesting aspect of the application was the mix of JavaScript and CoffeeScript code. It wasn't clear why both types of code were used.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery -&amp;gt;&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: -&amp;gt;&lt;br /&gt;
      $.post($(this).data('update-url'), $(this).sortable('serialize'))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery(document).ready ($) -&amp;gt;&lt;br /&gt;
  sendUpdatedOrder = -&amp;gt;&lt;br /&gt;
    sortedIDs = $(this).sortable('toArray')&lt;br /&gt;
&lt;br /&gt;
    topicIDs = sortedIDs.map (id) -&amp;gt;&lt;br /&gt;
      id.replace 'topic_', ''&lt;br /&gt;
&lt;br /&gt;
    participant_id = $(this).data('participant-id')&lt;br /&gt;
&lt;br /&gt;
    assignment_id = new URL($(this).data('update-url'), window.location.origin).searchParams.get('assignment_id')&lt;br /&gt;
&lt;br /&gt;
    $.ajax&lt;br /&gt;
      type: 'POST'&lt;br /&gt;
      url: $(this).data('update-url')&lt;br /&gt;
      data:&lt;br /&gt;
        topic: topicIDs&lt;br /&gt;
        participant_id: participant_id&lt;br /&gt;
        assignment_id: assignment_id&lt;br /&gt;
      success: (response) -&amp;gt;&lt;br /&gt;
        console.log 'Selections updated successfully.'&lt;br /&gt;
      error: (xhr, status, error) -&amp;gt;&lt;br /&gt;
        alert 'An error occurred while saving your selections. Please try again.'&lt;br /&gt;
        console.error error&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: sendUpdatedOrder&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-1.-signupsheetcontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; Updates ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; method renders the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; action in &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt;. However, the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; does not include the controller action for this view. We need to find the old implementation of the controller action, add it back, and review and update it for any DRY violations.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # provides variables for reviewing page located at views/review_bids/others_work.html.erb&lt;br /&gt;
  def index&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
    return unless current_user_id?(@participant.user_id)&lt;br /&gt;
&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
    # Finding how many reviews have been completed&lt;br /&gt;
    @num_reviews_completed = 0&lt;br /&gt;
    @review_mappings.each do |map|&lt;br /&gt;
      @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # render view for completing reviews after review bidding has been completed&lt;br /&gt;
    render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
  end  &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-2.-reviewbidsotherswork-view-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;review_bids_others_work.html.erb&amp;lt;/code&amp;gt; View Updates ====&lt;br /&gt;
&lt;br /&gt;
The code only contains a view with this name and no associated controller action. Reviewing the code in the view, we noticed some SRP violations where there is much logic in the presentation layer. Set up helper methods in the controller to handle some of that logic or see if the model can handle some of that logic.&lt;br /&gt;
&lt;br /&gt;
Note: The view is technically part of the SignUpSheetController, but it's being rendered by the ReviewBidsController, which initially threw us off. After chatting with the professor, we decided not to change this view. He indicated that the plan is to reuse these views for both assignment bidding and review bidding.&lt;br /&gt;
&lt;br /&gt;
We would suggest building the assignment bidding and review bidding features separately first to make sure each one works on its own. Once that’s done, we can go back and clean things up by combining any shared functionality. This could include creating shared views or even introducing an abstract model to handle both types of bidding in a cleaner way. It could also mean replacing the ReviewBidsController with something more generic, like a BidsController, to keep things reusable and easier to maintain down the line.&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Rename &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; method ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; should be &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt;&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:''' Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The primary activity here is to refactor the method name &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; and fix the hardcoded URL. However, the match topic bidding algorithm no longer works since Heroku is no longer free, per the professor. The reference link below has detailed information about the web service that we can leverage to gain that understanding.&lt;br /&gt;
&lt;br /&gt;
Reference Link for Webservice Details: [https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Updated Implementation ====&lt;br /&gt;
The original requirement was to rename the run_bidding_algorithm method to assign_reviewers, but after digging into the code, we realized that renaming it didn’t make sense. The method runs the bidding algorithm to match topics, and there’s already a separate method to handle assigning review topics.&lt;br /&gt;
&lt;br /&gt;
However, we improved the logic in the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; method (which calls the private &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt;) by introducing a separate &amp;lt;code&amp;gt;BiddingAlgorithmService&amp;lt;/code&amp;gt; object. This keeps the business logic out of the controller action, which makes things cleaner and more maintainable.&lt;br /&gt;
&lt;br /&gt;
==== review_bids_controller ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def run_bidding_algorithm&lt;br /&gt;
    review_bid = ReviewBid.new&lt;br /&gt;
    bidding_data = review_bid.bidding_data&lt;br /&gt;
    matched_topics = BiddingAlgorithmService.new(bidding_data).run&lt;br /&gt;
    raise ArgumentError, 'Failed to assign reviewers. Please try again later.' unless matched_topics&lt;br /&gt;
&lt;br /&gt;
    matched_topics&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== bidding_algorithm_service ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# frozen_string_literal: true&lt;br /&gt;
&lt;br /&gt;
# The `BiddingAlgorithmService` run the bid assignment algorithm&lt;br /&gt;
# Sends student IDs, topic IDs, student preferences, and timestamps to the web service&lt;br /&gt;
# The web service returns the matched assignments in the JSON response body&lt;br /&gt;
class BiddingAlgorithmService&lt;br /&gt;
  SERVICE_URL = 'http://app-csc517.herokuapp.com/match_topics'.freeze&lt;br /&gt;
&lt;br /&gt;
  # MOCK_DATA is based on the documentation detailing how the webservice behaves.&lt;br /&gt;
  # Reference: https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review#Webservice&lt;br /&gt;
  MOCK_DATA = {&lt;br /&gt;
    36239 =&amp;gt; [3970, 3972, 3975],&lt;br /&gt;
    36240 =&amp;gt; [3973, 3974, 3972],&lt;br /&gt;
    36241 =&amp;gt; [3969, 3971, 3972],&lt;br /&gt;
    36242 =&amp;gt; [3969, 3971, 3973],&lt;br /&gt;
    36243 =&amp;gt; [3969, 3970, 3971]&lt;br /&gt;
  }.freeze&lt;br /&gt;
&lt;br /&gt;
  def initialize(bidding_data)&lt;br /&gt;
    @bidding_data = bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def run&lt;br /&gt;
    return MOCK_DATA if Rails.application.config.use_mock_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
    perform_request&lt;br /&gt;
  rescue RestClient::ExceptionWithResponse, JSON::ParserError&lt;br /&gt;
    false&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def perform_request&lt;br /&gt;
    response = RestClient.post(&lt;br /&gt;
      SERVICE_URL,&lt;br /&gt;
      @bidding_data.to_json,&lt;br /&gt;
      content_type: :json,&lt;br /&gt;
      accept: :json&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    validate_response(response)&lt;br /&gt;
    JSON.parse(response.body)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def validate_response(response)&lt;br /&gt;
    raise 'Invalid response format' unless response.headers[:content_type] &amp;amp;&amp;amp; response.headers[:content_type].include?('application/json')&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Resolve Functionality Issues for Non Bidders ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Currently it doesn't work if some student does not bid. In this case, algorithm needs to be fixed to ignore anyone who didn’t bid, and let them choose from what’s left over.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to do several things within this controller. We will set a default state for non-bidders that we can point to to give them options to select topics to review from the leftovers. We will consolidate &amp;lt;code&amp;gt;@sign_up_topics&amp;lt;/code&amp;gt; to resolve the DRY violation. Lastly, we will remove business logic from the show method and push it to a new helper to conform to the SRP.&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # Additional method&lt;br /&gt;
&lt;br /&gt;
  # Provides variables for review bidding page&lt;br /&gt;
  def show&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id].to_i)&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @sign_up_topics = SignUpTopic.where(assignment_id: @assignment.id, private_to: nil)&lt;br /&gt;
    my_topic = SignedUpTeam.topic_id(@participant.parent_id, @participant.user_id)&lt;br /&gt;
    @sign_up_topics -= SignUpTopic.where(assignment_id: @assignment.id, id: my_topic)&lt;br /&gt;
    @num_participants = AssignmentParticipant.where(parent_id: @assignment.id).count&lt;br /&gt;
    @selected_topics = nil # This is used to list the topics assigned to review (i.e., select == assigned)&lt;br /&gt;
    @bids = ReviewBid.where(participant_id: @participant, assignment_id: @assignment.id)&lt;br /&gt;
    signed_up_topics = []&lt;br /&gt;
    &lt;br /&gt;
    @bids.each do |bid|&lt;br /&gt;
      sign_up_topic = SignUpTopic.find_by(id: bid.signuptopic_id)&lt;br /&gt;
      signed_up_topics &amp;lt;&amp;lt; sign_up_topic if sign_up_topic&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    signed_up_topics &amp;amp;= @sign_up_topics&lt;br /&gt;
    @sign_up_topics -= signed_up_topics&lt;br /&gt;
    @bids = signed_up_topics&lt;br /&gt;
    @num_of_topics = @sign_up_topics.size&lt;br /&gt;
    @assigned_review_maps = []&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: @assignment.id, reviewer_id: @participant.id).each do |review_map|&lt;br /&gt;
      @assigned_review_maps &amp;lt;&amp;lt; review_map&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Explicitly render view since it's in the sign up sheet views&lt;br /&gt;
    render 'sign_up_sheet/review_bids_show'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Additional methods&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-2.-reviewbidshelper-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
 # Assigns bidding topics to reviewers&lt;br /&gt;
  def assign_bidding&lt;br /&gt;
      assignment = validate_assignment(params[:assignment_id])&lt;br /&gt;
&lt;br /&gt;
      reviewers = validate_reviewers(assignment.id)&lt;br /&gt;
      reviewer_ids = reviewers.map(&amp;amp;:id)&lt;br /&gt;
&lt;br /&gt;
      matched_topics = run_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
      if matched_topics.blank?&lt;br /&gt;
        flash[:alert] = 'Topic or assignment is missing'&lt;br /&gt;
        redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      leftover_topics = find_leftover_topics(assignment.id, matched_topics)&lt;br /&gt;
      assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.new.assign_review_topics(matched_topics)&lt;br /&gt;
      assignment.update!(can_choose_topic_to_review: false)&lt;br /&gt;
&lt;br /&gt;
      flash[:notice] = 'Reviewers were successfully assigned to topics.'&lt;br /&gt;
      redirect_back fallback_location: root_path&lt;br /&gt;
    rescue ArgumentError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ArgumentError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: e.message&lt;br /&gt;
    rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::RecordInvalid: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue ActiveRecord::ActiveRecordError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::ActiveRecordError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue StandardError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;StandardError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers. Please try again later.'&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  #service methods here ...&lt;br /&gt;
&lt;br /&gt;
  def find_leftover_topics(assignment_id, matched_topics)&lt;br /&gt;
    all_topic_ids = SignUpTopic.where(assignment_id: assignment_id).pluck(:id)&lt;br /&gt;
    assigned_topic_ids = matched_topics.map { |match| match[:topic_id] }&lt;br /&gt;
&lt;br /&gt;
    # Calculate leftover topics&lt;br /&gt;
    all_topic_ids - assigned_topic_ids&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
    return if leftover_topics.blank?&lt;br /&gt;
&lt;br /&gt;
    # Find non-bidders by excluding already matched reviewers&lt;br /&gt;
    non_bidders = reviewer_ids - matched_topics.keys&lt;br /&gt;
&lt;br /&gt;
    # Assign leftover topics to non-bidders in a round-robin fashion&lt;br /&gt;
    non_bidders.each_with_index do |reviewer_id, index|&lt;br /&gt;
      topic_id = leftover_topics[index % leftover_topics.length]&lt;br /&gt;
      ReviewBid.create(priority: 1, signuptopic_id: topic_id, participant_id: reviewer_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirm Functionality for Single-person Teams ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Make sure your code works on individual assignments, i.e., assignments where participants sign up for topics instead of teams. In this case, a team is created for each participant when they sign up. So the code should work for assignments to which either individuals or teams submit.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to make an update to focus on topic rather than team or have it where teams are updated based on topic id.We will probably utilize sign up topic or a similar object to accomplish this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-5-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bid Status Messaging ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| To make the functionality more intuitive, include a message to say how many students are eligible to submit bids, how many have submitted their bids, when the deadline for submitting bids is.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
The logic for these messages will go into the helper file. It is effectively the same reasoning as our migration of other business/functionality code from the controller to the helper to conform to the SRP. It will go hand in hand with the SignUpTopic portion of the code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-6-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Why are the methods in review_bid.rb class methods? Can we change them to instance methods or move it to helpers?&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-1.-reviewbid-model-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model Updates ====&lt;br /&gt;
Since the current class methods are tied to &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model instances, we will change them to instance methods. There is no need to move them to helpers.&lt;br /&gt;
&lt;br /&gt;
===== Review Bid Model Flow Before Refactoring =====&lt;br /&gt;
This sequence diagram describes how the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; interacts with the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model in assigning review topics to reviewers according to their bids. Once the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; action is initiated by the user, the controller gathers the IDs of reviewers and calls &amp;lt;code&amp;gt;ReviewBid.bidding_data&amp;lt;/code&amp;gt; to gather bidding information. That is sent out to an external bidding algorithm, and the matched topics that come back are used by &amp;lt;code&amp;gt;ReviewBid.assign_review_topics&amp;lt;/code&amp;gt; to create &amp;lt;code&amp;gt;ReviewResponseMap&amp;lt;/code&amp;gt; entries, assigning topics to reviewers for the assignment.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
  belongs_to :assignment, class_name: 'Assignment'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  def self.bidding_data(assignment_id, reviewer_ids)&lt;br /&gt;
    # create basic hash and set basic hash data&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'users' =&amp;gt; {}, 'max_accepted_proposals' =&amp;gt; [] }&lt;br /&gt;
    bidding_data['tid'] = SignUpTopic.where(assignment_id: assignment_id).ids&lt;br /&gt;
    bidding_data['max_accepted_proposals'] = Assignment.where(id: assignment_id).pluck(:num_reviews_allowed).first&lt;br /&gt;
&lt;br /&gt;
    # loop through reviewer_ids to get reviewer specific bidding data&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      bidding_data['users'][reviewer_id] = reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # assigns topics to reviews as matched by the webservice algorithm&lt;br /&gt;
  def self.assign_review_topics(assignment_id, reviewer_ids, matched_topics, _min_num_reviews = 2)&lt;br /&gt;
    # if review response map already created, delete it&lt;br /&gt;
    if ReviewResponseMap.where(reviewed_object_id: assignment_id)&lt;br /&gt;
      ReviewResponseMap.where(reviewed_object_id: assignment_id).destroy_all&lt;br /&gt;
    end&lt;br /&gt;
    # loop through reviewer_ids to assign reviews to each reviewer&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      topics_to_assign = matched_topics[reviewer_id.to_s]&lt;br /&gt;
      topics_to_assign.each do |topic|&lt;br /&gt;
        assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method to assign a single topic to a reviewer&lt;br /&gt;
  def self.assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic).pluck(:team_id).first&lt;br /&gt;
    team_to_review.nil? ? [] : ReviewResponseMap.create(reviewed_object_id: assignment_id, reviewer_id: reviewer_id, reviewee_id: team_to_review, type: 'ReviewResponseMap')&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method for getting individual reviewer_ids bidding data&lt;br /&gt;
  # returns user's bidding data hash&lt;br /&gt;
  def self.reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    reviewer_user_id = AssignmentParticipant.find(reviewer_id).user_id&lt;br /&gt;
    self_topic = SignedUpTeam.topic_id(assignment_id, reviewer_user_id)&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'otid' =&amp;gt; self_topic, 'priority' =&amp;gt; [], 'time' =&amp;gt; [] }&lt;br /&gt;
    bids = ReviewBid.where(participant_id: reviewer_id)&lt;br /&gt;
&lt;br /&gt;
    # loop through each bid for a topic to get specific data&lt;br /&gt;
    bids.each do |bid|&lt;br /&gt;
      bidding_data['tid'] &amp;lt;&amp;lt; bid.signuptopic_id&lt;br /&gt;
      bidding_data['priority'] &amp;lt;&amp;lt; bid.priority&lt;br /&gt;
      bidding_data['time'] &amp;lt;&amp;lt; bid.updated_at&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  # Instance method to get bidding data for this review bid&lt;br /&gt;
  def bidding_data&lt;br /&gt;
    {&lt;br /&gt;
      'tid' =&amp;gt; topic_ids,&lt;br /&gt;
      'otid' =&amp;gt; self_topic,&lt;br /&gt;
      'priority' =&amp;gt; priorities,&lt;br /&gt;
      'time' =&amp;gt; updated_times&lt;br /&gt;
    }&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # bid object to limit db touches for similar data acquisitions&lt;br /&gt;
  def bids&lt;br /&gt;
    @bids ||= ReviewBid.where(participant_id: participant_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def topic_ids&lt;br /&gt;
    bids.pluck(:signuptopic_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def priorities&lt;br /&gt;
    bids.pluck(:priority)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def updated_times&lt;br /&gt;
    bids.pluck(:updated_at)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self_topic&lt;br /&gt;
    return nil unless topic &amp;amp;&amp;amp; topic.assignment_id&lt;br /&gt;
&lt;br /&gt;
    SignedUpTeam.topic_id(topic.assignment_id, participant.user_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign topics to reviews&lt;br /&gt;
  def assign_review_topics(matched_topics)&lt;br /&gt;
    validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: topic.assignment_id).destroy_all&lt;br /&gt;
&lt;br /&gt;
    matched_topics.each do |reviewer_id, topics|&lt;br /&gt;
      Array(topics).each { |topic_id| assign_topic_to_reviewer(reviewer_id, topic_id) }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign a single topic to a reviewer&lt;br /&gt;
  def assign_topic_to_reviewer(reviewer_id, topic_id)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic_id).pluck(:team_id).first&lt;br /&gt;
    return if team_to_review.nil?&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.create(&lt;br /&gt;
      reviewed_object_id: topic.assignment_id,&lt;br /&gt;
      reviewer_id: reviewer_id,&lt;br /&gt;
      reviewee_id: team_to_review,&lt;br /&gt;
      type: 'ReviewResponseMap'&lt;br /&gt;
    )&lt;br /&gt;
    # error handling&lt;br /&gt;
  rescue StandardError =&amp;gt; e&lt;br /&gt;
    Rails.logger.error(&amp;quot;Failed to assign topic #{topic_id} to reviewer #{reviewer_id}: #{e.message}&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
    raise ArgumentError, 'Topic or assignment is missing' if topic.nil? || topic.assignment_id.nil?&lt;br /&gt;
    raise ArgumentError, 'Matched topics must be a Hash' unless matched_topics.is_a?(Hash)&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
Once we changed them to instance methods, we modified their use in the controller code to instantiate the object first.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-3.-schema.db-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;Schema.db&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We also need to remove the foreign key reference to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model since the &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model already has access to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;, and that is the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; we should be referencing. This will also involve modifying the database to remove the foreign key reference to the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Steps required:&lt;br /&gt;
&lt;br /&gt;
# Review the records in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table in the database to see if there are any records and if those records have values for &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;&lt;br /&gt;
# Create a migration file using the command: &amp;lt;code&amp;gt;rails generate migration RemoveAssignmentFromReviewBids&amp;lt;/code&amp;gt;&lt;br /&gt;
# In the generated file, indicate the change that needs to be made using the change method and migration columns &amp;lt;code&amp;gt;remove_foreign_key&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;remove_column&amp;lt;/code&amp;gt;. Migration should be straightforward since there is no cascading behavior (i.e., &amp;lt;code&amp;gt;dependent:destroy&amp;lt;/code&amp;gt;) to worry about.&lt;br /&gt;
# Once comfortable with the updates to the generated file, run the migration using the &amp;lt;code&amp;gt;rails db:migrate&amp;lt;/code&amp;gt; command.&lt;br /&gt;
# Run a query in the DB to ensure no orphaned records are in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table after the updates.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-4.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implement Edge Case Testing ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| In the previous implementation wiki, there are edge cases which are not exhaustively tested. Should test those edge cases thoroughly and add more edge case testing - link&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span class=&amp;quot;mark&amp;quot;&amp;gt;TODO&amp;lt;/span&amp;gt;:We just got initial test results back. We still need to figure out why tests are failing and what may be slipping through the cracks.[[File:Test expertiza.png|411x459px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-8-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design Patterns and Principles ===&lt;br /&gt;
Below are the design patterns and principles we plan to use to address the problem statements outlined in the project requirements:&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
** Controller Responsibility&lt;br /&gt;
*** To address the issue of authentication logic residing in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, we will refactor the code to ensure the controller solely handles request and response flows.&lt;br /&gt;
*** Authentication logic will be moved to dedicated methods in &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt;, making the codebase easier to maintain and test.&lt;br /&gt;
** View Simplification&lt;br /&gt;
*** Currently, the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; view contains excessive business logic, violating SRP.&lt;br /&gt;
*** We will move this logic into helper methods or models, ensuring each component has a single responsibility and improving code readability.&lt;br /&gt;
&lt;br /&gt;
* Separation of Concerns (SoC)&lt;br /&gt;
** By relocating authorization logic out of the controller, we separate authentication from request handling.&lt;br /&gt;
** Adhering to SoC enhances readability and maintainability by allowing each part of the application to focus on its specific role.&lt;br /&gt;
* Don't Repeat Yourself (DRY)&lt;br /&gt;
** We will inspect the existing implementation for code duplication, particularly in the controller actions.&lt;br /&gt;
** Any repetitive code will be refactored or removed to reduce redundancy and improve clarity.&lt;br /&gt;
&lt;br /&gt;
* Model-View-Controller (MVC) Adherence&lt;br /&gt;
** Moving business logic from the view aligns with MVC best practices.&lt;br /&gt;
** By ensuring the view only presents data without processing it, we maintain clear boundaries between the model, view, and controller layers.&lt;br /&gt;
&lt;br /&gt;
* Dependency Inversion Principle (DIP)&lt;br /&gt;
** We plan to update class methods to instance methods, allowing the controller to depend on abstractions rather than concrete implementations.&lt;br /&gt;
** This change leverages Ruby's duck typing, providing flexibility and making the code easier to maintain and test.&lt;br /&gt;
&lt;br /&gt;
** Clear and intentional naming during refactoring will enhance code clarity and maintainability.&lt;br /&gt;
&lt;br /&gt;
== Files Modified/Added ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-1.-controllers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
review_bids_controller.rb&lt;br /&gt;
&lt;br /&gt;
signup_sheet_controller.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-2.-models&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
review_bid.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-3.-helpers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
&lt;br /&gt;
authorization_helper.rb&lt;br /&gt;
&lt;br /&gt;
Review_bids_helper.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;viii.-test-plan&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
The Excel spreadsheet below details our test plan for testing the code we changed or created to ensure code coverage. The process involved reviewing the code and determining all the possible scenarios that needed to be covered based on what the method was doing, then creating test cases accordingly.&lt;br /&gt;
&lt;br /&gt;
* [https://docs.google.com/spreadsheets/d/1iHzOdSwlTtxOtf5VsNdwOaCP8NzYMmiJ/edit?usp=drive_link Open Test Matrix Spreadsheet]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/review_bids_controller_spec.rb ReviewBidsController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/sign_up_sheet_controller_spec.rb SignUpSheetController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/models/review_bid_spec.rb ReviewBid model updated test cases]&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-1.-mentor&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Ed Gehringer&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-2.-members&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Members ===&lt;br /&gt;
&lt;br /&gt;
* Gavin Teague&lt;br /&gt;
* Janice Uwujaren&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xi.-links&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
Previous Wiki: [https://wiki.expertiza.ncsu.edu/index.php/CSC/ECE_517_Fall_2021_-_E2151._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;Link&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xii.-references&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/document/d/1LHVacylQmB15I6VcYUA5nuC7GRRU9szHzZhjlPcE-PA/edit?tab=t.0#heading=h.u0oycoj6e0ev &amp;lt;u&amp;gt;Project Instructions&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;appendix-a-uml-class-diagram-plantuml-code-for-review-bids&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Juwujar</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160985</id>
		<title>CSC/ECE 517 Fall 2024 - E2485. Allow reviewers to bid on what to review</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160985"/>
		<updated>2024-12-11T01:39:12Z</updated>

		<summary type="html">&lt;p&gt;Juwujar: /* Optimize ReviewBidsController#set_priority Action functionality */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
This project will fix the defects and improve the functionality in Expertiza of the Reviewer Bidding feature, a process where reviewers can bid for assignments they want to review. It will make the functionality smoother, more reliable, and align with Expertiza's collaborative learning objectives.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iii.-problem-statements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Problem Statements ==&lt;br /&gt;
&lt;br /&gt;
Assigning reviews to users on Expertiza involves a complex bidding algorithm that requires optimization before production deployment. Below are key areas we plan to address as per the project requirements:&lt;br /&gt;
* Code Refactoring and Best Practices&lt;br /&gt;
** '''Utilize Authentication Utilities''': Modify the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; function in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to use existing authentication utilities, enhancing code reusability and adhering to the Single Responsibility Principle (SRP) and Separation of Concerns (SoC).&lt;br /&gt;
** '''Eliminate Code Duplication''': Refactor &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; to comply with the Don't Repeat Yourself (DRY) principle.&lt;br /&gt;
** '''Rename Functions for Clarity''': Change &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; to better reflect its purpose.&lt;br /&gt;
** '''Refactor Methods in '''&amp;lt;code&amp;gt;review_bid.rb&amp;lt;/code&amp;gt;''':''': Convert class methods to instance methods or relocate them to helper classes for improved code organization.&lt;br /&gt;
* Functionality Enhancements&lt;br /&gt;
** '''Handle Missing Bids Gracefully''': Adjust the bidding algorithm to accommodate students who do not bid, allowing them to select from the remaining options.&lt;br /&gt;
** '''Support Both Individual and Team Assignments''': Ensure the code functions correctly for participant and team-based assignments when signing up for topics.&lt;br /&gt;
* User Interface Improvements&lt;br /&gt;
** '''Increase Transparency in the Bidding Process''': Display messages indicating how many students are eligible to bid, how many have submitted bids, and the deadline.&lt;br /&gt;
** '''Accurate Bid Status Indicators''': Ensure topic colors change correctly based on the number of outstanding bids, providing clear visual feedback to users.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Exhaustive Edge Case Testing''': Conduct thorough testing for edge cases not previously covered, adding additional test cases as needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iv.-design-goals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Design Goals ==&lt;br /&gt;
&lt;br /&gt;
While addressing the existing defects and enhancing the Reviewer Bidding feature, we plan to adhere to the following design guidelines:&lt;br /&gt;
&lt;br /&gt;
* Code Quality and Maintainability&lt;br /&gt;
** '''Improve Code Structure: '''Implement refactoring strategies to enhance code readability and maintainability, ensuring adherence to best practices like the DRY principle.&lt;br /&gt;
** '''Standardize Authentication Logic: '''Utilize a centralized authentication utility to promote consistency and reusability across the application.&lt;br /&gt;
* System Stability and Flexibility&lt;br /&gt;
** '''Enhance Algorithm Robustness: '''Modify the bidding algorithm to handle scenarios where students do not bid, ensuring the system remains stable under all conditions.&lt;br /&gt;
** '''Ensure Assignment Versatility: '''Design the system to seamlessly support both individual and team assignments without additional configuration.&lt;br /&gt;
* User Experience Enhancements&lt;br /&gt;
** '''Increase Bidding Transparency: '''Develop user interface elements that clearly display bidding status, deadlines, and participation metrics.&lt;br /&gt;
** '''Implement Dynamic Visual Feedback: '''Use real-time visual cues, such as color changes, to inform users about the bidding status instantly.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Develop Comprehensive Test Suites: '''Create extensive automated tests covering edge cases and critical functionalities to guarantee reliable system behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;v.-uml-diagram&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== UML Diagram ==&lt;br /&gt;
&lt;br /&gt;
Below is a visual representation of the models associated with the review bids functionality, depicting the data structures and their relationships.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml class diagram2.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi.-implementation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
'''ReviewBid: '''The &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; manages the bidding process, where participants indicate their preferences for reviewing different topics or assignments. This helps distribute review tasks fairly based on participants' interests.&lt;br /&gt;
&lt;br /&gt;
'''Assignment: '''The &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model represents team or individual assignments in Expertiza. It stores all the essential information required to keep the assignment process organized, like managing participants, setting due dates, forming teams, and overseeing the review process.&lt;br /&gt;
&lt;br /&gt;
'''SignUpTopic: '''The &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt; model identifies specific topics or areas within an assignment for which participants can sign up. It manages the availability of these topics, tracks bids, coordinates team sign-ups, and assigns topics based on both bids and participant availability. &lt;br /&gt;
&lt;br /&gt;
'''Participant: '''The &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model is about the individual users who participate in an assignment. This includes students, instructors, teaching assistants, or other relevant roles. This model is responsible for managing key user information, monitoring interactions with the assignment, keeping track of team memberships, and recording things like badges and review grades.&lt;br /&gt;
&lt;br /&gt;
== Implementation ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1.-decoupling-authorization-logic-from-reviewbidscontroller&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Decoupling Authorization Logic from &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| '''&amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; needs to use the authentication utilities'''&lt;br /&gt;
&lt;br /&gt;
The function &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; should use the available authentication utilities to support the Single Responsibility Principle and Separation of Concerns and to support code reusability.&lt;br /&gt;
&lt;br /&gt;
| '''Authentication Consistency:''' Remove authentication logic in the controller and call logic in the authentication utility class instead to ensure consistent use of the same logic across the application, foster reusability, and eliminate DRY.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
==== Authorization Flow Before Refactoring ====&lt;br /&gt;
The sequence diagram depicts the authorization process within the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;. When a user invokes an action, the controller checks if the action is allowed by verifying the user's role through the &amp;lt;code&amp;gt;current_role_name()&amp;lt;/code&amp;gt; method in the &amp;lt;code&amp;gt;ApplicationController&amp;lt;/code&amp;gt;. Depending on the action and the user's role, the controller either authorizes the user to proceed or denies access, ultimately executing the action logic if authorized and responding to the user.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 1.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We simplified the &amp;lt;code&amp;gt;action_allowed?&amp;lt;/code&amp;gt; method by decoupling authorization logic from the controller to use helper methods in the &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt; module. The &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; should not handle authentication, which is a violation of SRP. &lt;br /&gt;
&lt;br /&gt;
Note: We also made similar updates to the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; methods for &amp;lt;code&amp;gt;SignUpController&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;vertical-align:top;&amp;quot; | Original Code !! style=&amp;quot;vertical-align:top;&amp;quot; | Refactored Code&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index'&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator',&lt;br /&gt;
     'Student'].include? current_role_name and&lt;br /&gt;
        ((%w[list].include? action_name) ? are_needed_authorizations_present?(params[:id], &amp;quot;participant&amp;quot;, &amp;quot;reader&amp;quot;, &amp;quot;submitter&amp;quot;, &amp;quot;reviewer&amp;quot;) : true)&lt;br /&gt;
  else&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator'].include? current_role_name&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index', 'list'&lt;br /&gt;
    current_user_has_student_privileges? &amp;amp;&amp;amp; list_authorization_check&lt;br /&gt;
  else&lt;br /&gt;
    current_user_has_ta_privileges?&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; action ===&lt;br /&gt;
Previously, the &amp;lt;code&amp;gt;ReviewBidsController's index&amp;lt;/code&amp;gt; action was overloaded with responsibilities—it handled everything from locating and validating assignments to fetching review mappings and counting completed reviews. This all-in-one approach made the controller action messy. &lt;br /&gt;
&lt;br /&gt;
We have moved the business logic from the controller action and into dedicated service objects. With this change, we introduced two key service objects: &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; focuses on retrieving the participant along with their assignments, while the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; takes on the task of managing review mappings and calculating review-related stats, like how many reviews have been completed. &lt;br /&gt;
&lt;br /&gt;
This separation of responsibilities aligns with the Single Responsibility Principle. The logic for fetching assignments, retrieving review mappings, and counting reviews is now more flexible and reusable throughout the application. For example, we used methods in the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; in both the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; and the &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;, helping to avoid code duplication.The article https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial served as a guide on when and how to use service objects in Rails.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
  return unless current_user_id?(@participant.user_id)&lt;br /&gt;
  @assignment = @participant.assignment&lt;br /&gt;
  @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
  # Finding how many reviews have been completed&lt;br /&gt;
  @num_reviews_completed = 0&lt;br /&gt;
  @review_mappings.each do |map|&lt;br /&gt;
    @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # render view for completing reviews after review bidding has been completed&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @assignment = participant_service.assignment&lt;br /&gt;
  unless @assignment.is_a?(Assignment)&lt;br /&gt;
    flash[:error] = 'Assignment not found.'&lt;br /&gt;
    redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  @review_mappings = review_service.review_mappings&lt;br /&gt;
  @num_reviews_completed = review_service.review_counts[:completed]&lt;br /&gt;
&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Optimize &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Action functionality ===&lt;br /&gt;
In the earlier version of the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, the server only needed a couple of key pieces of information: an array of topic IDs (params[:topic]) and the participant ID (params[:id}). From there, it would figure out the assignment_id by using the first topic as a reference. This is risky because the &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt; associated with the topic may not be a valid &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
With the new implementation, we structured the parameters to include the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt;, an array of topic IDs, and &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;. This change ensures that the server always receives complete and reliable information, preventing issues caused by missing or inconsistent data. It also removes the need for the server to perform unnecessary lookups, as in the case of &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;, allowing the controller to focus on receiving requests and rendering appropriate responses.&lt;br /&gt;
&lt;br /&gt;
While these updates have cleaned up the set_priority action, they still contain a lot of business logic, like validations and priority updates. To make the code even more maintainable, there’s room for further refactoring—perhaps moving some of this logic into a service class or model method to streamline the controller even more.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | Old Code&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    if params[:topic].nil?&lt;br /&gt;
      ReviewBid.where(participant_id: params[:id]).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      participant = AssignmentParticipant.find_by(id: params[:id])&lt;br /&gt;
      assignment_id = SignUpTopic.find(params[:topic].first).assignment.id&lt;br /&gt;
      @bids = ReviewBid.where(participant_id: params[:id])&lt;br /&gt;
      signed_up_topics = ReviewBid.where(participant_id: params[:id]).map(&amp;amp;:signuptopic_id)&lt;br /&gt;
      signed_up_topics -= params[:topic].map(&amp;amp;:to_i)&lt;br /&gt;
      signed_up_topics.each do |topic|&lt;br /&gt;
        ReviewBid.where(signuptopic_id: topic, participant_id: params[:id]).destroy_all&lt;br /&gt;
      end&lt;br /&gt;
      params[:topic].each_with_index do |topic_id, index|&lt;br /&gt;
        bid_existence = ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id])&lt;br /&gt;
        if bid_existence.empty?&lt;br /&gt;
          ReviewBid.create(priority: index + 1, signuptopic_id: topic_id, participant_id: params[:id], assignment_id: assignment_id)&lt;br /&gt;
        else&lt;br /&gt;
          ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id]).update_all(priority: index + 1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to action: 'show', assignment_id: params[:assignment_id], id: params[:id]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    participant_id = params[:participant_id].to_i&lt;br /&gt;
    selected_topic_ids = params[:topic]&amp;amp;.map(&amp;amp;:to_i) || []&lt;br /&gt;
    assignment_id = params[:assignment_id].to_i&lt;br /&gt;
&lt;br /&gt;
    unless current_user_can_modify_participant?(participant_id)&lt;br /&gt;
      flash[:error] = 'You are not authorized to modify these bids.'&lt;br /&gt;
      respond_to do |format|&lt;br /&gt;
        format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
        format.json { render json: { status: 'unauthorized' }, status: :unauthorized }&lt;br /&gt;
      end&lt;br /&gt;
      return&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    if selected_topic_ids.empty?&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      assignment = Assignment.find_by(id: assignment_id)&lt;br /&gt;
      if assignment.nil?&lt;br /&gt;
        flash[:error] = &amp;quot;Invalid assignment.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_assignment' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      unless SignUpTopic.where(id: selected_topic_ids, assignment_id: assignment_id).count == selected_topic_ids.size&lt;br /&gt;
        flash[:error] = &amp;quot;One or more selected topics are invalid.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_topics' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).where.not(signuptopic_id: selected_topic_ids).destroy_all&lt;br /&gt;
&lt;br /&gt;
      selected_topic_ids.each_with_index do |topic_id, index|&lt;br /&gt;
        bid = ReviewBid.find_or_initialize_by(signuptopic_id: topic_id, participant_id: participant_id)&lt;br /&gt;
        bid.priority = index + 1&lt;br /&gt;
        bid.assignment_id = assignment_id&lt;br /&gt;
        bid.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_to action: 'show', assignment_id: assignment_id, id: participant_id, notice: 'Review bids updated successfully.' }&lt;br /&gt;
      format.json { render json: { status: 'success' }, status: :ok }&lt;br /&gt;
    end&lt;br /&gt;
  rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
    flash[:error] = &amp;quot;Failed to update priorities: #{e.message}&amp;quot;&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
      format.json { render json: { status: 'error', message: e.message }, status: :unprocessable_entity }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Client Side Changes to Support &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Changes ====&lt;br /&gt;
After tidying up the server-side logic, we needed to refactor the client-side code to ensure it passed the correct information from the view to the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action. On the client side, we now grab the topicIDs and send them as &amp;lt;code&amp;gt;selected_topic_ids&amp;lt;/code&amp;gt;, allowing the controller to figure out which topics were selected and in what order.  &lt;br /&gt;
&lt;br /&gt;
We also extracted the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, making it a lot easier to send data with AJAX and keeping things flexible. When working with jQuery, AJAX is awesome because it lets us update the server without reloading the page, making the user experience smooth and responsive. When we pull the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, we can grab it along with other data, like the list of topics, and include it in the request. This way, we don’t have to rely on the URL to know which participant we’re working with, which makes the code cleaner and more adaptable.&lt;br /&gt;
&lt;br /&gt;
If we were to rely on the route (as opposed to using the DOM) for the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt;, every AJAX request would need to be tied to a specific URL structure. That’s limiting, especially if the route doesn’t naturally include the participant ID, as can be the case with collection routes versus member routes in Rails. &lt;br /&gt;
&lt;br /&gt;
Note: One interesting aspect of the application was the mix of JavaScript and CoffeeScript code. It wasn't clear why both types of code were used.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery -&amp;gt;&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: -&amp;gt;&lt;br /&gt;
      $.post($(this).data('update-url'), $(this).sortable('serialize'))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery(document).ready ($) -&amp;gt;&lt;br /&gt;
  sendUpdatedOrder = -&amp;gt;&lt;br /&gt;
    sortedIDs = $(this).sortable('toArray')&lt;br /&gt;
&lt;br /&gt;
    topicIDs = sortedIDs.map (id) -&amp;gt;&lt;br /&gt;
      id.replace 'topic_', ''&lt;br /&gt;
&lt;br /&gt;
    participant_id = $(this).data('participant-id')&lt;br /&gt;
&lt;br /&gt;
    assignment_id = new URL($(this).data('update-url'), window.location.origin).searchParams.get('assignment_id')&lt;br /&gt;
&lt;br /&gt;
    $.ajax&lt;br /&gt;
      type: 'POST'&lt;br /&gt;
      url: $(this).data('update-url')&lt;br /&gt;
      data:&lt;br /&gt;
        topic: topicIDs&lt;br /&gt;
        participant_id: participant_id&lt;br /&gt;
        assignment_id: assignment_id&lt;br /&gt;
      success: (response) -&amp;gt;&lt;br /&gt;
        console.log 'Selections updated successfully.'&lt;br /&gt;
      error: (xhr, status, error) -&amp;gt;&lt;br /&gt;
        alert 'An error occurred while saving your selections. Please try again.'&lt;br /&gt;
        console.error error&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: sendUpdatedOrder&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-1.-signupsheetcontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; Updates ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; method renders the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; action in &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt;. However, the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; does not include the controller action for this view. We need to find the old implementation of the controller action, add it back, and review and update it for any DRY violations.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # provides variables for reviewing page located at views/review_bids/others_work.html.erb&lt;br /&gt;
  def index&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
    return unless current_user_id?(@participant.user_id)&lt;br /&gt;
&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
    # Finding how many reviews have been completed&lt;br /&gt;
    @num_reviews_completed = 0&lt;br /&gt;
    @review_mappings.each do |map|&lt;br /&gt;
      @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # render view for completing reviews after review bidding has been completed&lt;br /&gt;
    render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
  end  &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-2.-reviewbidsotherswork-view-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;review_bids_others_work.html.erb&amp;lt;/code&amp;gt; View Updates ====&lt;br /&gt;
&lt;br /&gt;
The code only contains a view with this name and no associated controller action. Reviewing the code in the view, we noticed some SRP violations where there is much logic in the presentation layer. Set up helper methods in the controller to handle some of that logic or see if the model can handle some of that logic.&lt;br /&gt;
&lt;br /&gt;
Note: The view is technically part of the SignUpSheetController, but it's being rendered by the ReviewBidsController, which initially threw us off. After chatting with the professor, we decided not to change this view. He indicated that the plan is to reuse these views for both assignment bidding and review bidding.&lt;br /&gt;
&lt;br /&gt;
We would suggest building the assignment bidding and review bidding features separately first to make sure each one works on its own. Once that’s done, we can go back and clean things up by combining any shared functionality. This could include creating shared views or even introducing an abstract model to handle both types of bidding in a cleaner way. It could also mean replacing the ReviewBidsController with something more generic, like a BidsController, to keep things reusable and easier to maintain down the line.&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Rename &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; method ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; should be &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt;&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:''' Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The primary activity here is to refactor the method name &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; and fix the hardcoded URL. However, the match topic bidding algorithm no longer works since Heroku is no longer free, per the professor. The reference link below has detailed information about the web service that we can leverage to gain that understanding.&lt;br /&gt;
&lt;br /&gt;
Reference Link for Webservice Details: [https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Updated Implementation ====&lt;br /&gt;
The original requirement was to rename the run_bidding_algorithm method to assign_reviewers, but after digging into the code, we realized that renaming it didn’t make sense. The method runs the bidding algorithm to match topics, and there’s already a separate method to handle assigning review topics.&lt;br /&gt;
&lt;br /&gt;
However, we improved the logic in the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; method (which calls the private &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt;) by introducing a separate &amp;lt;code&amp;gt;BiddingAlgorithmService&amp;lt;/code&amp;gt; object. This keeps the business logic out of the controller action, which makes things cleaner and more maintainable.&lt;br /&gt;
&lt;br /&gt;
==== review_bids_controller ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def run_bidding_algorithm&lt;br /&gt;
    review_bid = ReviewBid.new&lt;br /&gt;
    bidding_data = review_bid.bidding_data&lt;br /&gt;
    matched_topics = BiddingAlgorithmService.new(bidding_data).run&lt;br /&gt;
    raise ArgumentError, 'Failed to assign reviewers. Please try again later.' unless matched_topics&lt;br /&gt;
&lt;br /&gt;
    matched_topics&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== bidding_algorithm_service ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# frozen_string_literal: true&lt;br /&gt;
&lt;br /&gt;
# The `BiddingAlgorithmService` run the bid assignment algorithm&lt;br /&gt;
# Sends student IDs, topic IDs, student preferences, and timestamps to the web service&lt;br /&gt;
# The web service returns the matched assignments in the JSON response body&lt;br /&gt;
class BiddingAlgorithmService&lt;br /&gt;
  SERVICE_URL = 'http://app-csc517.herokuapp.com/match_topics'.freeze&lt;br /&gt;
&lt;br /&gt;
  # MOCK_DATA is based on the documentation detailing how the webservice behaves.&lt;br /&gt;
  # Reference: https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review#Webservice&lt;br /&gt;
  MOCK_DATA = {&lt;br /&gt;
    36239 =&amp;gt; [3970, 3972, 3975],&lt;br /&gt;
    36240 =&amp;gt; [3973, 3974, 3972],&lt;br /&gt;
    36241 =&amp;gt; [3969, 3971, 3972],&lt;br /&gt;
    36242 =&amp;gt; [3969, 3971, 3973],&lt;br /&gt;
    36243 =&amp;gt; [3969, 3970, 3971]&lt;br /&gt;
  }.freeze&lt;br /&gt;
&lt;br /&gt;
  def initialize(bidding_data)&lt;br /&gt;
    @bidding_data = bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def run&lt;br /&gt;
    return MOCK_DATA if Rails.application.config.use_mock_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
    perform_request&lt;br /&gt;
  rescue RestClient::ExceptionWithResponse, JSON::ParserError&lt;br /&gt;
    false&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def perform_request&lt;br /&gt;
    response = RestClient.post(&lt;br /&gt;
      SERVICE_URL,&lt;br /&gt;
      @bidding_data.to_json,&lt;br /&gt;
      content_type: :json,&lt;br /&gt;
      accept: :json&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    validate_response(response)&lt;br /&gt;
    JSON.parse(response.body)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def validate_response(response)&lt;br /&gt;
    raise 'Invalid response format' unless response.headers[:content_type] &amp;amp;&amp;amp; response.headers[:content_type].include?('application/json')&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Resolve Functionality Issues for Non Bidders ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Currently it doesn't work if some student does not bid. In this case, algorithm needs to be fixed to ignore anyone who didn’t bid, and let them choose from what’s left over.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to do several things within this controller. We will set a default state for non-bidders that we can point to to give them options to select topics to review from the leftovers. We will consolidate &amp;lt;code&amp;gt;@sign_up_topics&amp;lt;/code&amp;gt; to resolve the DRY violation. Lastly, we will remove business logic from the show method and push it to a new helper to conform to the SRP.&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # Additional method&lt;br /&gt;
&lt;br /&gt;
  # Provides variables for review bidding page&lt;br /&gt;
  def show&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id].to_i)&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @sign_up_topics = SignUpTopic.where(assignment_id: @assignment.id, private_to: nil)&lt;br /&gt;
    my_topic = SignedUpTeam.topic_id(@participant.parent_id, @participant.user_id)&lt;br /&gt;
    @sign_up_topics -= SignUpTopic.where(assignment_id: @assignment.id, id: my_topic)&lt;br /&gt;
    @num_participants = AssignmentParticipant.where(parent_id: @assignment.id).count&lt;br /&gt;
    @selected_topics = nil # This is used to list the topics assigned to review (i.e., select == assigned)&lt;br /&gt;
    @bids = ReviewBid.where(participant_id: @participant, assignment_id: @assignment.id)&lt;br /&gt;
    signed_up_topics = []&lt;br /&gt;
    &lt;br /&gt;
    @bids.each do |bid|&lt;br /&gt;
      sign_up_topic = SignUpTopic.find_by(id: bid.signuptopic_id)&lt;br /&gt;
      signed_up_topics &amp;lt;&amp;lt; sign_up_topic if sign_up_topic&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    signed_up_topics &amp;amp;= @sign_up_topics&lt;br /&gt;
    @sign_up_topics -= signed_up_topics&lt;br /&gt;
    @bids = signed_up_topics&lt;br /&gt;
    @num_of_topics = @sign_up_topics.size&lt;br /&gt;
    @assigned_review_maps = []&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: @assignment.id, reviewer_id: @participant.id).each do |review_map|&lt;br /&gt;
      @assigned_review_maps &amp;lt;&amp;lt; review_map&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Explicitly render view since it's in the sign up sheet views&lt;br /&gt;
    render 'sign_up_sheet/review_bids_show'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Additional methods&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-2.-reviewbidshelper-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
 # Assigns bidding topics to reviewers&lt;br /&gt;
  def assign_bidding&lt;br /&gt;
      assignment = validate_assignment(params[:assignment_id])&lt;br /&gt;
&lt;br /&gt;
      reviewers = validate_reviewers(assignment.id)&lt;br /&gt;
      reviewer_ids = reviewers.map(&amp;amp;:id)&lt;br /&gt;
&lt;br /&gt;
      matched_topics = run_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
      if matched_topics.blank?&lt;br /&gt;
        flash[:alert] = 'Topic or assignment is missing'&lt;br /&gt;
        redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      leftover_topics = find_leftover_topics(assignment.id, matched_topics)&lt;br /&gt;
      assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.new.assign_review_topics(matched_topics)&lt;br /&gt;
      assignment.update!(can_choose_topic_to_review: false)&lt;br /&gt;
&lt;br /&gt;
      flash[:notice] = 'Reviewers were successfully assigned to topics.'&lt;br /&gt;
      redirect_back fallback_location: root_path&lt;br /&gt;
    rescue ArgumentError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ArgumentError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: e.message&lt;br /&gt;
    rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::RecordInvalid: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue ActiveRecord::ActiveRecordError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::ActiveRecordError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue StandardError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;StandardError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers. Please try again later.'&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  #service methods here ...&lt;br /&gt;
&lt;br /&gt;
  def find_leftover_topics(assignment_id, matched_topics)&lt;br /&gt;
    all_topic_ids = SignUpTopic.where(assignment_id: assignment_id).pluck(:id)&lt;br /&gt;
    assigned_topic_ids = matched_topics.map { |match| match[:topic_id] }&lt;br /&gt;
&lt;br /&gt;
    # Calculate leftover topics&lt;br /&gt;
    all_topic_ids - assigned_topic_ids&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
    return if leftover_topics.blank?&lt;br /&gt;
&lt;br /&gt;
    # Find non-bidders by excluding already matched reviewers&lt;br /&gt;
    non_bidders = reviewer_ids - matched_topics.keys&lt;br /&gt;
&lt;br /&gt;
    # Assign leftover topics to non-bidders in a round-robin fashion&lt;br /&gt;
    non_bidders.each_with_index do |reviewer_id, index|&lt;br /&gt;
      topic_id = leftover_topics[index % leftover_topics.length]&lt;br /&gt;
      ReviewBid.create(priority: 1, signuptopic_id: topic_id, participant_id: reviewer_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirm Functionality for Single-person Teams ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Make sure your code works on individual assignments, i.e., assignments where participants sign up for topics instead of teams. In this case, a team is created for each participant when they sign up. So the code should work for assignments to which either individuals or teams submit.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to make an update to focus on topic rather than team or have it where teams are updated based on topic id.We will probably utilize sign up topic or a similar object to accomplish this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-5-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bid Status Messaging ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| To make the functionality more intuitive, include a message to say how many students are eligible to submit bids, how many have submitted their bids, when the deadline for submitting bids is.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
The logic for these messages will go into the helper file. It is effectively the same reasoning as our migration of other business/functionality code from the controller to the helper to conform to the SRP. It will go hand in hand with the SignUpTopic portion of the code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-6-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Why are the methods in review_bid.rb class methods? Can we change them to instance methods or move it to helpers?&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-1.-reviewbid-model-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model Updates ====&lt;br /&gt;
Since the current class methods are tied to &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model instances, we will change them to instance methods. There is no need to move them to helpers.&lt;br /&gt;
&lt;br /&gt;
===== Review Bid Model Flow Before Refactoring =====&lt;br /&gt;
This sequence diagram describes how the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; interacts with the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model in assigning review topics to reviewers according to their bids. Once the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; action is initiated by the user, the controller gathers the IDs of reviewers and calls &amp;lt;code&amp;gt;ReviewBid.bidding_data&amp;lt;/code&amp;gt; to gather bidding information. That is sent out to an external bidding algorithm, and the matched topics that come back are used by &amp;lt;code&amp;gt;ReviewBid.assign_review_topics&amp;lt;/code&amp;gt; to create &amp;lt;code&amp;gt;ReviewResponseMap&amp;lt;/code&amp;gt; entries, assigning topics to reviewers for the assignment.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
  belongs_to :assignment, class_name: 'Assignment'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  def self.bidding_data(assignment_id, reviewer_ids)&lt;br /&gt;
    # create basic hash and set basic hash data&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'users' =&amp;gt; {}, 'max_accepted_proposals' =&amp;gt; [] }&lt;br /&gt;
    bidding_data['tid'] = SignUpTopic.where(assignment_id: assignment_id).ids&lt;br /&gt;
    bidding_data['max_accepted_proposals'] = Assignment.where(id: assignment_id).pluck(:num_reviews_allowed).first&lt;br /&gt;
&lt;br /&gt;
    # loop through reviewer_ids to get reviewer specific bidding data&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      bidding_data['users'][reviewer_id] = reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # assigns topics to reviews as matched by the webservice algorithm&lt;br /&gt;
  def self.assign_review_topics(assignment_id, reviewer_ids, matched_topics, _min_num_reviews = 2)&lt;br /&gt;
    # if review response map already created, delete it&lt;br /&gt;
    if ReviewResponseMap.where(reviewed_object_id: assignment_id)&lt;br /&gt;
      ReviewResponseMap.where(reviewed_object_id: assignment_id).destroy_all&lt;br /&gt;
    end&lt;br /&gt;
    # loop through reviewer_ids to assign reviews to each reviewer&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      topics_to_assign = matched_topics[reviewer_id.to_s]&lt;br /&gt;
      topics_to_assign.each do |topic|&lt;br /&gt;
        assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method to assign a single topic to a reviewer&lt;br /&gt;
  def self.assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic).pluck(:team_id).first&lt;br /&gt;
    team_to_review.nil? ? [] : ReviewResponseMap.create(reviewed_object_id: assignment_id, reviewer_id: reviewer_id, reviewee_id: team_to_review, type: 'ReviewResponseMap')&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method for getting individual reviewer_ids bidding data&lt;br /&gt;
  # returns user's bidding data hash&lt;br /&gt;
  def self.reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    reviewer_user_id = AssignmentParticipant.find(reviewer_id).user_id&lt;br /&gt;
    self_topic = SignedUpTeam.topic_id(assignment_id, reviewer_user_id)&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'otid' =&amp;gt; self_topic, 'priority' =&amp;gt; [], 'time' =&amp;gt; [] }&lt;br /&gt;
    bids = ReviewBid.where(participant_id: reviewer_id)&lt;br /&gt;
&lt;br /&gt;
    # loop through each bid for a topic to get specific data&lt;br /&gt;
    bids.each do |bid|&lt;br /&gt;
      bidding_data['tid'] &amp;lt;&amp;lt; bid.signuptopic_id&lt;br /&gt;
      bidding_data['priority'] &amp;lt;&amp;lt; bid.priority&lt;br /&gt;
      bidding_data['time'] &amp;lt;&amp;lt; bid.updated_at&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  # Instance method to get bidding data for this review bid&lt;br /&gt;
  def bidding_data&lt;br /&gt;
    {&lt;br /&gt;
      'tid' =&amp;gt; topic_ids,&lt;br /&gt;
      'otid' =&amp;gt; self_topic,&lt;br /&gt;
      'priority' =&amp;gt; priorities,&lt;br /&gt;
      'time' =&amp;gt; updated_times&lt;br /&gt;
    }&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # bid object to limit db touches for similar data acquisitions&lt;br /&gt;
  def bids&lt;br /&gt;
    @bids ||= ReviewBid.where(participant_id: participant_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def topic_ids&lt;br /&gt;
    bids.pluck(:signuptopic_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def priorities&lt;br /&gt;
    bids.pluck(:priority)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def updated_times&lt;br /&gt;
    bids.pluck(:updated_at)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self_topic&lt;br /&gt;
    return nil unless topic &amp;amp;&amp;amp; topic.assignment_id&lt;br /&gt;
&lt;br /&gt;
    SignedUpTeam.topic_id(topic.assignment_id, participant.user_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign topics to reviews&lt;br /&gt;
  def assign_review_topics(matched_topics)&lt;br /&gt;
    validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: topic.assignment_id).destroy_all&lt;br /&gt;
&lt;br /&gt;
    matched_topics.each do |reviewer_id, topics|&lt;br /&gt;
      Array(topics).each { |topic_id| assign_topic_to_reviewer(reviewer_id, topic_id) }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign a single topic to a reviewer&lt;br /&gt;
  def assign_topic_to_reviewer(reviewer_id, topic_id)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic_id).pluck(:team_id).first&lt;br /&gt;
    return if team_to_review.nil?&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.create(&lt;br /&gt;
      reviewed_object_id: topic.assignment_id,&lt;br /&gt;
      reviewer_id: reviewer_id,&lt;br /&gt;
      reviewee_id: team_to_review,&lt;br /&gt;
      type: 'ReviewResponseMap'&lt;br /&gt;
    )&lt;br /&gt;
    # error handling&lt;br /&gt;
  rescue StandardError =&amp;gt; e&lt;br /&gt;
    Rails.logger.error(&amp;quot;Failed to assign topic #{topic_id} to reviewer #{reviewer_id}: #{e.message}&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
    raise ArgumentError, 'Topic or assignment is missing' if topic.nil? || topic.assignment_id.nil?&lt;br /&gt;
    raise ArgumentError, 'Matched topics must be a Hash' unless matched_topics.is_a?(Hash)&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
Once we changed them to instance methods, we modified their use in the controller code to instantiate the object first.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-3.-schema.db-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;Schema.db&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We also need to remove the foreign key reference to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model since the &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model already has access to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;, and that is the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; we should be referencing. This will also involve modifying the database to remove the foreign key reference to the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Steps required:&lt;br /&gt;
&lt;br /&gt;
# Review the records in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table in the database to see if there are any records and if those records have values for &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;&lt;br /&gt;
# Create a migration file using the command: &amp;lt;code&amp;gt;rails generate migration RemoveAssignmentFromReviewBids&amp;lt;/code&amp;gt;&lt;br /&gt;
# In the generated file, indicate the change that needs to be made using the change method and migration columns &amp;lt;code&amp;gt;remove_foreign_key&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;remove_column&amp;lt;/code&amp;gt;. Migration should be straightforward since there is no cascading behavior (i.e., &amp;lt;code&amp;gt;dependent:destroy&amp;lt;/code&amp;gt;) to worry about.&lt;br /&gt;
# Once comfortable with the updates to the generated file, run the migration using the &amp;lt;code&amp;gt;rails db:migrate&amp;lt;/code&amp;gt; command.&lt;br /&gt;
# Run a query in the DB to ensure no orphaned records are in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table after the updates.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-4.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implement Edge Case Testing ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| In the previous implementation wiki, there are edge cases which are not exhaustively tested. Should test those edge cases thoroughly and add more edge case testing - link&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span class=&amp;quot;mark&amp;quot;&amp;gt;TODO&amp;lt;/span&amp;gt;:We just got initial test results back. We still need to figure out why tests are failing and what may be slipping through the cracks.[[File:Test expertiza.png|411x459px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-8-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design Patterns and Principles ===&lt;br /&gt;
Below are the design patterns and principles we plan to use to address the problem statements outlined in the project requirements:&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
** Controller Responsibility&lt;br /&gt;
*** To address the issue of authentication logic residing in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, we will refactor the code to ensure the controller solely handles request and response flows.&lt;br /&gt;
*** Authentication logic will be moved to dedicated methods in &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt;, making the codebase easier to maintain and test.&lt;br /&gt;
** View Simplification&lt;br /&gt;
*** Currently, the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; view contains excessive business logic, violating SRP.&lt;br /&gt;
*** We will move this logic into helper methods or models, ensuring each component has a single responsibility and improving code readability.&lt;br /&gt;
&lt;br /&gt;
* Separation of Concerns (SoC)&lt;br /&gt;
** By relocating authorization logic out of the controller, we separate authentication from request handling.&lt;br /&gt;
** Adhering to SoC enhances readability and maintainability by allowing each part of the application to focus on its specific role.&lt;br /&gt;
* Don't Repeat Yourself (DRY)&lt;br /&gt;
** We will inspect the existing implementation for code duplication, particularly in the controller actions.&lt;br /&gt;
** Any repetitive code will be refactored or removed to reduce redundancy and improve clarity.&lt;br /&gt;
&lt;br /&gt;
* Model-View-Controller (MVC) Adherence&lt;br /&gt;
** Moving business logic from the view aligns with MVC best practices.&lt;br /&gt;
** By ensuring the view only presents data without processing it, we maintain clear boundaries between the model, view, and controller layers.&lt;br /&gt;
&lt;br /&gt;
* Dependency Inversion Principle (DIP)&lt;br /&gt;
** We plan to update class methods to instance methods, allowing the controller to depend on abstractions rather than concrete implementations.&lt;br /&gt;
** This change leverages Ruby's duck typing, providing flexibility and making the code easier to maintain and test.&lt;br /&gt;
&lt;br /&gt;
** Clear and intentional naming during refactoring will enhance code clarity and maintainability.&lt;br /&gt;
&lt;br /&gt;
== Files Modified/Added ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-1.-controllers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
review_bids_controller.rb&lt;br /&gt;
&lt;br /&gt;
signup_sheet_controller.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-2.-models&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
review_bid.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-3.-helpers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
&lt;br /&gt;
authorization_helper.rb&lt;br /&gt;
&lt;br /&gt;
Review_bids_helper.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;viii.-test-plan&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
The Excel spreadsheet below details our test plan for testing the code we changed or created to ensure code coverage. The process involved reviewing the code and determining all the possible scenarios that needed to be covered based on what the method was doing, then creating test cases accordingly.&lt;br /&gt;
&lt;br /&gt;
* [https://docs.google.com/spreadsheets/d/1iHzOdSwlTtxOtf5VsNdwOaCP8NzYMmiJ/edit?usp=drive_link Open Test Matrix Spreadsheet]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/review_bids_controller_spec.rb ReviewBidsController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/sign_up_sheet_controller_spec.rb SignUpSheetController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/models/review_bid_spec.rb ReviewBid model updated test cases]&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-1.-mentor&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Ed Gehringer&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-2.-members&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Members ===&lt;br /&gt;
&lt;br /&gt;
* Gavin Teague&lt;br /&gt;
* Janice Uwujaren&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xi.-links&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
Previous Wiki: [https://wiki.expertiza.ncsu.edu/index.php/CSC/ECE_517_Fall_2021_-_E2151._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;Link&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xii.-references&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/document/d/1LHVacylQmB15I6VcYUA5nuC7GRRU9szHzZhjlPcE-PA/edit?tab=t.0#heading=h.u0oycoj6e0ev &amp;lt;u&amp;gt;Project Instructions&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;appendix-a-uml-class-diagram-plantuml-code-for-review-bids&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Juwujar</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160984</id>
		<title>CSC/ECE 517 Fall 2024 - E2485. Allow reviewers to bid on what to review</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160984"/>
		<updated>2024-12-11T01:37:44Z</updated>

		<summary type="html">&lt;p&gt;Juwujar: /* Optimize ReviewBidsController#set_priority Action functionality */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
This project will fix the defects and improve the functionality in Expertiza of the Reviewer Bidding feature, a process where reviewers can bid for assignments they want to review. It will make the functionality smoother, more reliable, and align with Expertiza's collaborative learning objectives.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iii.-problem-statements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Problem Statements ==&lt;br /&gt;
&lt;br /&gt;
Assigning reviews to users on Expertiza involves a complex bidding algorithm that requires optimization before production deployment. Below are key areas we plan to address as per the project requirements:&lt;br /&gt;
* Code Refactoring and Best Practices&lt;br /&gt;
** '''Utilize Authentication Utilities''': Modify the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; function in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to use existing authentication utilities, enhancing code reusability and adhering to the Single Responsibility Principle (SRP) and Separation of Concerns (SoC).&lt;br /&gt;
** '''Eliminate Code Duplication''': Refactor &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; to comply with the Don't Repeat Yourself (DRY) principle.&lt;br /&gt;
** '''Rename Functions for Clarity''': Change &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; to better reflect its purpose.&lt;br /&gt;
** '''Refactor Methods in '''&amp;lt;code&amp;gt;review_bid.rb&amp;lt;/code&amp;gt;''':''': Convert class methods to instance methods or relocate them to helper classes for improved code organization.&lt;br /&gt;
* Functionality Enhancements&lt;br /&gt;
** '''Handle Missing Bids Gracefully''': Adjust the bidding algorithm to accommodate students who do not bid, allowing them to select from the remaining options.&lt;br /&gt;
** '''Support Both Individual and Team Assignments''': Ensure the code functions correctly for participant and team-based assignments when signing up for topics.&lt;br /&gt;
* User Interface Improvements&lt;br /&gt;
** '''Increase Transparency in the Bidding Process''': Display messages indicating how many students are eligible to bid, how many have submitted bids, and the deadline.&lt;br /&gt;
** '''Accurate Bid Status Indicators''': Ensure topic colors change correctly based on the number of outstanding bids, providing clear visual feedback to users.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Exhaustive Edge Case Testing''': Conduct thorough testing for edge cases not previously covered, adding additional test cases as needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iv.-design-goals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Design Goals ==&lt;br /&gt;
&lt;br /&gt;
While addressing the existing defects and enhancing the Reviewer Bidding feature, we plan to adhere to the following design guidelines:&lt;br /&gt;
&lt;br /&gt;
* Code Quality and Maintainability&lt;br /&gt;
** '''Improve Code Structure: '''Implement refactoring strategies to enhance code readability and maintainability, ensuring adherence to best practices like the DRY principle.&lt;br /&gt;
** '''Standardize Authentication Logic: '''Utilize a centralized authentication utility to promote consistency and reusability across the application.&lt;br /&gt;
* System Stability and Flexibility&lt;br /&gt;
** '''Enhance Algorithm Robustness: '''Modify the bidding algorithm to handle scenarios where students do not bid, ensuring the system remains stable under all conditions.&lt;br /&gt;
** '''Ensure Assignment Versatility: '''Design the system to seamlessly support both individual and team assignments without additional configuration.&lt;br /&gt;
* User Experience Enhancements&lt;br /&gt;
** '''Increase Bidding Transparency: '''Develop user interface elements that clearly display bidding status, deadlines, and participation metrics.&lt;br /&gt;
** '''Implement Dynamic Visual Feedback: '''Use real-time visual cues, such as color changes, to inform users about the bidding status instantly.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Develop Comprehensive Test Suites: '''Create extensive automated tests covering edge cases and critical functionalities to guarantee reliable system behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;v.-uml-diagram&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== UML Diagram ==&lt;br /&gt;
&lt;br /&gt;
Below is a visual representation of the models associated with the review bids functionality, depicting the data structures and their relationships.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml class diagram2.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi.-implementation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
'''ReviewBid: '''The &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; manages the bidding process, where participants indicate their preferences for reviewing different topics or assignments. This helps distribute review tasks fairly based on participants' interests.&lt;br /&gt;
&lt;br /&gt;
'''Assignment: '''The &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model represents team or individual assignments in Expertiza. It stores all the essential information required to keep the assignment process organized, like managing participants, setting due dates, forming teams, and overseeing the review process.&lt;br /&gt;
&lt;br /&gt;
'''SignUpTopic: '''The &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt; model identifies specific topics or areas within an assignment for which participants can sign up. It manages the availability of these topics, tracks bids, coordinates team sign-ups, and assigns topics based on both bids and participant availability. &lt;br /&gt;
&lt;br /&gt;
'''Participant: '''The &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model is about the individual users who participate in an assignment. This includes students, instructors, teaching assistants, or other relevant roles. This model is responsible for managing key user information, monitoring interactions with the assignment, keeping track of team memberships, and recording things like badges and review grades.&lt;br /&gt;
&lt;br /&gt;
== Implementation ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1.-decoupling-authorization-logic-from-reviewbidscontroller&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Decoupling Authorization Logic from &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| '''&amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; needs to use the authentication utilities'''&lt;br /&gt;
&lt;br /&gt;
The function &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; should use the available authentication utilities to support the Single Responsibility Principle and Separation of Concerns and to support code reusability.&lt;br /&gt;
&lt;br /&gt;
| '''Authentication Consistency:''' Remove authentication logic in the controller and call logic in the authentication utility class instead to ensure consistent use of the same logic across the application, foster reusability, and eliminate DRY.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
==== Authorization Flow Before Refactoring ====&lt;br /&gt;
The sequence diagram depicts the authorization process within the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;. When a user invokes an action, the controller checks if the action is allowed by verifying the user's role through the &amp;lt;code&amp;gt;current_role_name()&amp;lt;/code&amp;gt; method in the &amp;lt;code&amp;gt;ApplicationController&amp;lt;/code&amp;gt;. Depending on the action and the user's role, the controller either authorizes the user to proceed or denies access, ultimately executing the action logic if authorized and responding to the user.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 1.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We simplified the &amp;lt;code&amp;gt;action_allowed?&amp;lt;/code&amp;gt; method by decoupling authorization logic from the controller to use helper methods in the &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt; module. The &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; should not handle authentication, which is a violation of SRP. &lt;br /&gt;
&lt;br /&gt;
Note: We also made similar updates to the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; methods for &amp;lt;code&amp;gt;SignUpController&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;vertical-align:top;&amp;quot; | Original Code !! style=&amp;quot;vertical-align:top;&amp;quot; | Refactored Code&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index'&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator',&lt;br /&gt;
     'Student'].include? current_role_name and&lt;br /&gt;
        ((%w[list].include? action_name) ? are_needed_authorizations_present?(params[:id], &amp;quot;participant&amp;quot;, &amp;quot;reader&amp;quot;, &amp;quot;submitter&amp;quot;, &amp;quot;reviewer&amp;quot;) : true)&lt;br /&gt;
  else&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator'].include? current_role_name&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index', 'list'&lt;br /&gt;
    current_user_has_student_privileges? &amp;amp;&amp;amp; list_authorization_check&lt;br /&gt;
  else&lt;br /&gt;
    current_user_has_ta_privileges?&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; action ===&lt;br /&gt;
Previously, the &amp;lt;code&amp;gt;ReviewBidsController's index&amp;lt;/code&amp;gt; action was overloaded with responsibilities—it handled everything from locating and validating assignments to fetching review mappings and counting completed reviews. This all-in-one approach made the controller action messy. &lt;br /&gt;
&lt;br /&gt;
We have moved the business logic from the controller action and into dedicated service objects. With this change, we introduced two key service objects: &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; focuses on retrieving the participant along with their assignments, while the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; takes on the task of managing review mappings and calculating review-related stats, like how many reviews have been completed. &lt;br /&gt;
&lt;br /&gt;
This separation of responsibilities aligns with the Single Responsibility Principle. The logic for fetching assignments, retrieving review mappings, and counting reviews is now more flexible and reusable throughout the application. For example, we used methods in the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; in both the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; and the &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;, helping to avoid code duplication.The article https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial served as a guide on when and how to use service objects in Rails.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
  return unless current_user_id?(@participant.user_id)&lt;br /&gt;
  @assignment = @participant.assignment&lt;br /&gt;
  @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
  # Finding how many reviews have been completed&lt;br /&gt;
  @num_reviews_completed = 0&lt;br /&gt;
  @review_mappings.each do |map|&lt;br /&gt;
    @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # render view for completing reviews after review bidding has been completed&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @assignment = participant_service.assignment&lt;br /&gt;
  unless @assignment.is_a?(Assignment)&lt;br /&gt;
    flash[:error] = 'Assignment not found.'&lt;br /&gt;
    redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  @review_mappings = review_service.review_mappings&lt;br /&gt;
  @num_reviews_completed = review_service.review_counts[:completed]&lt;br /&gt;
&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Optimize &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Action functionality ===&lt;br /&gt;
In the earlier version of the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, the server only needed a couple of key pieces of information: an array of topic IDs (params[:topic]) and the participant ID (params[:id}). From there, it would figure out the assignment_id by using the first topic as a reference. This is risky because the &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt; associated with the topic may not be the right &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
With the new implementation, we structured the parameters to include the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt;, an array of topic IDs, and &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;. This change ensures that the server always receives complete and reliable information, preventing issues caused by missing or inconsistent data. It also removes the need for the server to perform unnecessary lookups, as in the case of &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;, allowing the controller to focus on receiving requests and rendering appropriate responses.&lt;br /&gt;
&lt;br /&gt;
While these updates have cleaned up the set_priority action, they still contain a lot of business logic, like validations and priority updates. To make the code even more maintainable, there’s room for further refactoring—perhaps moving some of this logic into a service class or model method to streamline the controller even more.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | Old Code&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    if params[:topic].nil?&lt;br /&gt;
      ReviewBid.where(participant_id: params[:id]).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      participant = AssignmentParticipant.find_by(id: params[:id])&lt;br /&gt;
      assignment_id = SignUpTopic.find(params[:topic].first).assignment.id&lt;br /&gt;
      @bids = ReviewBid.where(participant_id: params[:id])&lt;br /&gt;
      signed_up_topics = ReviewBid.where(participant_id: params[:id]).map(&amp;amp;:signuptopic_id)&lt;br /&gt;
      signed_up_topics -= params[:topic].map(&amp;amp;:to_i)&lt;br /&gt;
      signed_up_topics.each do |topic|&lt;br /&gt;
        ReviewBid.where(signuptopic_id: topic, participant_id: params[:id]).destroy_all&lt;br /&gt;
      end&lt;br /&gt;
      params[:topic].each_with_index do |topic_id, index|&lt;br /&gt;
        bid_existence = ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id])&lt;br /&gt;
        if bid_existence.empty?&lt;br /&gt;
          ReviewBid.create(priority: index + 1, signuptopic_id: topic_id, participant_id: params[:id], assignment_id: assignment_id)&lt;br /&gt;
        else&lt;br /&gt;
          ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id]).update_all(priority: index + 1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to action: 'show', assignment_id: params[:assignment_id], id: params[:id]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    participant_id = params[:participant_id].to_i&lt;br /&gt;
    selected_topic_ids = params[:topic]&amp;amp;.map(&amp;amp;:to_i) || []&lt;br /&gt;
    assignment_id = params[:assignment_id].to_i&lt;br /&gt;
&lt;br /&gt;
    unless current_user_can_modify_participant?(participant_id)&lt;br /&gt;
      flash[:error] = 'You are not authorized to modify these bids.'&lt;br /&gt;
      respond_to do |format|&lt;br /&gt;
        format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
        format.json { render json: { status: 'unauthorized' }, status: :unauthorized }&lt;br /&gt;
      end&lt;br /&gt;
      return&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    if selected_topic_ids.empty?&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      assignment = Assignment.find_by(id: assignment_id)&lt;br /&gt;
      if assignment.nil?&lt;br /&gt;
        flash[:error] = &amp;quot;Invalid assignment.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_assignment' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      unless SignUpTopic.where(id: selected_topic_ids, assignment_id: assignment_id).count == selected_topic_ids.size&lt;br /&gt;
        flash[:error] = &amp;quot;One or more selected topics are invalid.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_topics' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).where.not(signuptopic_id: selected_topic_ids).destroy_all&lt;br /&gt;
&lt;br /&gt;
      selected_topic_ids.each_with_index do |topic_id, index|&lt;br /&gt;
        bid = ReviewBid.find_or_initialize_by(signuptopic_id: topic_id, participant_id: participant_id)&lt;br /&gt;
        bid.priority = index + 1&lt;br /&gt;
        bid.assignment_id = assignment_id&lt;br /&gt;
        bid.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_to action: 'show', assignment_id: assignment_id, id: participant_id, notice: 'Review bids updated successfully.' }&lt;br /&gt;
      format.json { render json: { status: 'success' }, status: :ok }&lt;br /&gt;
    end&lt;br /&gt;
  rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
    flash[:error] = &amp;quot;Failed to update priorities: #{e.message}&amp;quot;&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
      format.json { render json: { status: 'error', message: e.message }, status: :unprocessable_entity }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Client Side Changes to Support &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Changes ====&lt;br /&gt;
After tidying up the server-side logic, we needed to refactor the client-side code to ensure it passed the correct information from the view to the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action. On the client side, we now grab the topicIDs and send them as &amp;lt;code&amp;gt;selected_topic_ids&amp;lt;/code&amp;gt;, allowing the controller to figure out which topics were selected and in what order.  &lt;br /&gt;
&lt;br /&gt;
We also extracted the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, making it a lot easier to send data with AJAX and keeping things flexible. When working with jQuery, AJAX is awesome because it lets us update the server without reloading the page, making the user experience smooth and responsive. When we pull the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, we can grab it along with other data, like the list of topics, and include it in the request. This way, we don’t have to rely on the URL to know which participant we’re working with, which makes the code cleaner and more adaptable.&lt;br /&gt;
&lt;br /&gt;
If we were to rely on the route (as opposed to using the DOM) for the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt;, every AJAX request would need to be tied to a specific URL structure. That’s limiting, especially if the route doesn’t naturally include the participant ID, as can be the case with collection routes versus member routes in Rails. &lt;br /&gt;
&lt;br /&gt;
Note: One interesting aspect of the application was the mix of JavaScript and CoffeeScript code. It wasn't clear why both types of code were used.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery -&amp;gt;&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: -&amp;gt;&lt;br /&gt;
      $.post($(this).data('update-url'), $(this).sortable('serialize'))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery(document).ready ($) -&amp;gt;&lt;br /&gt;
  sendUpdatedOrder = -&amp;gt;&lt;br /&gt;
    sortedIDs = $(this).sortable('toArray')&lt;br /&gt;
&lt;br /&gt;
    topicIDs = sortedIDs.map (id) -&amp;gt;&lt;br /&gt;
      id.replace 'topic_', ''&lt;br /&gt;
&lt;br /&gt;
    participant_id = $(this).data('participant-id')&lt;br /&gt;
&lt;br /&gt;
    assignment_id = new URL($(this).data('update-url'), window.location.origin).searchParams.get('assignment_id')&lt;br /&gt;
&lt;br /&gt;
    $.ajax&lt;br /&gt;
      type: 'POST'&lt;br /&gt;
      url: $(this).data('update-url')&lt;br /&gt;
      data:&lt;br /&gt;
        topic: topicIDs&lt;br /&gt;
        participant_id: participant_id&lt;br /&gt;
        assignment_id: assignment_id&lt;br /&gt;
      success: (response) -&amp;gt;&lt;br /&gt;
        console.log 'Selections updated successfully.'&lt;br /&gt;
      error: (xhr, status, error) -&amp;gt;&lt;br /&gt;
        alert 'An error occurred while saving your selections. Please try again.'&lt;br /&gt;
        console.error error&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: sendUpdatedOrder&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-1.-signupsheetcontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; Updates ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; method renders the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; action in &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt;. However, the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; does not include the controller action for this view. We need to find the old implementation of the controller action, add it back, and review and update it for any DRY violations.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # provides variables for reviewing page located at views/review_bids/others_work.html.erb&lt;br /&gt;
  def index&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
    return unless current_user_id?(@participant.user_id)&lt;br /&gt;
&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
    # Finding how many reviews have been completed&lt;br /&gt;
    @num_reviews_completed = 0&lt;br /&gt;
    @review_mappings.each do |map|&lt;br /&gt;
      @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # render view for completing reviews after review bidding has been completed&lt;br /&gt;
    render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
  end  &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-2.-reviewbidsotherswork-view-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;review_bids_others_work.html.erb&amp;lt;/code&amp;gt; View Updates ====&lt;br /&gt;
&lt;br /&gt;
The code only contains a view with this name and no associated controller action. Reviewing the code in the view, we noticed some SRP violations where there is much logic in the presentation layer. Set up helper methods in the controller to handle some of that logic or see if the model can handle some of that logic.&lt;br /&gt;
&lt;br /&gt;
Note: The view is technically part of the SignUpSheetController, but it's being rendered by the ReviewBidsController, which initially threw us off. After chatting with the professor, we decided not to change this view. He indicated that the plan is to reuse these views for both assignment bidding and review bidding.&lt;br /&gt;
&lt;br /&gt;
We would suggest building the assignment bidding and review bidding features separately first to make sure each one works on its own. Once that’s done, we can go back and clean things up by combining any shared functionality. This could include creating shared views or even introducing an abstract model to handle both types of bidding in a cleaner way. It could also mean replacing the ReviewBidsController with something more generic, like a BidsController, to keep things reusable and easier to maintain down the line.&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Rename &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; method ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; should be &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt;&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:''' Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The primary activity here is to refactor the method name &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; and fix the hardcoded URL. However, the match topic bidding algorithm no longer works since Heroku is no longer free, per the professor. The reference link below has detailed information about the web service that we can leverage to gain that understanding.&lt;br /&gt;
&lt;br /&gt;
Reference Link for Webservice Details: [https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Updated Implementation ====&lt;br /&gt;
The original requirement was to rename the run_bidding_algorithm method to assign_reviewers, but after digging into the code, we realized that renaming it didn’t make sense. The method runs the bidding algorithm to match topics, and there’s already a separate method to handle assigning review topics.&lt;br /&gt;
&lt;br /&gt;
However, we improved the logic in the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; method (which calls the private &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt;) by introducing a separate &amp;lt;code&amp;gt;BiddingAlgorithmService&amp;lt;/code&amp;gt; object. This keeps the business logic out of the controller action, which makes things cleaner and more maintainable.&lt;br /&gt;
&lt;br /&gt;
==== review_bids_controller ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def run_bidding_algorithm&lt;br /&gt;
    review_bid = ReviewBid.new&lt;br /&gt;
    bidding_data = review_bid.bidding_data&lt;br /&gt;
    matched_topics = BiddingAlgorithmService.new(bidding_data).run&lt;br /&gt;
    raise ArgumentError, 'Failed to assign reviewers. Please try again later.' unless matched_topics&lt;br /&gt;
&lt;br /&gt;
    matched_topics&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== bidding_algorithm_service ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# frozen_string_literal: true&lt;br /&gt;
&lt;br /&gt;
# The `BiddingAlgorithmService` run the bid assignment algorithm&lt;br /&gt;
# Sends student IDs, topic IDs, student preferences, and timestamps to the web service&lt;br /&gt;
# The web service returns the matched assignments in the JSON response body&lt;br /&gt;
class BiddingAlgorithmService&lt;br /&gt;
  SERVICE_URL = 'http://app-csc517.herokuapp.com/match_topics'.freeze&lt;br /&gt;
&lt;br /&gt;
  # MOCK_DATA is based on the documentation detailing how the webservice behaves.&lt;br /&gt;
  # Reference: https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review#Webservice&lt;br /&gt;
  MOCK_DATA = {&lt;br /&gt;
    36239 =&amp;gt; [3970, 3972, 3975],&lt;br /&gt;
    36240 =&amp;gt; [3973, 3974, 3972],&lt;br /&gt;
    36241 =&amp;gt; [3969, 3971, 3972],&lt;br /&gt;
    36242 =&amp;gt; [3969, 3971, 3973],&lt;br /&gt;
    36243 =&amp;gt; [3969, 3970, 3971]&lt;br /&gt;
  }.freeze&lt;br /&gt;
&lt;br /&gt;
  def initialize(bidding_data)&lt;br /&gt;
    @bidding_data = bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def run&lt;br /&gt;
    return MOCK_DATA if Rails.application.config.use_mock_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
    perform_request&lt;br /&gt;
  rescue RestClient::ExceptionWithResponse, JSON::ParserError&lt;br /&gt;
    false&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def perform_request&lt;br /&gt;
    response = RestClient.post(&lt;br /&gt;
      SERVICE_URL,&lt;br /&gt;
      @bidding_data.to_json,&lt;br /&gt;
      content_type: :json,&lt;br /&gt;
      accept: :json&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    validate_response(response)&lt;br /&gt;
    JSON.parse(response.body)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def validate_response(response)&lt;br /&gt;
    raise 'Invalid response format' unless response.headers[:content_type] &amp;amp;&amp;amp; response.headers[:content_type].include?('application/json')&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Resolve Functionality Issues for Non Bidders ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Currently it doesn't work if some student does not bid. In this case, algorithm needs to be fixed to ignore anyone who didn’t bid, and let them choose from what’s left over.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to do several things within this controller. We will set a default state for non-bidders that we can point to to give them options to select topics to review from the leftovers. We will consolidate &amp;lt;code&amp;gt;@sign_up_topics&amp;lt;/code&amp;gt; to resolve the DRY violation. Lastly, we will remove business logic from the show method and push it to a new helper to conform to the SRP.&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # Additional method&lt;br /&gt;
&lt;br /&gt;
  # Provides variables for review bidding page&lt;br /&gt;
  def show&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id].to_i)&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @sign_up_topics = SignUpTopic.where(assignment_id: @assignment.id, private_to: nil)&lt;br /&gt;
    my_topic = SignedUpTeam.topic_id(@participant.parent_id, @participant.user_id)&lt;br /&gt;
    @sign_up_topics -= SignUpTopic.where(assignment_id: @assignment.id, id: my_topic)&lt;br /&gt;
    @num_participants = AssignmentParticipant.where(parent_id: @assignment.id).count&lt;br /&gt;
    @selected_topics = nil # This is used to list the topics assigned to review (i.e., select == assigned)&lt;br /&gt;
    @bids = ReviewBid.where(participant_id: @participant, assignment_id: @assignment.id)&lt;br /&gt;
    signed_up_topics = []&lt;br /&gt;
    &lt;br /&gt;
    @bids.each do |bid|&lt;br /&gt;
      sign_up_topic = SignUpTopic.find_by(id: bid.signuptopic_id)&lt;br /&gt;
      signed_up_topics &amp;lt;&amp;lt; sign_up_topic if sign_up_topic&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    signed_up_topics &amp;amp;= @sign_up_topics&lt;br /&gt;
    @sign_up_topics -= signed_up_topics&lt;br /&gt;
    @bids = signed_up_topics&lt;br /&gt;
    @num_of_topics = @sign_up_topics.size&lt;br /&gt;
    @assigned_review_maps = []&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: @assignment.id, reviewer_id: @participant.id).each do |review_map|&lt;br /&gt;
      @assigned_review_maps &amp;lt;&amp;lt; review_map&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Explicitly render view since it's in the sign up sheet views&lt;br /&gt;
    render 'sign_up_sheet/review_bids_show'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Additional methods&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-2.-reviewbidshelper-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
 # Assigns bidding topics to reviewers&lt;br /&gt;
  def assign_bidding&lt;br /&gt;
      assignment = validate_assignment(params[:assignment_id])&lt;br /&gt;
&lt;br /&gt;
      reviewers = validate_reviewers(assignment.id)&lt;br /&gt;
      reviewer_ids = reviewers.map(&amp;amp;:id)&lt;br /&gt;
&lt;br /&gt;
      matched_topics = run_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
      if matched_topics.blank?&lt;br /&gt;
        flash[:alert] = 'Topic or assignment is missing'&lt;br /&gt;
        redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      leftover_topics = find_leftover_topics(assignment.id, matched_topics)&lt;br /&gt;
      assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.new.assign_review_topics(matched_topics)&lt;br /&gt;
      assignment.update!(can_choose_topic_to_review: false)&lt;br /&gt;
&lt;br /&gt;
      flash[:notice] = 'Reviewers were successfully assigned to topics.'&lt;br /&gt;
      redirect_back fallback_location: root_path&lt;br /&gt;
    rescue ArgumentError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ArgumentError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: e.message&lt;br /&gt;
    rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::RecordInvalid: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue ActiveRecord::ActiveRecordError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::ActiveRecordError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue StandardError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;StandardError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers. Please try again later.'&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  #service methods here ...&lt;br /&gt;
&lt;br /&gt;
  def find_leftover_topics(assignment_id, matched_topics)&lt;br /&gt;
    all_topic_ids = SignUpTopic.where(assignment_id: assignment_id).pluck(:id)&lt;br /&gt;
    assigned_topic_ids = matched_topics.map { |match| match[:topic_id] }&lt;br /&gt;
&lt;br /&gt;
    # Calculate leftover topics&lt;br /&gt;
    all_topic_ids - assigned_topic_ids&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
    return if leftover_topics.blank?&lt;br /&gt;
&lt;br /&gt;
    # Find non-bidders by excluding already matched reviewers&lt;br /&gt;
    non_bidders = reviewer_ids - matched_topics.keys&lt;br /&gt;
&lt;br /&gt;
    # Assign leftover topics to non-bidders in a round-robin fashion&lt;br /&gt;
    non_bidders.each_with_index do |reviewer_id, index|&lt;br /&gt;
      topic_id = leftover_topics[index % leftover_topics.length]&lt;br /&gt;
      ReviewBid.create(priority: 1, signuptopic_id: topic_id, participant_id: reviewer_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirm Functionality for Single-person Teams ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Make sure your code works on individual assignments, i.e., assignments where participants sign up for topics instead of teams. In this case, a team is created for each participant when they sign up. So the code should work for assignments to which either individuals or teams submit.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to make an update to focus on topic rather than team or have it where teams are updated based on topic id.We will probably utilize sign up topic or a similar object to accomplish this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-5-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bid Status Messaging ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| To make the functionality more intuitive, include a message to say how many students are eligible to submit bids, how many have submitted their bids, when the deadline for submitting bids is.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
The logic for these messages will go into the helper file. It is effectively the same reasoning as our migration of other business/functionality code from the controller to the helper to conform to the SRP. It will go hand in hand with the SignUpTopic portion of the code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-6-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Why are the methods in review_bid.rb class methods? Can we change them to instance methods or move it to helpers?&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-1.-reviewbid-model-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model Updates ====&lt;br /&gt;
Since the current class methods are tied to &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model instances, we will change them to instance methods. There is no need to move them to helpers.&lt;br /&gt;
&lt;br /&gt;
===== Review Bid Model Flow Before Refactoring =====&lt;br /&gt;
This sequence diagram describes how the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; interacts with the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model in assigning review topics to reviewers according to their bids. Once the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; action is initiated by the user, the controller gathers the IDs of reviewers and calls &amp;lt;code&amp;gt;ReviewBid.bidding_data&amp;lt;/code&amp;gt; to gather bidding information. That is sent out to an external bidding algorithm, and the matched topics that come back are used by &amp;lt;code&amp;gt;ReviewBid.assign_review_topics&amp;lt;/code&amp;gt; to create &amp;lt;code&amp;gt;ReviewResponseMap&amp;lt;/code&amp;gt; entries, assigning topics to reviewers for the assignment.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
  belongs_to :assignment, class_name: 'Assignment'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  def self.bidding_data(assignment_id, reviewer_ids)&lt;br /&gt;
    # create basic hash and set basic hash data&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'users' =&amp;gt; {}, 'max_accepted_proposals' =&amp;gt; [] }&lt;br /&gt;
    bidding_data['tid'] = SignUpTopic.where(assignment_id: assignment_id).ids&lt;br /&gt;
    bidding_data['max_accepted_proposals'] = Assignment.where(id: assignment_id).pluck(:num_reviews_allowed).first&lt;br /&gt;
&lt;br /&gt;
    # loop through reviewer_ids to get reviewer specific bidding data&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      bidding_data['users'][reviewer_id] = reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # assigns topics to reviews as matched by the webservice algorithm&lt;br /&gt;
  def self.assign_review_topics(assignment_id, reviewer_ids, matched_topics, _min_num_reviews = 2)&lt;br /&gt;
    # if review response map already created, delete it&lt;br /&gt;
    if ReviewResponseMap.where(reviewed_object_id: assignment_id)&lt;br /&gt;
      ReviewResponseMap.where(reviewed_object_id: assignment_id).destroy_all&lt;br /&gt;
    end&lt;br /&gt;
    # loop through reviewer_ids to assign reviews to each reviewer&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      topics_to_assign = matched_topics[reviewer_id.to_s]&lt;br /&gt;
      topics_to_assign.each do |topic|&lt;br /&gt;
        assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method to assign a single topic to a reviewer&lt;br /&gt;
  def self.assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic).pluck(:team_id).first&lt;br /&gt;
    team_to_review.nil? ? [] : ReviewResponseMap.create(reviewed_object_id: assignment_id, reviewer_id: reviewer_id, reviewee_id: team_to_review, type: 'ReviewResponseMap')&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method for getting individual reviewer_ids bidding data&lt;br /&gt;
  # returns user's bidding data hash&lt;br /&gt;
  def self.reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    reviewer_user_id = AssignmentParticipant.find(reviewer_id).user_id&lt;br /&gt;
    self_topic = SignedUpTeam.topic_id(assignment_id, reviewer_user_id)&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'otid' =&amp;gt; self_topic, 'priority' =&amp;gt; [], 'time' =&amp;gt; [] }&lt;br /&gt;
    bids = ReviewBid.where(participant_id: reviewer_id)&lt;br /&gt;
&lt;br /&gt;
    # loop through each bid for a topic to get specific data&lt;br /&gt;
    bids.each do |bid|&lt;br /&gt;
      bidding_data['tid'] &amp;lt;&amp;lt; bid.signuptopic_id&lt;br /&gt;
      bidding_data['priority'] &amp;lt;&amp;lt; bid.priority&lt;br /&gt;
      bidding_data['time'] &amp;lt;&amp;lt; bid.updated_at&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  # Instance method to get bidding data for this review bid&lt;br /&gt;
  def bidding_data&lt;br /&gt;
    {&lt;br /&gt;
      'tid' =&amp;gt; topic_ids,&lt;br /&gt;
      'otid' =&amp;gt; self_topic,&lt;br /&gt;
      'priority' =&amp;gt; priorities,&lt;br /&gt;
      'time' =&amp;gt; updated_times&lt;br /&gt;
    }&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # bid object to limit db touches for similar data acquisitions&lt;br /&gt;
  def bids&lt;br /&gt;
    @bids ||= ReviewBid.where(participant_id: participant_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def topic_ids&lt;br /&gt;
    bids.pluck(:signuptopic_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def priorities&lt;br /&gt;
    bids.pluck(:priority)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def updated_times&lt;br /&gt;
    bids.pluck(:updated_at)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self_topic&lt;br /&gt;
    return nil unless topic &amp;amp;&amp;amp; topic.assignment_id&lt;br /&gt;
&lt;br /&gt;
    SignedUpTeam.topic_id(topic.assignment_id, participant.user_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign topics to reviews&lt;br /&gt;
  def assign_review_topics(matched_topics)&lt;br /&gt;
    validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: topic.assignment_id).destroy_all&lt;br /&gt;
&lt;br /&gt;
    matched_topics.each do |reviewer_id, topics|&lt;br /&gt;
      Array(topics).each { |topic_id| assign_topic_to_reviewer(reviewer_id, topic_id) }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign a single topic to a reviewer&lt;br /&gt;
  def assign_topic_to_reviewer(reviewer_id, topic_id)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic_id).pluck(:team_id).first&lt;br /&gt;
    return if team_to_review.nil?&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.create(&lt;br /&gt;
      reviewed_object_id: topic.assignment_id,&lt;br /&gt;
      reviewer_id: reviewer_id,&lt;br /&gt;
      reviewee_id: team_to_review,&lt;br /&gt;
      type: 'ReviewResponseMap'&lt;br /&gt;
    )&lt;br /&gt;
    # error handling&lt;br /&gt;
  rescue StandardError =&amp;gt; e&lt;br /&gt;
    Rails.logger.error(&amp;quot;Failed to assign topic #{topic_id} to reviewer #{reviewer_id}: #{e.message}&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
    raise ArgumentError, 'Topic or assignment is missing' if topic.nil? || topic.assignment_id.nil?&lt;br /&gt;
    raise ArgumentError, 'Matched topics must be a Hash' unless matched_topics.is_a?(Hash)&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
Once we changed them to instance methods, we modified their use in the controller code to instantiate the object first.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-3.-schema.db-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;Schema.db&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We also need to remove the foreign key reference to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model since the &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model already has access to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;, and that is the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; we should be referencing. This will also involve modifying the database to remove the foreign key reference to the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Steps required:&lt;br /&gt;
&lt;br /&gt;
# Review the records in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table in the database to see if there are any records and if those records have values for &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;&lt;br /&gt;
# Create a migration file using the command: &amp;lt;code&amp;gt;rails generate migration RemoveAssignmentFromReviewBids&amp;lt;/code&amp;gt;&lt;br /&gt;
# In the generated file, indicate the change that needs to be made using the change method and migration columns &amp;lt;code&amp;gt;remove_foreign_key&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;remove_column&amp;lt;/code&amp;gt;. Migration should be straightforward since there is no cascading behavior (i.e., &amp;lt;code&amp;gt;dependent:destroy&amp;lt;/code&amp;gt;) to worry about.&lt;br /&gt;
# Once comfortable with the updates to the generated file, run the migration using the &amp;lt;code&amp;gt;rails db:migrate&amp;lt;/code&amp;gt; command.&lt;br /&gt;
# Run a query in the DB to ensure no orphaned records are in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table after the updates.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-4.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implement Edge Case Testing ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| In the previous implementation wiki, there are edge cases which are not exhaustively tested. Should test those edge cases thoroughly and add more edge case testing - link&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span class=&amp;quot;mark&amp;quot;&amp;gt;TODO&amp;lt;/span&amp;gt;:We just got initial test results back. We still need to figure out why tests are failing and what may be slipping through the cracks.[[File:Test expertiza.png|411x459px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-8-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design Patterns and Principles ===&lt;br /&gt;
Below are the design patterns and principles we plan to use to address the problem statements outlined in the project requirements:&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
** Controller Responsibility&lt;br /&gt;
*** To address the issue of authentication logic residing in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, we will refactor the code to ensure the controller solely handles request and response flows.&lt;br /&gt;
*** Authentication logic will be moved to dedicated methods in &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt;, making the codebase easier to maintain and test.&lt;br /&gt;
** View Simplification&lt;br /&gt;
*** Currently, the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; view contains excessive business logic, violating SRP.&lt;br /&gt;
*** We will move this logic into helper methods or models, ensuring each component has a single responsibility and improving code readability.&lt;br /&gt;
&lt;br /&gt;
* Separation of Concerns (SoC)&lt;br /&gt;
** By relocating authorization logic out of the controller, we separate authentication from request handling.&lt;br /&gt;
** Adhering to SoC enhances readability and maintainability by allowing each part of the application to focus on its specific role.&lt;br /&gt;
* Don't Repeat Yourself (DRY)&lt;br /&gt;
** We will inspect the existing implementation for code duplication, particularly in the controller actions.&lt;br /&gt;
** Any repetitive code will be refactored or removed to reduce redundancy and improve clarity.&lt;br /&gt;
&lt;br /&gt;
* Model-View-Controller (MVC) Adherence&lt;br /&gt;
** Moving business logic from the view aligns with MVC best practices.&lt;br /&gt;
** By ensuring the view only presents data without processing it, we maintain clear boundaries between the model, view, and controller layers.&lt;br /&gt;
&lt;br /&gt;
* Dependency Inversion Principle (DIP)&lt;br /&gt;
** We plan to update class methods to instance methods, allowing the controller to depend on abstractions rather than concrete implementations.&lt;br /&gt;
** This change leverages Ruby's duck typing, providing flexibility and making the code easier to maintain and test.&lt;br /&gt;
&lt;br /&gt;
** Clear and intentional naming during refactoring will enhance code clarity and maintainability.&lt;br /&gt;
&lt;br /&gt;
== Files Modified/Added ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-1.-controllers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
review_bids_controller.rb&lt;br /&gt;
&lt;br /&gt;
signup_sheet_controller.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-2.-models&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
review_bid.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-3.-helpers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
&lt;br /&gt;
authorization_helper.rb&lt;br /&gt;
&lt;br /&gt;
Review_bids_helper.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;viii.-test-plan&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
The Excel spreadsheet below details our test plan for testing the code we changed or created to ensure code coverage. The process involved reviewing the code and determining all the possible scenarios that needed to be covered based on what the method was doing, then creating test cases accordingly.&lt;br /&gt;
&lt;br /&gt;
* [https://docs.google.com/spreadsheets/d/1iHzOdSwlTtxOtf5VsNdwOaCP8NzYMmiJ/edit?usp=drive_link Open Test Matrix Spreadsheet]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/review_bids_controller_spec.rb ReviewBidsController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/sign_up_sheet_controller_spec.rb SignUpSheetController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/models/review_bid_spec.rb ReviewBid model updated test cases]&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-1.-mentor&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Ed Gehringer&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-2.-members&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Members ===&lt;br /&gt;
&lt;br /&gt;
* Gavin Teague&lt;br /&gt;
* Janice Uwujaren&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xi.-links&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
Previous Wiki: [https://wiki.expertiza.ncsu.edu/index.php/CSC/ECE_517_Fall_2021_-_E2151._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;Link&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xii.-references&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/document/d/1LHVacylQmB15I6VcYUA5nuC7GRRU9szHzZhjlPcE-PA/edit?tab=t.0#heading=h.u0oycoj6e0ev &amp;lt;u&amp;gt;Project Instructions&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;appendix-a-uml-class-diagram-plantuml-code-for-review-bids&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Juwujar</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160983</id>
		<title>CSC/ECE 517 Fall 2024 - E2485. Allow reviewers to bid on what to review</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160983"/>
		<updated>2024-12-11T01:32:43Z</updated>

		<summary type="html">&lt;p&gt;Juwujar: /* Optimize ReviewBidsController#set_priority Action functionality */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
This project will fix the defects and improve the functionality in Expertiza of the Reviewer Bidding feature, a process where reviewers can bid for assignments they want to review. It will make the functionality smoother, more reliable, and align with Expertiza's collaborative learning objectives.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iii.-problem-statements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Problem Statements ==&lt;br /&gt;
&lt;br /&gt;
Assigning reviews to users on Expertiza involves a complex bidding algorithm that requires optimization before production deployment. Below are key areas we plan to address as per the project requirements:&lt;br /&gt;
* Code Refactoring and Best Practices&lt;br /&gt;
** '''Utilize Authentication Utilities''': Modify the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; function in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to use existing authentication utilities, enhancing code reusability and adhering to the Single Responsibility Principle (SRP) and Separation of Concerns (SoC).&lt;br /&gt;
** '''Eliminate Code Duplication''': Refactor &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; to comply with the Don't Repeat Yourself (DRY) principle.&lt;br /&gt;
** '''Rename Functions for Clarity''': Change &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; to better reflect its purpose.&lt;br /&gt;
** '''Refactor Methods in '''&amp;lt;code&amp;gt;review_bid.rb&amp;lt;/code&amp;gt;''':''': Convert class methods to instance methods or relocate them to helper classes for improved code organization.&lt;br /&gt;
* Functionality Enhancements&lt;br /&gt;
** '''Handle Missing Bids Gracefully''': Adjust the bidding algorithm to accommodate students who do not bid, allowing them to select from the remaining options.&lt;br /&gt;
** '''Support Both Individual and Team Assignments''': Ensure the code functions correctly for participant and team-based assignments when signing up for topics.&lt;br /&gt;
* User Interface Improvements&lt;br /&gt;
** '''Increase Transparency in the Bidding Process''': Display messages indicating how many students are eligible to bid, how many have submitted bids, and the deadline.&lt;br /&gt;
** '''Accurate Bid Status Indicators''': Ensure topic colors change correctly based on the number of outstanding bids, providing clear visual feedback to users.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Exhaustive Edge Case Testing''': Conduct thorough testing for edge cases not previously covered, adding additional test cases as needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iv.-design-goals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Design Goals ==&lt;br /&gt;
&lt;br /&gt;
While addressing the existing defects and enhancing the Reviewer Bidding feature, we plan to adhere to the following design guidelines:&lt;br /&gt;
&lt;br /&gt;
* Code Quality and Maintainability&lt;br /&gt;
** '''Improve Code Structure: '''Implement refactoring strategies to enhance code readability and maintainability, ensuring adherence to best practices like the DRY principle.&lt;br /&gt;
** '''Standardize Authentication Logic: '''Utilize a centralized authentication utility to promote consistency and reusability across the application.&lt;br /&gt;
* System Stability and Flexibility&lt;br /&gt;
** '''Enhance Algorithm Robustness: '''Modify the bidding algorithm to handle scenarios where students do not bid, ensuring the system remains stable under all conditions.&lt;br /&gt;
** '''Ensure Assignment Versatility: '''Design the system to seamlessly support both individual and team assignments without additional configuration.&lt;br /&gt;
* User Experience Enhancements&lt;br /&gt;
** '''Increase Bidding Transparency: '''Develop user interface elements that clearly display bidding status, deadlines, and participation metrics.&lt;br /&gt;
** '''Implement Dynamic Visual Feedback: '''Use real-time visual cues, such as color changes, to inform users about the bidding status instantly.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Develop Comprehensive Test Suites: '''Create extensive automated tests covering edge cases and critical functionalities to guarantee reliable system behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;v.-uml-diagram&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== UML Diagram ==&lt;br /&gt;
&lt;br /&gt;
Below is a visual representation of the models associated with the review bids functionality, depicting the data structures and their relationships.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml class diagram2.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi.-implementation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
'''ReviewBid: '''The &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; manages the bidding process, where participants indicate their preferences for reviewing different topics or assignments. This helps distribute review tasks fairly based on participants' interests.&lt;br /&gt;
&lt;br /&gt;
'''Assignment: '''The &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model represents team or individual assignments in Expertiza. It stores all the essential information required to keep the assignment process organized, like managing participants, setting due dates, forming teams, and overseeing the review process.&lt;br /&gt;
&lt;br /&gt;
'''SignUpTopic: '''The &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt; model identifies specific topics or areas within an assignment for which participants can sign up. It manages the availability of these topics, tracks bids, coordinates team sign-ups, and assigns topics based on both bids and participant availability. &lt;br /&gt;
&lt;br /&gt;
'''Participant: '''The &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model is about the individual users who participate in an assignment. This includes students, instructors, teaching assistants, or other relevant roles. This model is responsible for managing key user information, monitoring interactions with the assignment, keeping track of team memberships, and recording things like badges and review grades.&lt;br /&gt;
&lt;br /&gt;
== Implementation ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1.-decoupling-authorization-logic-from-reviewbidscontroller&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Decoupling Authorization Logic from &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| '''&amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; needs to use the authentication utilities'''&lt;br /&gt;
&lt;br /&gt;
The function &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; should use the available authentication utilities to support the Single Responsibility Principle and Separation of Concerns and to support code reusability.&lt;br /&gt;
&lt;br /&gt;
| '''Authentication Consistency:''' Remove authentication logic in the controller and call logic in the authentication utility class instead to ensure consistent use of the same logic across the application, foster reusability, and eliminate DRY.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
==== Authorization Flow Before Refactoring ====&lt;br /&gt;
The sequence diagram depicts the authorization process within the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;. When a user invokes an action, the controller checks if the action is allowed by verifying the user's role through the &amp;lt;code&amp;gt;current_role_name()&amp;lt;/code&amp;gt; method in the &amp;lt;code&amp;gt;ApplicationController&amp;lt;/code&amp;gt;. Depending on the action and the user's role, the controller either authorizes the user to proceed or denies access, ultimately executing the action logic if authorized and responding to the user.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 1.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We simplified the &amp;lt;code&amp;gt;action_allowed?&amp;lt;/code&amp;gt; method by decoupling authorization logic from the controller to use helper methods in the &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt; module. The &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; should not handle authentication, which is a violation of SRP. &lt;br /&gt;
&lt;br /&gt;
Note: We also made similar updates to the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; methods for &amp;lt;code&amp;gt;SignUpController&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;vertical-align:top;&amp;quot; | Original Code !! style=&amp;quot;vertical-align:top;&amp;quot; | Refactored Code&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index'&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator',&lt;br /&gt;
     'Student'].include? current_role_name and&lt;br /&gt;
        ((%w[list].include? action_name) ? are_needed_authorizations_present?(params[:id], &amp;quot;participant&amp;quot;, &amp;quot;reader&amp;quot;, &amp;quot;submitter&amp;quot;, &amp;quot;reviewer&amp;quot;) : true)&lt;br /&gt;
  else&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator'].include? current_role_name&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index', 'list'&lt;br /&gt;
    current_user_has_student_privileges? &amp;amp;&amp;amp; list_authorization_check&lt;br /&gt;
  else&lt;br /&gt;
    current_user_has_ta_privileges?&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; action ===&lt;br /&gt;
Previously, the &amp;lt;code&amp;gt;ReviewBidsController's index&amp;lt;/code&amp;gt; action was overloaded with responsibilities—it handled everything from locating and validating assignments to fetching review mappings and counting completed reviews. This all-in-one approach made the controller action messy. &lt;br /&gt;
&lt;br /&gt;
We have moved the business logic from the controller action and into dedicated service objects. With this change, we introduced two key service objects: &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; focuses on retrieving the participant along with their assignments, while the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; takes on the task of managing review mappings and calculating review-related stats, like how many reviews have been completed. &lt;br /&gt;
&lt;br /&gt;
This separation of responsibilities aligns with the Single Responsibility Principle. The logic for fetching assignments, retrieving review mappings, and counting reviews is now more flexible and reusable throughout the application. For example, we used methods in the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; in both the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; and the &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;, helping to avoid code duplication.The article https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial served as a guide on when and how to use service objects in Rails.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
  return unless current_user_id?(@participant.user_id)&lt;br /&gt;
  @assignment = @participant.assignment&lt;br /&gt;
  @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
  # Finding how many reviews have been completed&lt;br /&gt;
  @num_reviews_completed = 0&lt;br /&gt;
  @review_mappings.each do |map|&lt;br /&gt;
    @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # render view for completing reviews after review bidding has been completed&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @assignment = participant_service.assignment&lt;br /&gt;
  unless @assignment.is_a?(Assignment)&lt;br /&gt;
    flash[:error] = 'Assignment not found.'&lt;br /&gt;
    redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  @review_mappings = review_service.review_mappings&lt;br /&gt;
  @num_reviews_completed = review_service.review_counts[:completed]&lt;br /&gt;
&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Optimize &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Action functionality ===&lt;br /&gt;
In the earlier version of the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, the server only needed a couple of key pieces of information: an array of topic IDs (params[:topic]) and the participant ID (params[:id}). From there, it would figure out the assignment_id by using the first topic as a reference. &lt;br /&gt;
&lt;br /&gt;
With the new implementation, we structured the parameters to include the participant_id, an array of topic IDs, and assignment_id. This change ensures that the server always receives complete and reliable information, preventing issues caused by missing or inconsistent data. It also removes the need for the server to perform unnecessary lookups as in the case of assignment_id, allowing the controller to focus on its main job of receiving requests and rendering appropriate responses.&lt;br /&gt;
&lt;br /&gt;
While these updates have cleaned up the set_priority action, the action still contains a lot of business logic, like validations and priority updates. To make the code even more maintainable, there’s room for further refactoring—perhaps moving some of this logic into a service class or model method to streamline the controller even more.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | Old Code&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    if params[:topic].nil?&lt;br /&gt;
      ReviewBid.where(participant_id: params[:id]).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      participant = AssignmentParticipant.find_by(id: params[:id])&lt;br /&gt;
      assignment_id = SignUpTopic.find(params[:topic].first).assignment.id&lt;br /&gt;
      @bids = ReviewBid.where(participant_id: params[:id])&lt;br /&gt;
      signed_up_topics = ReviewBid.where(participant_id: params[:id]).map(&amp;amp;:signuptopic_id)&lt;br /&gt;
      signed_up_topics -= params[:topic].map(&amp;amp;:to_i)&lt;br /&gt;
      signed_up_topics.each do |topic|&lt;br /&gt;
        ReviewBid.where(signuptopic_id: topic, participant_id: params[:id]).destroy_all&lt;br /&gt;
      end&lt;br /&gt;
      params[:topic].each_with_index do |topic_id, index|&lt;br /&gt;
        bid_existence = ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id])&lt;br /&gt;
        if bid_existence.empty?&lt;br /&gt;
          ReviewBid.create(priority: index + 1, signuptopic_id: topic_id, participant_id: params[:id], assignment_id: assignment_id)&lt;br /&gt;
        else&lt;br /&gt;
          ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id]).update_all(priority: index + 1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to action: 'show', assignment_id: params[:assignment_id], id: params[:id]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    participant_id = params[:participant_id].to_i&lt;br /&gt;
    selected_topic_ids = params[:topic]&amp;amp;.map(&amp;amp;:to_i) || []&lt;br /&gt;
    assignment_id = params[:assignment_id].to_i&lt;br /&gt;
&lt;br /&gt;
    unless current_user_can_modify_participant?(participant_id)&lt;br /&gt;
      flash[:error] = 'You are not authorized to modify these bids.'&lt;br /&gt;
      respond_to do |format|&lt;br /&gt;
        format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
        format.json { render json: { status: 'unauthorized' }, status: :unauthorized }&lt;br /&gt;
      end&lt;br /&gt;
      return&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    if selected_topic_ids.empty?&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      assignment = Assignment.find_by(id: assignment_id)&lt;br /&gt;
      if assignment.nil?&lt;br /&gt;
        flash[:error] = &amp;quot;Invalid assignment.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_assignment' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      unless SignUpTopic.where(id: selected_topic_ids, assignment_id: assignment_id).count == selected_topic_ids.size&lt;br /&gt;
        flash[:error] = &amp;quot;One or more selected topics are invalid.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_topics' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).where.not(signuptopic_id: selected_topic_ids).destroy_all&lt;br /&gt;
&lt;br /&gt;
      selected_topic_ids.each_with_index do |topic_id, index|&lt;br /&gt;
        bid = ReviewBid.find_or_initialize_by(signuptopic_id: topic_id, participant_id: participant_id)&lt;br /&gt;
        bid.priority = index + 1&lt;br /&gt;
        bid.assignment_id = assignment_id&lt;br /&gt;
        bid.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_to action: 'show', assignment_id: assignment_id, id: participant_id, notice: 'Review bids updated successfully.' }&lt;br /&gt;
      format.json { render json: { status: 'success' }, status: :ok }&lt;br /&gt;
    end&lt;br /&gt;
  rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
    flash[:error] = &amp;quot;Failed to update priorities: #{e.message}&amp;quot;&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
      format.json { render json: { status: 'error', message: e.message }, status: :unprocessable_entity }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Client Side Changes to Support &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Changes ====&lt;br /&gt;
After tidying up the server-side logic, we needed to refactor the client-side code to ensure it passed the correct information from the view to the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action. On the client side, we now grab the topicIDs and send them as &amp;lt;code&amp;gt;selected_topic_ids&amp;lt;/code&amp;gt;, allowing the controller to figure out which topics were selected and in what order.  &lt;br /&gt;
&lt;br /&gt;
We also extracted the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, making it a lot easier to send data with AJAX and keeping things flexible. When working with jQuery, AJAX is awesome because it lets us update the server without reloading the page, making the user experience smooth and responsive. When we pull the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, we can grab it along with other data, like the list of topics, and include it in the request. This way, we don’t have to rely on the URL to know which participant we’re working with, which makes the code cleaner and more adaptable.&lt;br /&gt;
&lt;br /&gt;
If we were to rely on the route (as opposed to using the DOM) for the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt;, every AJAX request would need to be tied to a specific URL structure. That’s limiting, especially if the route doesn’t naturally include the participant ID, as can be the case with collection routes versus member routes in Rails. &lt;br /&gt;
&lt;br /&gt;
Note: One interesting aspect of the application was the mix of JavaScript and CoffeeScript code. It wasn't clear why both types of code were used.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery -&amp;gt;&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: -&amp;gt;&lt;br /&gt;
      $.post($(this).data('update-url'), $(this).sortable('serialize'))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery(document).ready ($) -&amp;gt;&lt;br /&gt;
  sendUpdatedOrder = -&amp;gt;&lt;br /&gt;
    sortedIDs = $(this).sortable('toArray')&lt;br /&gt;
&lt;br /&gt;
    topicIDs = sortedIDs.map (id) -&amp;gt;&lt;br /&gt;
      id.replace 'topic_', ''&lt;br /&gt;
&lt;br /&gt;
    participant_id = $(this).data('participant-id')&lt;br /&gt;
&lt;br /&gt;
    assignment_id = new URL($(this).data('update-url'), window.location.origin).searchParams.get('assignment_id')&lt;br /&gt;
&lt;br /&gt;
    $.ajax&lt;br /&gt;
      type: 'POST'&lt;br /&gt;
      url: $(this).data('update-url')&lt;br /&gt;
      data:&lt;br /&gt;
        topic: topicIDs&lt;br /&gt;
        participant_id: participant_id&lt;br /&gt;
        assignment_id: assignment_id&lt;br /&gt;
      success: (response) -&amp;gt;&lt;br /&gt;
        console.log 'Selections updated successfully.'&lt;br /&gt;
      error: (xhr, status, error) -&amp;gt;&lt;br /&gt;
        alert 'An error occurred while saving your selections. Please try again.'&lt;br /&gt;
        console.error error&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: sendUpdatedOrder&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-1.-signupsheetcontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; Updates ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; method renders the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; action in &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt;. However, the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; does not include the controller action for this view. We need to find the old implementation of the controller action, add it back, and review and update it for any DRY violations.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # provides variables for reviewing page located at views/review_bids/others_work.html.erb&lt;br /&gt;
  def index&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
    return unless current_user_id?(@participant.user_id)&lt;br /&gt;
&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
    # Finding how many reviews have been completed&lt;br /&gt;
    @num_reviews_completed = 0&lt;br /&gt;
    @review_mappings.each do |map|&lt;br /&gt;
      @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # render view for completing reviews after review bidding has been completed&lt;br /&gt;
    render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
  end  &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-2.-reviewbidsotherswork-view-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;review_bids_others_work.html.erb&amp;lt;/code&amp;gt; View Updates ====&lt;br /&gt;
&lt;br /&gt;
The code only contains a view with this name and no associated controller action. Reviewing the code in the view, we noticed some SRP violations where there is much logic in the presentation layer. Set up helper methods in the controller to handle some of that logic or see if the model can handle some of that logic.&lt;br /&gt;
&lt;br /&gt;
Note: The view is technically part of the SignUpSheetController, but it's being rendered by the ReviewBidsController, which initially threw us off. After chatting with the professor, we decided not to change this view. He indicated that the plan is to reuse these views for both assignment bidding and review bidding.&lt;br /&gt;
&lt;br /&gt;
We would suggest building the assignment bidding and review bidding features separately first to make sure each one works on its own. Once that’s done, we can go back and clean things up by combining any shared functionality. This could include creating shared views or even introducing an abstract model to handle both types of bidding in a cleaner way. It could also mean replacing the ReviewBidsController with something more generic, like a BidsController, to keep things reusable and easier to maintain down the line.&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Rename &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; method ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; should be &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt;&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:''' Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The primary activity here is to refactor the method name &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; and fix the hardcoded URL. However, the match topic bidding algorithm no longer works since Heroku is no longer free, per the professor. The reference link below has detailed information about the web service that we can leverage to gain that understanding.&lt;br /&gt;
&lt;br /&gt;
Reference Link for Webservice Details: [https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Updated Implementation ====&lt;br /&gt;
The original requirement was to rename the run_bidding_algorithm method to assign_reviewers, but after digging into the code, we realized that renaming it didn’t make sense. The method runs the bidding algorithm to match topics, and there’s already a separate method to handle assigning review topics.&lt;br /&gt;
&lt;br /&gt;
However, we improved the logic in the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; method (which calls the private &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt;) by introducing a separate &amp;lt;code&amp;gt;BiddingAlgorithmService&amp;lt;/code&amp;gt; object. This keeps the business logic out of the controller action, which makes things cleaner and more maintainable.&lt;br /&gt;
&lt;br /&gt;
==== review_bids_controller ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def run_bidding_algorithm&lt;br /&gt;
    review_bid = ReviewBid.new&lt;br /&gt;
    bidding_data = review_bid.bidding_data&lt;br /&gt;
    matched_topics = BiddingAlgorithmService.new(bidding_data).run&lt;br /&gt;
    raise ArgumentError, 'Failed to assign reviewers. Please try again later.' unless matched_topics&lt;br /&gt;
&lt;br /&gt;
    matched_topics&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== bidding_algorithm_service ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# frozen_string_literal: true&lt;br /&gt;
&lt;br /&gt;
# The `BiddingAlgorithmService` run the bid assignment algorithm&lt;br /&gt;
# Sends student IDs, topic IDs, student preferences, and timestamps to the web service&lt;br /&gt;
# The web service returns the matched assignments in the JSON response body&lt;br /&gt;
class BiddingAlgorithmService&lt;br /&gt;
  SERVICE_URL = 'http://app-csc517.herokuapp.com/match_topics'.freeze&lt;br /&gt;
&lt;br /&gt;
  # MOCK_DATA is based on the documentation detailing how the webservice behaves.&lt;br /&gt;
  # Reference: https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review#Webservice&lt;br /&gt;
  MOCK_DATA = {&lt;br /&gt;
    36239 =&amp;gt; [3970, 3972, 3975],&lt;br /&gt;
    36240 =&amp;gt; [3973, 3974, 3972],&lt;br /&gt;
    36241 =&amp;gt; [3969, 3971, 3972],&lt;br /&gt;
    36242 =&amp;gt; [3969, 3971, 3973],&lt;br /&gt;
    36243 =&amp;gt; [3969, 3970, 3971]&lt;br /&gt;
  }.freeze&lt;br /&gt;
&lt;br /&gt;
  def initialize(bidding_data)&lt;br /&gt;
    @bidding_data = bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def run&lt;br /&gt;
    return MOCK_DATA if Rails.application.config.use_mock_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
    perform_request&lt;br /&gt;
  rescue RestClient::ExceptionWithResponse, JSON::ParserError&lt;br /&gt;
    false&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def perform_request&lt;br /&gt;
    response = RestClient.post(&lt;br /&gt;
      SERVICE_URL,&lt;br /&gt;
      @bidding_data.to_json,&lt;br /&gt;
      content_type: :json,&lt;br /&gt;
      accept: :json&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    validate_response(response)&lt;br /&gt;
    JSON.parse(response.body)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def validate_response(response)&lt;br /&gt;
    raise 'Invalid response format' unless response.headers[:content_type] &amp;amp;&amp;amp; response.headers[:content_type].include?('application/json')&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Resolve Functionality Issues for Non Bidders ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Currently it doesn't work if some student does not bid. In this case, algorithm needs to be fixed to ignore anyone who didn’t bid, and let them choose from what’s left over.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to do several things within this controller. We will set a default state for non-bidders that we can point to to give them options to select topics to review from the leftovers. We will consolidate &amp;lt;code&amp;gt;@sign_up_topics&amp;lt;/code&amp;gt; to resolve the DRY violation. Lastly, we will remove business logic from the show method and push it to a new helper to conform to the SRP.&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # Additional method&lt;br /&gt;
&lt;br /&gt;
  # Provides variables for review bidding page&lt;br /&gt;
  def show&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id].to_i)&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @sign_up_topics = SignUpTopic.where(assignment_id: @assignment.id, private_to: nil)&lt;br /&gt;
    my_topic = SignedUpTeam.topic_id(@participant.parent_id, @participant.user_id)&lt;br /&gt;
    @sign_up_topics -= SignUpTopic.where(assignment_id: @assignment.id, id: my_topic)&lt;br /&gt;
    @num_participants = AssignmentParticipant.where(parent_id: @assignment.id).count&lt;br /&gt;
    @selected_topics = nil # This is used to list the topics assigned to review (i.e., select == assigned)&lt;br /&gt;
    @bids = ReviewBid.where(participant_id: @participant, assignment_id: @assignment.id)&lt;br /&gt;
    signed_up_topics = []&lt;br /&gt;
    &lt;br /&gt;
    @bids.each do |bid|&lt;br /&gt;
      sign_up_topic = SignUpTopic.find_by(id: bid.signuptopic_id)&lt;br /&gt;
      signed_up_topics &amp;lt;&amp;lt; sign_up_topic if sign_up_topic&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    signed_up_topics &amp;amp;= @sign_up_topics&lt;br /&gt;
    @sign_up_topics -= signed_up_topics&lt;br /&gt;
    @bids = signed_up_topics&lt;br /&gt;
    @num_of_topics = @sign_up_topics.size&lt;br /&gt;
    @assigned_review_maps = []&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: @assignment.id, reviewer_id: @participant.id).each do |review_map|&lt;br /&gt;
      @assigned_review_maps &amp;lt;&amp;lt; review_map&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Explicitly render view since it's in the sign up sheet views&lt;br /&gt;
    render 'sign_up_sheet/review_bids_show'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Additional methods&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-2.-reviewbidshelper-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
 # Assigns bidding topics to reviewers&lt;br /&gt;
  def assign_bidding&lt;br /&gt;
      assignment = validate_assignment(params[:assignment_id])&lt;br /&gt;
&lt;br /&gt;
      reviewers = validate_reviewers(assignment.id)&lt;br /&gt;
      reviewer_ids = reviewers.map(&amp;amp;:id)&lt;br /&gt;
&lt;br /&gt;
      matched_topics = run_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
      if matched_topics.blank?&lt;br /&gt;
        flash[:alert] = 'Topic or assignment is missing'&lt;br /&gt;
        redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      leftover_topics = find_leftover_topics(assignment.id, matched_topics)&lt;br /&gt;
      assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.new.assign_review_topics(matched_topics)&lt;br /&gt;
      assignment.update!(can_choose_topic_to_review: false)&lt;br /&gt;
&lt;br /&gt;
      flash[:notice] = 'Reviewers were successfully assigned to topics.'&lt;br /&gt;
      redirect_back fallback_location: root_path&lt;br /&gt;
    rescue ArgumentError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ArgumentError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: e.message&lt;br /&gt;
    rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::RecordInvalid: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue ActiveRecord::ActiveRecordError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::ActiveRecordError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue StandardError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;StandardError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers. Please try again later.'&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  #service methods here ...&lt;br /&gt;
&lt;br /&gt;
  def find_leftover_topics(assignment_id, matched_topics)&lt;br /&gt;
    all_topic_ids = SignUpTopic.where(assignment_id: assignment_id).pluck(:id)&lt;br /&gt;
    assigned_topic_ids = matched_topics.map { |match| match[:topic_id] }&lt;br /&gt;
&lt;br /&gt;
    # Calculate leftover topics&lt;br /&gt;
    all_topic_ids - assigned_topic_ids&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
    return if leftover_topics.blank?&lt;br /&gt;
&lt;br /&gt;
    # Find non-bidders by excluding already matched reviewers&lt;br /&gt;
    non_bidders = reviewer_ids - matched_topics.keys&lt;br /&gt;
&lt;br /&gt;
    # Assign leftover topics to non-bidders in a round-robin fashion&lt;br /&gt;
    non_bidders.each_with_index do |reviewer_id, index|&lt;br /&gt;
      topic_id = leftover_topics[index % leftover_topics.length]&lt;br /&gt;
      ReviewBid.create(priority: 1, signuptopic_id: topic_id, participant_id: reviewer_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirm Functionality for Single-person Teams ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Make sure your code works on individual assignments, i.e., assignments where participants sign up for topics instead of teams. In this case, a team is created for each participant when they sign up. So the code should work for assignments to which either individuals or teams submit.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to make an update to focus on topic rather than team or have it where teams are updated based on topic id.We will probably utilize sign up topic or a similar object to accomplish this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-5-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bid Status Messaging ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| To make the functionality more intuitive, include a message to say how many students are eligible to submit bids, how many have submitted their bids, when the deadline for submitting bids is.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
The logic for these messages will go into the helper file. It is effectively the same reasoning as our migration of other business/functionality code from the controller to the helper to conform to the SRP. It will go hand in hand with the SignUpTopic portion of the code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-6-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Why are the methods in review_bid.rb class methods? Can we change them to instance methods or move it to helpers?&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-1.-reviewbid-model-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model Updates ====&lt;br /&gt;
Since the current class methods are tied to &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model instances, we will change them to instance methods. There is no need to move them to helpers.&lt;br /&gt;
&lt;br /&gt;
===== Review Bid Model Flow Before Refactoring =====&lt;br /&gt;
This sequence diagram describes how the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; interacts with the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model in assigning review topics to reviewers according to their bids. Once the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; action is initiated by the user, the controller gathers the IDs of reviewers and calls &amp;lt;code&amp;gt;ReviewBid.bidding_data&amp;lt;/code&amp;gt; to gather bidding information. That is sent out to an external bidding algorithm, and the matched topics that come back are used by &amp;lt;code&amp;gt;ReviewBid.assign_review_topics&amp;lt;/code&amp;gt; to create &amp;lt;code&amp;gt;ReviewResponseMap&amp;lt;/code&amp;gt; entries, assigning topics to reviewers for the assignment.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
  belongs_to :assignment, class_name: 'Assignment'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  def self.bidding_data(assignment_id, reviewer_ids)&lt;br /&gt;
    # create basic hash and set basic hash data&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'users' =&amp;gt; {}, 'max_accepted_proposals' =&amp;gt; [] }&lt;br /&gt;
    bidding_data['tid'] = SignUpTopic.where(assignment_id: assignment_id).ids&lt;br /&gt;
    bidding_data['max_accepted_proposals'] = Assignment.where(id: assignment_id).pluck(:num_reviews_allowed).first&lt;br /&gt;
&lt;br /&gt;
    # loop through reviewer_ids to get reviewer specific bidding data&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      bidding_data['users'][reviewer_id] = reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # assigns topics to reviews as matched by the webservice algorithm&lt;br /&gt;
  def self.assign_review_topics(assignment_id, reviewer_ids, matched_topics, _min_num_reviews = 2)&lt;br /&gt;
    # if review response map already created, delete it&lt;br /&gt;
    if ReviewResponseMap.where(reviewed_object_id: assignment_id)&lt;br /&gt;
      ReviewResponseMap.where(reviewed_object_id: assignment_id).destroy_all&lt;br /&gt;
    end&lt;br /&gt;
    # loop through reviewer_ids to assign reviews to each reviewer&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      topics_to_assign = matched_topics[reviewer_id.to_s]&lt;br /&gt;
      topics_to_assign.each do |topic|&lt;br /&gt;
        assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method to assign a single topic to a reviewer&lt;br /&gt;
  def self.assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic).pluck(:team_id).first&lt;br /&gt;
    team_to_review.nil? ? [] : ReviewResponseMap.create(reviewed_object_id: assignment_id, reviewer_id: reviewer_id, reviewee_id: team_to_review, type: 'ReviewResponseMap')&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method for getting individual reviewer_ids bidding data&lt;br /&gt;
  # returns user's bidding data hash&lt;br /&gt;
  def self.reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    reviewer_user_id = AssignmentParticipant.find(reviewer_id).user_id&lt;br /&gt;
    self_topic = SignedUpTeam.topic_id(assignment_id, reviewer_user_id)&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'otid' =&amp;gt; self_topic, 'priority' =&amp;gt; [], 'time' =&amp;gt; [] }&lt;br /&gt;
    bids = ReviewBid.where(participant_id: reviewer_id)&lt;br /&gt;
&lt;br /&gt;
    # loop through each bid for a topic to get specific data&lt;br /&gt;
    bids.each do |bid|&lt;br /&gt;
      bidding_data['tid'] &amp;lt;&amp;lt; bid.signuptopic_id&lt;br /&gt;
      bidding_data['priority'] &amp;lt;&amp;lt; bid.priority&lt;br /&gt;
      bidding_data['time'] &amp;lt;&amp;lt; bid.updated_at&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  # Instance method to get bidding data for this review bid&lt;br /&gt;
  def bidding_data&lt;br /&gt;
    {&lt;br /&gt;
      'tid' =&amp;gt; topic_ids,&lt;br /&gt;
      'otid' =&amp;gt; self_topic,&lt;br /&gt;
      'priority' =&amp;gt; priorities,&lt;br /&gt;
      'time' =&amp;gt; updated_times&lt;br /&gt;
    }&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # bid object to limit db touches for similar data acquisitions&lt;br /&gt;
  def bids&lt;br /&gt;
    @bids ||= ReviewBid.where(participant_id: participant_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def topic_ids&lt;br /&gt;
    bids.pluck(:signuptopic_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def priorities&lt;br /&gt;
    bids.pluck(:priority)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def updated_times&lt;br /&gt;
    bids.pluck(:updated_at)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self_topic&lt;br /&gt;
    return nil unless topic &amp;amp;&amp;amp; topic.assignment_id&lt;br /&gt;
&lt;br /&gt;
    SignedUpTeam.topic_id(topic.assignment_id, participant.user_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign topics to reviews&lt;br /&gt;
  def assign_review_topics(matched_topics)&lt;br /&gt;
    validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: topic.assignment_id).destroy_all&lt;br /&gt;
&lt;br /&gt;
    matched_topics.each do |reviewer_id, topics|&lt;br /&gt;
      Array(topics).each { |topic_id| assign_topic_to_reviewer(reviewer_id, topic_id) }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign a single topic to a reviewer&lt;br /&gt;
  def assign_topic_to_reviewer(reviewer_id, topic_id)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic_id).pluck(:team_id).first&lt;br /&gt;
    return if team_to_review.nil?&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.create(&lt;br /&gt;
      reviewed_object_id: topic.assignment_id,&lt;br /&gt;
      reviewer_id: reviewer_id,&lt;br /&gt;
      reviewee_id: team_to_review,&lt;br /&gt;
      type: 'ReviewResponseMap'&lt;br /&gt;
    )&lt;br /&gt;
    # error handling&lt;br /&gt;
  rescue StandardError =&amp;gt; e&lt;br /&gt;
    Rails.logger.error(&amp;quot;Failed to assign topic #{topic_id} to reviewer #{reviewer_id}: #{e.message}&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
    raise ArgumentError, 'Topic or assignment is missing' if topic.nil? || topic.assignment_id.nil?&lt;br /&gt;
    raise ArgumentError, 'Matched topics must be a Hash' unless matched_topics.is_a?(Hash)&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
Once we changed them to instance methods, we modified their use in the controller code to instantiate the object first.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-3.-schema.db-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;Schema.db&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We also need to remove the foreign key reference to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model since the &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model already has access to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;, and that is the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; we should be referencing. This will also involve modifying the database to remove the foreign key reference to the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Steps required:&lt;br /&gt;
&lt;br /&gt;
# Review the records in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table in the database to see if there are any records and if those records have values for &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;&lt;br /&gt;
# Create a migration file using the command: &amp;lt;code&amp;gt;rails generate migration RemoveAssignmentFromReviewBids&amp;lt;/code&amp;gt;&lt;br /&gt;
# In the generated file, indicate the change that needs to be made using the change method and migration columns &amp;lt;code&amp;gt;remove_foreign_key&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;remove_column&amp;lt;/code&amp;gt;. Migration should be straightforward since there is no cascading behavior (i.e., &amp;lt;code&amp;gt;dependent:destroy&amp;lt;/code&amp;gt;) to worry about.&lt;br /&gt;
# Once comfortable with the updates to the generated file, run the migration using the &amp;lt;code&amp;gt;rails db:migrate&amp;lt;/code&amp;gt; command.&lt;br /&gt;
# Run a query in the DB to ensure no orphaned records are in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table after the updates.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-4.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implement Edge Case Testing ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| In the previous implementation wiki, there are edge cases which are not exhaustively tested. Should test those edge cases thoroughly and add more edge case testing - link&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span class=&amp;quot;mark&amp;quot;&amp;gt;TODO&amp;lt;/span&amp;gt;:We just got initial test results back. We still need to figure out why tests are failing and what may be slipping through the cracks.[[File:Test expertiza.png|411x459px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-8-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design Patterns and Principles ===&lt;br /&gt;
Below are the design patterns and principles we plan to use to address the problem statements outlined in the project requirements:&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
** Controller Responsibility&lt;br /&gt;
*** To address the issue of authentication logic residing in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, we will refactor the code to ensure the controller solely handles request and response flows.&lt;br /&gt;
*** Authentication logic will be moved to dedicated methods in &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt;, making the codebase easier to maintain and test.&lt;br /&gt;
** View Simplification&lt;br /&gt;
*** Currently, the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; view contains excessive business logic, violating SRP.&lt;br /&gt;
*** We will move this logic into helper methods or models, ensuring each component has a single responsibility and improving code readability.&lt;br /&gt;
&lt;br /&gt;
* Separation of Concerns (SoC)&lt;br /&gt;
** By relocating authorization logic out of the controller, we separate authentication from request handling.&lt;br /&gt;
** Adhering to SoC enhances readability and maintainability by allowing each part of the application to focus on its specific role.&lt;br /&gt;
* Don't Repeat Yourself (DRY)&lt;br /&gt;
** We will inspect the existing implementation for code duplication, particularly in the controller actions.&lt;br /&gt;
** Any repetitive code will be refactored or removed to reduce redundancy and improve clarity.&lt;br /&gt;
&lt;br /&gt;
* Model-View-Controller (MVC) Adherence&lt;br /&gt;
** Moving business logic from the view aligns with MVC best practices.&lt;br /&gt;
** By ensuring the view only presents data without processing it, we maintain clear boundaries between the model, view, and controller layers.&lt;br /&gt;
&lt;br /&gt;
* Dependency Inversion Principle (DIP)&lt;br /&gt;
** We plan to update class methods to instance methods, allowing the controller to depend on abstractions rather than concrete implementations.&lt;br /&gt;
** This change leverages Ruby's duck typing, providing flexibility and making the code easier to maintain and test.&lt;br /&gt;
&lt;br /&gt;
** Clear and intentional naming during refactoring will enhance code clarity and maintainability.&lt;br /&gt;
&lt;br /&gt;
== Files Modified/Added ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-1.-controllers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
review_bids_controller.rb&lt;br /&gt;
&lt;br /&gt;
signup_sheet_controller.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-2.-models&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
review_bid.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-3.-helpers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
&lt;br /&gt;
authorization_helper.rb&lt;br /&gt;
&lt;br /&gt;
Review_bids_helper.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;viii.-test-plan&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
The Excel spreadsheet below details our test plan for testing the code we changed or created to ensure code coverage. The process involved reviewing the code and determining all the possible scenarios that needed to be covered based on what the method was doing, then creating test cases accordingly.&lt;br /&gt;
&lt;br /&gt;
* [https://docs.google.com/spreadsheets/d/1iHzOdSwlTtxOtf5VsNdwOaCP8NzYMmiJ/edit?usp=drive_link Open Test Matrix Spreadsheet]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/review_bids_controller_spec.rb ReviewBidsController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/sign_up_sheet_controller_spec.rb SignUpSheetController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/models/review_bid_spec.rb ReviewBid model updated test cases]&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-1.-mentor&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Ed Gehringer&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-2.-members&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Members ===&lt;br /&gt;
&lt;br /&gt;
* Gavin Teague&lt;br /&gt;
* Janice Uwujaren&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xi.-links&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
Previous Wiki: [https://wiki.expertiza.ncsu.edu/index.php/CSC/ECE_517_Fall_2021_-_E2151._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;Link&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xii.-references&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/document/d/1LHVacylQmB15I6VcYUA5nuC7GRRU9szHzZhjlPcE-PA/edit?tab=t.0#heading=h.u0oycoj6e0ev &amp;lt;u&amp;gt;Project Instructions&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;appendix-a-uml-class-diagram-plantuml-code-for-review-bids&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Juwujar</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160982</id>
		<title>CSC/ECE 517 Fall 2024 - E2485. Allow reviewers to bid on what to review</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160982"/>
		<updated>2024-12-11T01:27:54Z</updated>

		<summary type="html">&lt;p&gt;Juwujar: /* Optimize ReviewBidsController#set_priority Action functionality */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
This project will fix the defects and improve the functionality in Expertiza of the Reviewer Bidding feature, a process where reviewers can bid for assignments they want to review. It will make the functionality smoother, more reliable, and align with Expertiza's collaborative learning objectives.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iii.-problem-statements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Problem Statements ==&lt;br /&gt;
&lt;br /&gt;
Assigning reviews to users on Expertiza involves a complex bidding algorithm that requires optimization before production deployment. Below are key areas we plan to address as per the project requirements:&lt;br /&gt;
* Code Refactoring and Best Practices&lt;br /&gt;
** '''Utilize Authentication Utilities''': Modify the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; function in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to use existing authentication utilities, enhancing code reusability and adhering to the Single Responsibility Principle (SRP) and Separation of Concerns (SoC).&lt;br /&gt;
** '''Eliminate Code Duplication''': Refactor &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; to comply with the Don't Repeat Yourself (DRY) principle.&lt;br /&gt;
** '''Rename Functions for Clarity''': Change &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; to better reflect its purpose.&lt;br /&gt;
** '''Refactor Methods in '''&amp;lt;code&amp;gt;review_bid.rb&amp;lt;/code&amp;gt;''':''': Convert class methods to instance methods or relocate them to helper classes for improved code organization.&lt;br /&gt;
* Functionality Enhancements&lt;br /&gt;
** '''Handle Missing Bids Gracefully''': Adjust the bidding algorithm to accommodate students who do not bid, allowing them to select from the remaining options.&lt;br /&gt;
** '''Support Both Individual and Team Assignments''': Ensure the code functions correctly for participant and team-based assignments when signing up for topics.&lt;br /&gt;
* User Interface Improvements&lt;br /&gt;
** '''Increase Transparency in the Bidding Process''': Display messages indicating how many students are eligible to bid, how many have submitted bids, and the deadline.&lt;br /&gt;
** '''Accurate Bid Status Indicators''': Ensure topic colors change correctly based on the number of outstanding bids, providing clear visual feedback to users.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Exhaustive Edge Case Testing''': Conduct thorough testing for edge cases not previously covered, adding additional test cases as needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iv.-design-goals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Design Goals ==&lt;br /&gt;
&lt;br /&gt;
While addressing the existing defects and enhancing the Reviewer Bidding feature, we plan to adhere to the following design guidelines:&lt;br /&gt;
&lt;br /&gt;
* Code Quality and Maintainability&lt;br /&gt;
** '''Improve Code Structure: '''Implement refactoring strategies to enhance code readability and maintainability, ensuring adherence to best practices like the DRY principle.&lt;br /&gt;
** '''Standardize Authentication Logic: '''Utilize a centralized authentication utility to promote consistency and reusability across the application.&lt;br /&gt;
* System Stability and Flexibility&lt;br /&gt;
** '''Enhance Algorithm Robustness: '''Modify the bidding algorithm to handle scenarios where students do not bid, ensuring the system remains stable under all conditions.&lt;br /&gt;
** '''Ensure Assignment Versatility: '''Design the system to seamlessly support both individual and team assignments without additional configuration.&lt;br /&gt;
* User Experience Enhancements&lt;br /&gt;
** '''Increase Bidding Transparency: '''Develop user interface elements that clearly display bidding status, deadlines, and participation metrics.&lt;br /&gt;
** '''Implement Dynamic Visual Feedback: '''Use real-time visual cues, such as color changes, to inform users about the bidding status instantly.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Develop Comprehensive Test Suites: '''Create extensive automated tests covering edge cases and critical functionalities to guarantee reliable system behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;v.-uml-diagram&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== UML Diagram ==&lt;br /&gt;
&lt;br /&gt;
Below is a visual representation of the models associated with the review bids functionality, depicting the data structures and their relationships.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml class diagram2.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi.-implementation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
'''ReviewBid: '''The &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; manages the bidding process, where participants indicate their preferences for reviewing different topics or assignments. This helps distribute review tasks fairly based on participants' interests.&lt;br /&gt;
&lt;br /&gt;
'''Assignment: '''The &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model represents team or individual assignments in Expertiza. It stores all the essential information required to keep the assignment process organized, like managing participants, setting due dates, forming teams, and overseeing the review process.&lt;br /&gt;
&lt;br /&gt;
'''SignUpTopic: '''The &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt; model identifies specific topics or areas within an assignment for which participants can sign up. It manages the availability of these topics, tracks bids, coordinates team sign-ups, and assigns topics based on both bids and participant availability. &lt;br /&gt;
&lt;br /&gt;
'''Participant: '''The &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model is about the individual users who participate in an assignment. This includes students, instructors, teaching assistants, or other relevant roles. This model is responsible for managing key user information, monitoring interactions with the assignment, keeping track of team memberships, and recording things like badges and review grades.&lt;br /&gt;
&lt;br /&gt;
== Implementation ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1.-decoupling-authorization-logic-from-reviewbidscontroller&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Decoupling Authorization Logic from &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| '''&amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; needs to use the authentication utilities'''&lt;br /&gt;
&lt;br /&gt;
The function &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; should use the available authentication utilities to support the Single Responsibility Principle and Separation of Concerns and to support code reusability.&lt;br /&gt;
&lt;br /&gt;
| '''Authentication Consistency:''' Remove authentication logic in the controller and call logic in the authentication utility class instead to ensure consistent use of the same logic across the application, foster reusability, and eliminate DRY.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
==== Authorization Flow Before Refactoring ====&lt;br /&gt;
The sequence diagram depicts the authorization process within the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;. When a user invokes an action, the controller checks if the action is allowed by verifying the user's role through the &amp;lt;code&amp;gt;current_role_name()&amp;lt;/code&amp;gt; method in the &amp;lt;code&amp;gt;ApplicationController&amp;lt;/code&amp;gt;. Depending on the action and the user's role, the controller either authorizes the user to proceed or denies access, ultimately executing the action logic if authorized and responding to the user.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 1.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We simplified the &amp;lt;code&amp;gt;action_allowed?&amp;lt;/code&amp;gt; method by decoupling authorization logic from the controller to use helper methods in the &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt; module. The &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; should not handle authentication, which is a violation of SRP. &lt;br /&gt;
&lt;br /&gt;
Note: We also made similar updates to the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; methods for &amp;lt;code&amp;gt;SignUpController&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;vertical-align:top;&amp;quot; | Original Code !! style=&amp;quot;vertical-align:top;&amp;quot; | Refactored Code&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index'&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator',&lt;br /&gt;
     'Student'].include? current_role_name and&lt;br /&gt;
        ((%w[list].include? action_name) ? are_needed_authorizations_present?(params[:id], &amp;quot;participant&amp;quot;, &amp;quot;reader&amp;quot;, &amp;quot;submitter&amp;quot;, &amp;quot;reviewer&amp;quot;) : true)&lt;br /&gt;
  else&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator'].include? current_role_name&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index', 'list'&lt;br /&gt;
    current_user_has_student_privileges? &amp;amp;&amp;amp; list_authorization_check&lt;br /&gt;
  else&lt;br /&gt;
    current_user_has_ta_privileges?&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; action ===&lt;br /&gt;
Previously, the &amp;lt;code&amp;gt;ReviewBidsController's index&amp;lt;/code&amp;gt; action was overloaded with responsibilities—it handled everything from locating and validating assignments to fetching review mappings and counting completed reviews. This all-in-one approach made the controller action messy. &lt;br /&gt;
&lt;br /&gt;
We have moved the business logic from the controller action and into dedicated service objects. With this change, we introduced two key service objects: &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; focuses on retrieving the participant along with their assignments, while the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; takes on the task of managing review mappings and calculating review-related stats, like how many reviews have been completed. &lt;br /&gt;
&lt;br /&gt;
This separation of responsibilities aligns with the Single Responsibility Principle. The logic for fetching assignments, retrieving review mappings, and counting reviews is now more flexible and reusable throughout the application. For example, we used methods in the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; in both the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; and the &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;, helping to avoid code duplication.The article https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial served as a guide on when and how to use service objects in Rails.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
  return unless current_user_id?(@participant.user_id)&lt;br /&gt;
  @assignment = @participant.assignment&lt;br /&gt;
  @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
  # Finding how many reviews have been completed&lt;br /&gt;
  @num_reviews_completed = 0&lt;br /&gt;
  @review_mappings.each do |map|&lt;br /&gt;
    @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # render view for completing reviews after review bidding has been completed&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @assignment = participant_service.assignment&lt;br /&gt;
  unless @assignment.is_a?(Assignment)&lt;br /&gt;
    flash[:error] = 'Assignment not found.'&lt;br /&gt;
    redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  @review_mappings = review_service.review_mappings&lt;br /&gt;
  @num_reviews_completed = review_service.review_counts[:completed]&lt;br /&gt;
&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Optimize &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Action functionality ===&lt;br /&gt;
In the earlier version of the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, the server only needed a couple of key pieces of information: an array of topic IDs (params[:topic]) and the participant ID (params[:id}). From there, it would figure out the assignment_id by using the first topic as a reference. &lt;br /&gt;
&lt;br /&gt;
With the new implementation, the action now expects clear and structured parameters, including participant_id, an array of topic IDs, and assignment_id. This change ensures that the server always receives complete and reliable information, preventing issues caused by missing or inconsistent data. It also removes the need for the server to perform unnecessary lookups, allowing the controller to focus on its main job: validating inputs (like making sure the topics in params[:topic] belong to the same assignment as the participant), updating the database, handling errors properly, and sending the correct response back to the view.&lt;br /&gt;
&lt;br /&gt;
While these updates have cleaned up the set_priority action, the action still contains a lot of business logic, like validations and priority updates. To make the code even more maintainable, there’s room for further refactoring—perhaps moving some of this logic into a service class or model method to streamline the controller even more.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | Old Code&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    if params[:topic].nil?&lt;br /&gt;
      ReviewBid.where(participant_id: params[:id]).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      participant = AssignmentParticipant.find_by(id: params[:id])&lt;br /&gt;
      assignment_id = SignUpTopic.find(params[:topic].first).assignment.id&lt;br /&gt;
      @bids = ReviewBid.where(participant_id: params[:id])&lt;br /&gt;
      signed_up_topics = ReviewBid.where(participant_id: params[:id]).map(&amp;amp;:signuptopic_id)&lt;br /&gt;
      signed_up_topics -= params[:topic].map(&amp;amp;:to_i)&lt;br /&gt;
      signed_up_topics.each do |topic|&lt;br /&gt;
        ReviewBid.where(signuptopic_id: topic, participant_id: params[:id]).destroy_all&lt;br /&gt;
      end&lt;br /&gt;
      params[:topic].each_with_index do |topic_id, index|&lt;br /&gt;
        bid_existence = ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id])&lt;br /&gt;
        if bid_existence.empty?&lt;br /&gt;
          ReviewBid.create(priority: index + 1, signuptopic_id: topic_id, participant_id: params[:id], assignment_id: assignment_id)&lt;br /&gt;
        else&lt;br /&gt;
          ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id]).update_all(priority: index + 1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to action: 'show', assignment_id: params[:assignment_id], id: params[:id]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    participant_id = params[:participant_id].to_i&lt;br /&gt;
    selected_topic_ids = params[:topic]&amp;amp;.map(&amp;amp;:to_i) || []&lt;br /&gt;
    assignment_id = params[:assignment_id].to_i&lt;br /&gt;
&lt;br /&gt;
    unless current_user_can_modify_participant?(participant_id)&lt;br /&gt;
      flash[:error] = 'You are not authorized to modify these bids.'&lt;br /&gt;
      respond_to do |format|&lt;br /&gt;
        format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
        format.json { render json: { status: 'unauthorized' }, status: :unauthorized }&lt;br /&gt;
      end&lt;br /&gt;
      return&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    if selected_topic_ids.empty?&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      assignment = Assignment.find_by(id: assignment_id)&lt;br /&gt;
      if assignment.nil?&lt;br /&gt;
        flash[:error] = &amp;quot;Invalid assignment.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_assignment' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      unless SignUpTopic.where(id: selected_topic_ids, assignment_id: assignment_id).count == selected_topic_ids.size&lt;br /&gt;
        flash[:error] = &amp;quot;One or more selected topics are invalid.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_topics' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).where.not(signuptopic_id: selected_topic_ids).destroy_all&lt;br /&gt;
&lt;br /&gt;
      selected_topic_ids.each_with_index do |topic_id, index|&lt;br /&gt;
        bid = ReviewBid.find_or_initialize_by(signuptopic_id: topic_id, participant_id: participant_id)&lt;br /&gt;
        bid.priority = index + 1&lt;br /&gt;
        bid.assignment_id = assignment_id&lt;br /&gt;
        bid.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_to action: 'show', assignment_id: assignment_id, id: participant_id, notice: 'Review bids updated successfully.' }&lt;br /&gt;
      format.json { render json: { status: 'success' }, status: :ok }&lt;br /&gt;
    end&lt;br /&gt;
  rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
    flash[:error] = &amp;quot;Failed to update priorities: #{e.message}&amp;quot;&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
      format.json { render json: { status: 'error', message: e.message }, status: :unprocessable_entity }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Client Side Changes to Support &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Changes ====&lt;br /&gt;
After tidying up the server-side logic, we needed to refactor the client-side code to ensure it passed the correct information from the view to the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action. On the client side, we now grab the topicIDs and send them as &amp;lt;code&amp;gt;selected_topic_ids&amp;lt;/code&amp;gt;, allowing the controller to figure out which topics were selected and in what order.  &lt;br /&gt;
&lt;br /&gt;
We also extracted the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, making it a lot easier to send data with AJAX and keeping things flexible. When working with jQuery, AJAX is awesome because it lets us update the server without reloading the page, making the user experience smooth and responsive. When we pull the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, we can grab it along with other data, like the list of topics, and include it in the request. This way, we don’t have to rely on the URL to know which participant we’re working with, which makes the code cleaner and more adaptable.&lt;br /&gt;
&lt;br /&gt;
If we were to rely on the route (as opposed to using the DOM) for the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt;, every AJAX request would need to be tied to a specific URL structure. That’s limiting, especially if the route doesn’t naturally include the participant ID, as can be the case with collection routes versus member routes in Rails. &lt;br /&gt;
&lt;br /&gt;
Note: One interesting aspect of the application was the mix of JavaScript and CoffeeScript code. It wasn't clear why both types of code were used.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery -&amp;gt;&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: -&amp;gt;&lt;br /&gt;
      $.post($(this).data('update-url'), $(this).sortable('serialize'))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery(document).ready ($) -&amp;gt;&lt;br /&gt;
  sendUpdatedOrder = -&amp;gt;&lt;br /&gt;
    sortedIDs = $(this).sortable('toArray')&lt;br /&gt;
&lt;br /&gt;
    topicIDs = sortedIDs.map (id) -&amp;gt;&lt;br /&gt;
      id.replace 'topic_', ''&lt;br /&gt;
&lt;br /&gt;
    participant_id = $(this).data('participant-id')&lt;br /&gt;
&lt;br /&gt;
    assignment_id = new URL($(this).data('update-url'), window.location.origin).searchParams.get('assignment_id')&lt;br /&gt;
&lt;br /&gt;
    $.ajax&lt;br /&gt;
      type: 'POST'&lt;br /&gt;
      url: $(this).data('update-url')&lt;br /&gt;
      data:&lt;br /&gt;
        topic: topicIDs&lt;br /&gt;
        participant_id: participant_id&lt;br /&gt;
        assignment_id: assignment_id&lt;br /&gt;
      success: (response) -&amp;gt;&lt;br /&gt;
        console.log 'Selections updated successfully.'&lt;br /&gt;
      error: (xhr, status, error) -&amp;gt;&lt;br /&gt;
        alert 'An error occurred while saving your selections. Please try again.'&lt;br /&gt;
        console.error error&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: sendUpdatedOrder&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-1.-signupsheetcontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; Updates ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; method renders the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; action in &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt;. However, the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; does not include the controller action for this view. We need to find the old implementation of the controller action, add it back, and review and update it for any DRY violations.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # provides variables for reviewing page located at views/review_bids/others_work.html.erb&lt;br /&gt;
  def index&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
    return unless current_user_id?(@participant.user_id)&lt;br /&gt;
&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
    # Finding how many reviews have been completed&lt;br /&gt;
    @num_reviews_completed = 0&lt;br /&gt;
    @review_mappings.each do |map|&lt;br /&gt;
      @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # render view for completing reviews after review bidding has been completed&lt;br /&gt;
    render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
  end  &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-2.-reviewbidsotherswork-view-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;review_bids_others_work.html.erb&amp;lt;/code&amp;gt; View Updates ====&lt;br /&gt;
&lt;br /&gt;
The code only contains a view with this name and no associated controller action. Reviewing the code in the view, we noticed some SRP violations where there is much logic in the presentation layer. Set up helper methods in the controller to handle some of that logic or see if the model can handle some of that logic.&lt;br /&gt;
&lt;br /&gt;
Note: The view is technically part of the SignUpSheetController, but it's being rendered by the ReviewBidsController, which initially threw us off. After chatting with the professor, we decided not to change this view. He indicated that the plan is to reuse these views for both assignment bidding and review bidding.&lt;br /&gt;
&lt;br /&gt;
We would suggest building the assignment bidding and review bidding features separately first to make sure each one works on its own. Once that’s done, we can go back and clean things up by combining any shared functionality. This could include creating shared views or even introducing an abstract model to handle both types of bidding in a cleaner way. It could also mean replacing the ReviewBidsController with something more generic, like a BidsController, to keep things reusable and easier to maintain down the line.&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Rename &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; method ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; should be &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt;&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:''' Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The primary activity here is to refactor the method name &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; and fix the hardcoded URL. However, the match topic bidding algorithm no longer works since Heroku is no longer free, per the professor. The reference link below has detailed information about the web service that we can leverage to gain that understanding.&lt;br /&gt;
&lt;br /&gt;
Reference Link for Webservice Details: [https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Updated Implementation ====&lt;br /&gt;
The original requirement was to rename the run_bidding_algorithm method to assign_reviewers, but after digging into the code, we realized that renaming it didn’t make sense. The method runs the bidding algorithm to match topics, and there’s already a separate method to handle assigning review topics.&lt;br /&gt;
&lt;br /&gt;
However, we improved the logic in the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; method (which calls the private &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt;) by introducing a separate &amp;lt;code&amp;gt;BiddingAlgorithmService&amp;lt;/code&amp;gt; object. This keeps the business logic out of the controller action, which makes things cleaner and more maintainable.&lt;br /&gt;
&lt;br /&gt;
==== review_bids_controller ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def run_bidding_algorithm&lt;br /&gt;
    review_bid = ReviewBid.new&lt;br /&gt;
    bidding_data = review_bid.bidding_data&lt;br /&gt;
    matched_topics = BiddingAlgorithmService.new(bidding_data).run&lt;br /&gt;
    raise ArgumentError, 'Failed to assign reviewers. Please try again later.' unless matched_topics&lt;br /&gt;
&lt;br /&gt;
    matched_topics&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== bidding_algorithm_service ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# frozen_string_literal: true&lt;br /&gt;
&lt;br /&gt;
# The `BiddingAlgorithmService` run the bid assignment algorithm&lt;br /&gt;
# Sends student IDs, topic IDs, student preferences, and timestamps to the web service&lt;br /&gt;
# The web service returns the matched assignments in the JSON response body&lt;br /&gt;
class BiddingAlgorithmService&lt;br /&gt;
  SERVICE_URL = 'http://app-csc517.herokuapp.com/match_topics'.freeze&lt;br /&gt;
&lt;br /&gt;
  # MOCK_DATA is based on the documentation detailing how the webservice behaves.&lt;br /&gt;
  # Reference: https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review#Webservice&lt;br /&gt;
  MOCK_DATA = {&lt;br /&gt;
    36239 =&amp;gt; [3970, 3972, 3975],&lt;br /&gt;
    36240 =&amp;gt; [3973, 3974, 3972],&lt;br /&gt;
    36241 =&amp;gt; [3969, 3971, 3972],&lt;br /&gt;
    36242 =&amp;gt; [3969, 3971, 3973],&lt;br /&gt;
    36243 =&amp;gt; [3969, 3970, 3971]&lt;br /&gt;
  }.freeze&lt;br /&gt;
&lt;br /&gt;
  def initialize(bidding_data)&lt;br /&gt;
    @bidding_data = bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def run&lt;br /&gt;
    return MOCK_DATA if Rails.application.config.use_mock_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
    perform_request&lt;br /&gt;
  rescue RestClient::ExceptionWithResponse, JSON::ParserError&lt;br /&gt;
    false&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def perform_request&lt;br /&gt;
    response = RestClient.post(&lt;br /&gt;
      SERVICE_URL,&lt;br /&gt;
      @bidding_data.to_json,&lt;br /&gt;
      content_type: :json,&lt;br /&gt;
      accept: :json&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    validate_response(response)&lt;br /&gt;
    JSON.parse(response.body)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def validate_response(response)&lt;br /&gt;
    raise 'Invalid response format' unless response.headers[:content_type] &amp;amp;&amp;amp; response.headers[:content_type].include?('application/json')&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Resolve Functionality Issues for Non Bidders ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Currently it doesn't work if some student does not bid. In this case, algorithm needs to be fixed to ignore anyone who didn’t bid, and let them choose from what’s left over.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to do several things within this controller. We will set a default state for non-bidders that we can point to to give them options to select topics to review from the leftovers. We will consolidate &amp;lt;code&amp;gt;@sign_up_topics&amp;lt;/code&amp;gt; to resolve the DRY violation. Lastly, we will remove business logic from the show method and push it to a new helper to conform to the SRP.&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # Additional method&lt;br /&gt;
&lt;br /&gt;
  # Provides variables for review bidding page&lt;br /&gt;
  def show&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id].to_i)&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @sign_up_topics = SignUpTopic.where(assignment_id: @assignment.id, private_to: nil)&lt;br /&gt;
    my_topic = SignedUpTeam.topic_id(@participant.parent_id, @participant.user_id)&lt;br /&gt;
    @sign_up_topics -= SignUpTopic.where(assignment_id: @assignment.id, id: my_topic)&lt;br /&gt;
    @num_participants = AssignmentParticipant.where(parent_id: @assignment.id).count&lt;br /&gt;
    @selected_topics = nil # This is used to list the topics assigned to review (i.e., select == assigned)&lt;br /&gt;
    @bids = ReviewBid.where(participant_id: @participant, assignment_id: @assignment.id)&lt;br /&gt;
    signed_up_topics = []&lt;br /&gt;
    &lt;br /&gt;
    @bids.each do |bid|&lt;br /&gt;
      sign_up_topic = SignUpTopic.find_by(id: bid.signuptopic_id)&lt;br /&gt;
      signed_up_topics &amp;lt;&amp;lt; sign_up_topic if sign_up_topic&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    signed_up_topics &amp;amp;= @sign_up_topics&lt;br /&gt;
    @sign_up_topics -= signed_up_topics&lt;br /&gt;
    @bids = signed_up_topics&lt;br /&gt;
    @num_of_topics = @sign_up_topics.size&lt;br /&gt;
    @assigned_review_maps = []&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: @assignment.id, reviewer_id: @participant.id).each do |review_map|&lt;br /&gt;
      @assigned_review_maps &amp;lt;&amp;lt; review_map&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Explicitly render view since it's in the sign up sheet views&lt;br /&gt;
    render 'sign_up_sheet/review_bids_show'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Additional methods&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-2.-reviewbidshelper-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
 # Assigns bidding topics to reviewers&lt;br /&gt;
  def assign_bidding&lt;br /&gt;
      assignment = validate_assignment(params[:assignment_id])&lt;br /&gt;
&lt;br /&gt;
      reviewers = validate_reviewers(assignment.id)&lt;br /&gt;
      reviewer_ids = reviewers.map(&amp;amp;:id)&lt;br /&gt;
&lt;br /&gt;
      matched_topics = run_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
      if matched_topics.blank?&lt;br /&gt;
        flash[:alert] = 'Topic or assignment is missing'&lt;br /&gt;
        redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      leftover_topics = find_leftover_topics(assignment.id, matched_topics)&lt;br /&gt;
      assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.new.assign_review_topics(matched_topics)&lt;br /&gt;
      assignment.update!(can_choose_topic_to_review: false)&lt;br /&gt;
&lt;br /&gt;
      flash[:notice] = 'Reviewers were successfully assigned to topics.'&lt;br /&gt;
      redirect_back fallback_location: root_path&lt;br /&gt;
    rescue ArgumentError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ArgumentError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: e.message&lt;br /&gt;
    rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::RecordInvalid: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue ActiveRecord::ActiveRecordError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::ActiveRecordError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue StandardError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;StandardError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers. Please try again later.'&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  #service methods here ...&lt;br /&gt;
&lt;br /&gt;
  def find_leftover_topics(assignment_id, matched_topics)&lt;br /&gt;
    all_topic_ids = SignUpTopic.where(assignment_id: assignment_id).pluck(:id)&lt;br /&gt;
    assigned_topic_ids = matched_topics.map { |match| match[:topic_id] }&lt;br /&gt;
&lt;br /&gt;
    # Calculate leftover topics&lt;br /&gt;
    all_topic_ids - assigned_topic_ids&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
    return if leftover_topics.blank?&lt;br /&gt;
&lt;br /&gt;
    # Find non-bidders by excluding already matched reviewers&lt;br /&gt;
    non_bidders = reviewer_ids - matched_topics.keys&lt;br /&gt;
&lt;br /&gt;
    # Assign leftover topics to non-bidders in a round-robin fashion&lt;br /&gt;
    non_bidders.each_with_index do |reviewer_id, index|&lt;br /&gt;
      topic_id = leftover_topics[index % leftover_topics.length]&lt;br /&gt;
      ReviewBid.create(priority: 1, signuptopic_id: topic_id, participant_id: reviewer_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirm Functionality for Single-person Teams ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Make sure your code works on individual assignments, i.e., assignments where participants sign up for topics instead of teams. In this case, a team is created for each participant when they sign up. So the code should work for assignments to which either individuals or teams submit.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to make an update to focus on topic rather than team or have it where teams are updated based on topic id.We will probably utilize sign up topic or a similar object to accomplish this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-5-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bid Status Messaging ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| To make the functionality more intuitive, include a message to say how many students are eligible to submit bids, how many have submitted their bids, when the deadline for submitting bids is.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
The logic for these messages will go into the helper file. It is effectively the same reasoning as our migration of other business/functionality code from the controller to the helper to conform to the SRP. It will go hand in hand with the SignUpTopic portion of the code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-6-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Why are the methods in review_bid.rb class methods? Can we change them to instance methods or move it to helpers?&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-1.-reviewbid-model-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model Updates ====&lt;br /&gt;
Since the current class methods are tied to &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model instances, we will change them to instance methods. There is no need to move them to helpers.&lt;br /&gt;
&lt;br /&gt;
===== Review Bid Model Flow Before Refactoring =====&lt;br /&gt;
This sequence diagram describes how the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; interacts with the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model in assigning review topics to reviewers according to their bids. Once the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; action is initiated by the user, the controller gathers the IDs of reviewers and calls &amp;lt;code&amp;gt;ReviewBid.bidding_data&amp;lt;/code&amp;gt; to gather bidding information. That is sent out to an external bidding algorithm, and the matched topics that come back are used by &amp;lt;code&amp;gt;ReviewBid.assign_review_topics&amp;lt;/code&amp;gt; to create &amp;lt;code&amp;gt;ReviewResponseMap&amp;lt;/code&amp;gt; entries, assigning topics to reviewers for the assignment.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
  belongs_to :assignment, class_name: 'Assignment'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  def self.bidding_data(assignment_id, reviewer_ids)&lt;br /&gt;
    # create basic hash and set basic hash data&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'users' =&amp;gt; {}, 'max_accepted_proposals' =&amp;gt; [] }&lt;br /&gt;
    bidding_data['tid'] = SignUpTopic.where(assignment_id: assignment_id).ids&lt;br /&gt;
    bidding_data['max_accepted_proposals'] = Assignment.where(id: assignment_id).pluck(:num_reviews_allowed).first&lt;br /&gt;
&lt;br /&gt;
    # loop through reviewer_ids to get reviewer specific bidding data&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      bidding_data['users'][reviewer_id] = reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # assigns topics to reviews as matched by the webservice algorithm&lt;br /&gt;
  def self.assign_review_topics(assignment_id, reviewer_ids, matched_topics, _min_num_reviews = 2)&lt;br /&gt;
    # if review response map already created, delete it&lt;br /&gt;
    if ReviewResponseMap.where(reviewed_object_id: assignment_id)&lt;br /&gt;
      ReviewResponseMap.where(reviewed_object_id: assignment_id).destroy_all&lt;br /&gt;
    end&lt;br /&gt;
    # loop through reviewer_ids to assign reviews to each reviewer&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      topics_to_assign = matched_topics[reviewer_id.to_s]&lt;br /&gt;
      topics_to_assign.each do |topic|&lt;br /&gt;
        assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method to assign a single topic to a reviewer&lt;br /&gt;
  def self.assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic).pluck(:team_id).first&lt;br /&gt;
    team_to_review.nil? ? [] : ReviewResponseMap.create(reviewed_object_id: assignment_id, reviewer_id: reviewer_id, reviewee_id: team_to_review, type: 'ReviewResponseMap')&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method for getting individual reviewer_ids bidding data&lt;br /&gt;
  # returns user's bidding data hash&lt;br /&gt;
  def self.reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    reviewer_user_id = AssignmentParticipant.find(reviewer_id).user_id&lt;br /&gt;
    self_topic = SignedUpTeam.topic_id(assignment_id, reviewer_user_id)&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'otid' =&amp;gt; self_topic, 'priority' =&amp;gt; [], 'time' =&amp;gt; [] }&lt;br /&gt;
    bids = ReviewBid.where(participant_id: reviewer_id)&lt;br /&gt;
&lt;br /&gt;
    # loop through each bid for a topic to get specific data&lt;br /&gt;
    bids.each do |bid|&lt;br /&gt;
      bidding_data['tid'] &amp;lt;&amp;lt; bid.signuptopic_id&lt;br /&gt;
      bidding_data['priority'] &amp;lt;&amp;lt; bid.priority&lt;br /&gt;
      bidding_data['time'] &amp;lt;&amp;lt; bid.updated_at&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  # Instance method to get bidding data for this review bid&lt;br /&gt;
  def bidding_data&lt;br /&gt;
    {&lt;br /&gt;
      'tid' =&amp;gt; topic_ids,&lt;br /&gt;
      'otid' =&amp;gt; self_topic,&lt;br /&gt;
      'priority' =&amp;gt; priorities,&lt;br /&gt;
      'time' =&amp;gt; updated_times&lt;br /&gt;
    }&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # bid object to limit db touches for similar data acquisitions&lt;br /&gt;
  def bids&lt;br /&gt;
    @bids ||= ReviewBid.where(participant_id: participant_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def topic_ids&lt;br /&gt;
    bids.pluck(:signuptopic_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def priorities&lt;br /&gt;
    bids.pluck(:priority)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def updated_times&lt;br /&gt;
    bids.pluck(:updated_at)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self_topic&lt;br /&gt;
    return nil unless topic &amp;amp;&amp;amp; topic.assignment_id&lt;br /&gt;
&lt;br /&gt;
    SignedUpTeam.topic_id(topic.assignment_id, participant.user_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign topics to reviews&lt;br /&gt;
  def assign_review_topics(matched_topics)&lt;br /&gt;
    validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: topic.assignment_id).destroy_all&lt;br /&gt;
&lt;br /&gt;
    matched_topics.each do |reviewer_id, topics|&lt;br /&gt;
      Array(topics).each { |topic_id| assign_topic_to_reviewer(reviewer_id, topic_id) }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign a single topic to a reviewer&lt;br /&gt;
  def assign_topic_to_reviewer(reviewer_id, topic_id)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic_id).pluck(:team_id).first&lt;br /&gt;
    return if team_to_review.nil?&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.create(&lt;br /&gt;
      reviewed_object_id: topic.assignment_id,&lt;br /&gt;
      reviewer_id: reviewer_id,&lt;br /&gt;
      reviewee_id: team_to_review,&lt;br /&gt;
      type: 'ReviewResponseMap'&lt;br /&gt;
    )&lt;br /&gt;
    # error handling&lt;br /&gt;
  rescue StandardError =&amp;gt; e&lt;br /&gt;
    Rails.logger.error(&amp;quot;Failed to assign topic #{topic_id} to reviewer #{reviewer_id}: #{e.message}&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
    raise ArgumentError, 'Topic or assignment is missing' if topic.nil? || topic.assignment_id.nil?&lt;br /&gt;
    raise ArgumentError, 'Matched topics must be a Hash' unless matched_topics.is_a?(Hash)&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
Once we changed them to instance methods, we modified their use in the controller code to instantiate the object first.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-3.-schema.db-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;Schema.db&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We also need to remove the foreign key reference to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model since the &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model already has access to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;, and that is the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; we should be referencing. This will also involve modifying the database to remove the foreign key reference to the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Steps required:&lt;br /&gt;
&lt;br /&gt;
# Review the records in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table in the database to see if there are any records and if those records have values for &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;&lt;br /&gt;
# Create a migration file using the command: &amp;lt;code&amp;gt;rails generate migration RemoveAssignmentFromReviewBids&amp;lt;/code&amp;gt;&lt;br /&gt;
# In the generated file, indicate the change that needs to be made using the change method and migration columns &amp;lt;code&amp;gt;remove_foreign_key&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;remove_column&amp;lt;/code&amp;gt;. Migration should be straightforward since there is no cascading behavior (i.e., &amp;lt;code&amp;gt;dependent:destroy&amp;lt;/code&amp;gt;) to worry about.&lt;br /&gt;
# Once comfortable with the updates to the generated file, run the migration using the &amp;lt;code&amp;gt;rails db:migrate&amp;lt;/code&amp;gt; command.&lt;br /&gt;
# Run a query in the DB to ensure no orphaned records are in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table after the updates.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-4.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implement Edge Case Testing ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| In the previous implementation wiki, there are edge cases which are not exhaustively tested. Should test those edge cases thoroughly and add more edge case testing - link&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span class=&amp;quot;mark&amp;quot;&amp;gt;TODO&amp;lt;/span&amp;gt;:We just got initial test results back. We still need to figure out why tests are failing and what may be slipping through the cracks.[[File:Test expertiza.png|411x459px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-8-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design Patterns and Principles ===&lt;br /&gt;
Below are the design patterns and principles we plan to use to address the problem statements outlined in the project requirements:&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
** Controller Responsibility&lt;br /&gt;
*** To address the issue of authentication logic residing in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, we will refactor the code to ensure the controller solely handles request and response flows.&lt;br /&gt;
*** Authentication logic will be moved to dedicated methods in &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt;, making the codebase easier to maintain and test.&lt;br /&gt;
** View Simplification&lt;br /&gt;
*** Currently, the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; view contains excessive business logic, violating SRP.&lt;br /&gt;
*** We will move this logic into helper methods or models, ensuring each component has a single responsibility and improving code readability.&lt;br /&gt;
&lt;br /&gt;
* Separation of Concerns (SoC)&lt;br /&gt;
** By relocating authorization logic out of the controller, we separate authentication from request handling.&lt;br /&gt;
** Adhering to SoC enhances readability and maintainability by allowing each part of the application to focus on its specific role.&lt;br /&gt;
* Don't Repeat Yourself (DRY)&lt;br /&gt;
** We will inspect the existing implementation for code duplication, particularly in the controller actions.&lt;br /&gt;
** Any repetitive code will be refactored or removed to reduce redundancy and improve clarity.&lt;br /&gt;
&lt;br /&gt;
* Model-View-Controller (MVC) Adherence&lt;br /&gt;
** Moving business logic from the view aligns with MVC best practices.&lt;br /&gt;
** By ensuring the view only presents data without processing it, we maintain clear boundaries between the model, view, and controller layers.&lt;br /&gt;
&lt;br /&gt;
* Dependency Inversion Principle (DIP)&lt;br /&gt;
** We plan to update class methods to instance methods, allowing the controller to depend on abstractions rather than concrete implementations.&lt;br /&gt;
** This change leverages Ruby's duck typing, providing flexibility and making the code easier to maintain and test.&lt;br /&gt;
&lt;br /&gt;
** Clear and intentional naming during refactoring will enhance code clarity and maintainability.&lt;br /&gt;
&lt;br /&gt;
== Files Modified/Added ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-1.-controllers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
review_bids_controller.rb&lt;br /&gt;
&lt;br /&gt;
signup_sheet_controller.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-2.-models&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
review_bid.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-3.-helpers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
&lt;br /&gt;
authorization_helper.rb&lt;br /&gt;
&lt;br /&gt;
Review_bids_helper.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;viii.-test-plan&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
The Excel spreadsheet below details our test plan for testing the code we changed or created to ensure code coverage. The process involved reviewing the code and determining all the possible scenarios that needed to be covered based on what the method was doing, then creating test cases accordingly.&lt;br /&gt;
&lt;br /&gt;
* [https://docs.google.com/spreadsheets/d/1iHzOdSwlTtxOtf5VsNdwOaCP8NzYMmiJ/edit?usp=drive_link Open Test Matrix Spreadsheet]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/review_bids_controller_spec.rb ReviewBidsController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/sign_up_sheet_controller_spec.rb SignUpSheetController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/models/review_bid_spec.rb ReviewBid model updated test cases]&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-1.-mentor&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Ed Gehringer&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-2.-members&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Members ===&lt;br /&gt;
&lt;br /&gt;
* Gavin Teague&lt;br /&gt;
* Janice Uwujaren&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xi.-links&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
Previous Wiki: [https://wiki.expertiza.ncsu.edu/index.php/CSC/ECE_517_Fall_2021_-_E2151._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;Link&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xii.-references&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/document/d/1LHVacylQmB15I6VcYUA5nuC7GRRU9szHzZhjlPcE-PA/edit?tab=t.0#heading=h.u0oycoj6e0ev &amp;lt;u&amp;gt;Project Instructions&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;appendix-a-uml-class-diagram-plantuml-code-for-review-bids&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Juwujar</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160981</id>
		<title>CSC/ECE 517 Fall 2024 - E2485. Allow reviewers to bid on what to review</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160981"/>
		<updated>2024-12-10T23:21:25Z</updated>

		<summary type="html">&lt;p&gt;Juwujar: /* Design Patterns and Principles */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
This project will fix the defects and improve the functionality in Expertiza of the Reviewer Bidding feature, a process where reviewers can bid for assignments they want to review. It will make the functionality smoother, more reliable, and align with Expertiza's collaborative learning objectives.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iii.-problem-statements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Problem Statements ==&lt;br /&gt;
&lt;br /&gt;
Assigning reviews to users on Expertiza involves a complex bidding algorithm that requires optimization before production deployment. Below are key areas we plan to address as per the project requirements:&lt;br /&gt;
* Code Refactoring and Best Practices&lt;br /&gt;
** '''Utilize Authentication Utilities''': Modify the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; function in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to use existing authentication utilities, enhancing code reusability and adhering to the Single Responsibility Principle (SRP) and Separation of Concerns (SoC).&lt;br /&gt;
** '''Eliminate Code Duplication''': Refactor &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; to comply with the Don't Repeat Yourself (DRY) principle.&lt;br /&gt;
** '''Rename Functions for Clarity''': Change &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; to better reflect its purpose.&lt;br /&gt;
** '''Refactor Methods in '''&amp;lt;code&amp;gt;review_bid.rb&amp;lt;/code&amp;gt;''':''': Convert class methods to instance methods or relocate them to helper classes for improved code organization.&lt;br /&gt;
* Functionality Enhancements&lt;br /&gt;
** '''Handle Missing Bids Gracefully''': Adjust the bidding algorithm to accommodate students who do not bid, allowing them to select from the remaining options.&lt;br /&gt;
** '''Support Both Individual and Team Assignments''': Ensure the code functions correctly for participant and team-based assignments when signing up for topics.&lt;br /&gt;
* User Interface Improvements&lt;br /&gt;
** '''Increase Transparency in the Bidding Process''': Display messages indicating how many students are eligible to bid, how many have submitted bids, and the deadline.&lt;br /&gt;
** '''Accurate Bid Status Indicators''': Ensure topic colors change correctly based on the number of outstanding bids, providing clear visual feedback to users.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Exhaustive Edge Case Testing''': Conduct thorough testing for edge cases not previously covered, adding additional test cases as needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iv.-design-goals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Design Goals ==&lt;br /&gt;
&lt;br /&gt;
While addressing the existing defects and enhancing the Reviewer Bidding feature, we plan to adhere to the following design guidelines:&lt;br /&gt;
&lt;br /&gt;
* Code Quality and Maintainability&lt;br /&gt;
** '''Improve Code Structure: '''Implement refactoring strategies to enhance code readability and maintainability, ensuring adherence to best practices like the DRY principle.&lt;br /&gt;
** '''Standardize Authentication Logic: '''Utilize a centralized authentication utility to promote consistency and reusability across the application.&lt;br /&gt;
* System Stability and Flexibility&lt;br /&gt;
** '''Enhance Algorithm Robustness: '''Modify the bidding algorithm to handle scenarios where students do not bid, ensuring the system remains stable under all conditions.&lt;br /&gt;
** '''Ensure Assignment Versatility: '''Design the system to seamlessly support both individual and team assignments without additional configuration.&lt;br /&gt;
* User Experience Enhancements&lt;br /&gt;
** '''Increase Bidding Transparency: '''Develop user interface elements that clearly display bidding status, deadlines, and participation metrics.&lt;br /&gt;
** '''Implement Dynamic Visual Feedback: '''Use real-time visual cues, such as color changes, to inform users about the bidding status instantly.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Develop Comprehensive Test Suites: '''Create extensive automated tests covering edge cases and critical functionalities to guarantee reliable system behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;v.-uml-diagram&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== UML Diagram ==&lt;br /&gt;
&lt;br /&gt;
Below is a visual representation of the models associated with the review bids functionality, depicting the data structures and their relationships.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml class diagram2.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi.-implementation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
'''ReviewBid: '''The &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; manages the bidding process, where participants indicate their preferences for reviewing different topics or assignments. This helps distribute review tasks fairly based on participants' interests.&lt;br /&gt;
&lt;br /&gt;
'''Assignment: '''The &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model represents team or individual assignments in Expertiza. It stores all the essential information required to keep the assignment process organized, like managing participants, setting due dates, forming teams, and overseeing the review process.&lt;br /&gt;
&lt;br /&gt;
'''SignUpTopic: '''The &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt; model identifies specific topics or areas within an assignment for which participants can sign up. It manages the availability of these topics, tracks bids, coordinates team sign-ups, and assigns topics based on both bids and participant availability. &lt;br /&gt;
&lt;br /&gt;
'''Participant: '''The &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model is about the individual users who participate in an assignment. This includes students, instructors, teaching assistants, or other relevant roles. This model is responsible for managing key user information, monitoring interactions with the assignment, keeping track of team memberships, and recording things like badges and review grades.&lt;br /&gt;
&lt;br /&gt;
== Implementation ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1.-decoupling-authorization-logic-from-reviewbidscontroller&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Decoupling Authorization Logic from &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| '''&amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; needs to use the authentication utilities'''&lt;br /&gt;
&lt;br /&gt;
The function &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; should use the available authentication utilities to support the Single Responsibility Principle and Separation of Concerns and to support code reusability.&lt;br /&gt;
&lt;br /&gt;
| '''Authentication Consistency:''' Remove authentication logic in the controller and call logic in the authentication utility class instead to ensure consistent use of the same logic across the application, foster reusability, and eliminate DRY.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
==== Authorization Flow Before Refactoring ====&lt;br /&gt;
The sequence diagram depicts the authorization process within the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;. When a user invokes an action, the controller checks if the action is allowed by verifying the user's role through the &amp;lt;code&amp;gt;current_role_name()&amp;lt;/code&amp;gt; method in the &amp;lt;code&amp;gt;ApplicationController&amp;lt;/code&amp;gt;. Depending on the action and the user's role, the controller either authorizes the user to proceed or denies access, ultimately executing the action logic if authorized and responding to the user.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 1.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We simplified the &amp;lt;code&amp;gt;action_allowed?&amp;lt;/code&amp;gt; method by decoupling authorization logic from the controller to use helper methods in the &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt; module. The &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; should not handle authentication, which is a violation of SRP. &lt;br /&gt;
&lt;br /&gt;
Note: We also made similar updates to the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; methods for &amp;lt;code&amp;gt;SignUpController&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;vertical-align:top;&amp;quot; | Original Code !! style=&amp;quot;vertical-align:top;&amp;quot; | Refactored Code&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index'&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator',&lt;br /&gt;
     'Student'].include? current_role_name and&lt;br /&gt;
        ((%w[list].include? action_name) ? are_needed_authorizations_present?(params[:id], &amp;quot;participant&amp;quot;, &amp;quot;reader&amp;quot;, &amp;quot;submitter&amp;quot;, &amp;quot;reviewer&amp;quot;) : true)&lt;br /&gt;
  else&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator'].include? current_role_name&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index', 'list'&lt;br /&gt;
    current_user_has_student_privileges? &amp;amp;&amp;amp; list_authorization_check&lt;br /&gt;
  else&lt;br /&gt;
    current_user_has_ta_privileges?&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; action ===&lt;br /&gt;
Previously, the &amp;lt;code&amp;gt;ReviewBidsController's index&amp;lt;/code&amp;gt; action was overloaded with responsibilities—it handled everything from locating and validating assignments to fetching review mappings and counting completed reviews. This all-in-one approach made the controller action messy. &lt;br /&gt;
&lt;br /&gt;
We have moved the business logic from the controller action and into dedicated service objects. With this change, we introduced two key service objects: &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; focuses on retrieving the participant along with their assignments, while the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; takes on the task of managing review mappings and calculating review-related stats, like how many reviews have been completed. &lt;br /&gt;
&lt;br /&gt;
This separation of responsibilities aligns with the Single Responsibility Principle. The logic for fetching assignments, retrieving review mappings, and counting reviews is now more flexible and reusable throughout the application. For example, we used methods in the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; in both the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; and the &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;, helping to avoid code duplication.The article https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial served as a guide on when and how to use service objects in Rails.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
  return unless current_user_id?(@participant.user_id)&lt;br /&gt;
  @assignment = @participant.assignment&lt;br /&gt;
  @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
  # Finding how many reviews have been completed&lt;br /&gt;
  @num_reviews_completed = 0&lt;br /&gt;
  @review_mappings.each do |map|&lt;br /&gt;
    @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # render view for completing reviews after review bidding has been completed&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @assignment = participant_service.assignment&lt;br /&gt;
  unless @assignment.is_a?(Assignment)&lt;br /&gt;
    flash[:error] = 'Assignment not found.'&lt;br /&gt;
    redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  @review_mappings = review_service.review_mappings&lt;br /&gt;
  @num_reviews_completed = review_service.review_counts[:completed]&lt;br /&gt;
&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Optimize &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Action functionality ===&lt;br /&gt;
In the earlier version of the set_priority action in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, the server only needed a couple of key pieces of information: an array of topic IDs (params[:topic]) and the participant ID (params[:id}). From there, it would figure out the assignment_id by using the first topic as a reference. &lt;br /&gt;
&lt;br /&gt;
With the new implementation, the action now expects clear and structured parameters, including participant_id, an array of topic IDs, and assignment_id. This change ensures that the server always receives complete and reliable information, preventing issues caused by missing or inconsistent data. It also removes the need for the server to perform unnecessary lookups, allowing the controller to focus on its main job: validating inputs (like making sure the topics in params[:topic] belong to the same assignment as the participant), updating the database, handling errors properly, and sending the correct response back to the view.&lt;br /&gt;
&lt;br /&gt;
While these updates have cleaned up the set_priority action, the action still contains a lot of business logic, like validations and priority updates. To make the code even more maintainable, there’s room for further refactoring—perhaps moving some of this logic into a service class or model method to streamline the controller even more.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | Old Code&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    if params[:topic].nil?&lt;br /&gt;
      ReviewBid.where(participant_id: params[:id]).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      participant = AssignmentParticipant.find_by(id: params[:id])&lt;br /&gt;
      assignment_id = SignUpTopic.find(params[:topic].first).assignment.id&lt;br /&gt;
      @bids = ReviewBid.where(participant_id: params[:id])&lt;br /&gt;
      signed_up_topics = ReviewBid.where(participant_id: params[:id]).map(&amp;amp;:signuptopic_id)&lt;br /&gt;
      signed_up_topics -= params[:topic].map(&amp;amp;:to_i)&lt;br /&gt;
      signed_up_topics.each do |topic|&lt;br /&gt;
        ReviewBid.where(signuptopic_id: topic, participant_id: params[:id]).destroy_all&lt;br /&gt;
      end&lt;br /&gt;
      params[:topic].each_with_index do |topic_id, index|&lt;br /&gt;
        bid_existence = ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id])&lt;br /&gt;
        if bid_existence.empty?&lt;br /&gt;
          ReviewBid.create(priority: index + 1, signuptopic_id: topic_id, participant_id: params[:id], assignment_id: assignment_id)&lt;br /&gt;
        else&lt;br /&gt;
          ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id]).update_all(priority: index + 1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to action: 'show', assignment_id: params[:assignment_id], id: params[:id]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    participant_id = params[:participant_id].to_i&lt;br /&gt;
    selected_topic_ids = params[:topic]&amp;amp;.map(&amp;amp;:to_i) || []&lt;br /&gt;
    assignment_id = params[:assignment_id].to_i&lt;br /&gt;
&lt;br /&gt;
    unless current_user_can_modify_participant?(participant_id)&lt;br /&gt;
      flash[:error] = 'You are not authorized to modify these bids.'&lt;br /&gt;
      respond_to do |format|&lt;br /&gt;
        format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
        format.json { render json: { status: 'unauthorized' }, status: :unauthorized }&lt;br /&gt;
      end&lt;br /&gt;
      return&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    if selected_topic_ids.empty?&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      assignment = Assignment.find_by(id: assignment_id)&lt;br /&gt;
      if assignment.nil?&lt;br /&gt;
        flash[:error] = &amp;quot;Invalid assignment.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_assignment' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      unless SignUpTopic.where(id: selected_topic_ids, assignment_id: assignment_id).count == selected_topic_ids.size&lt;br /&gt;
        flash[:error] = &amp;quot;One or more selected topics are invalid.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_topics' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).where.not(signuptopic_id: selected_topic_ids).destroy_all&lt;br /&gt;
&lt;br /&gt;
      selected_topic_ids.each_with_index do |topic_id, index|&lt;br /&gt;
        bid = ReviewBid.find_or_initialize_by(signuptopic_id: topic_id, participant_id: participant_id)&lt;br /&gt;
        bid.priority = index + 1&lt;br /&gt;
        bid.assignment_id = assignment_id&lt;br /&gt;
        bid.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_to action: 'show', assignment_id: assignment_id, id: participant_id, notice: 'Review bids updated successfully.' }&lt;br /&gt;
      format.json { render json: { status: 'success' }, status: :ok }&lt;br /&gt;
    end&lt;br /&gt;
  rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
    flash[:error] = &amp;quot;Failed to update priorities: #{e.message}&amp;quot;&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
      format.json { render json: { status: 'error', message: e.message }, status: :unprocessable_entity }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Client Side Changes to Support &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Changes ====&lt;br /&gt;
After tidying up the server-side logic, we needed to refactor the client-side code to ensure it passed the correct information from the view to the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action. On the client side, we now grab the topicIDs and send them as &amp;lt;code&amp;gt;selected_topic_ids&amp;lt;/code&amp;gt;, allowing the controller to figure out which topics were selected and in what order.  &lt;br /&gt;
&lt;br /&gt;
We also extracted the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, making it a lot easier to send data with AJAX and keeping things flexible. When working with jQuery, AJAX is awesome because it lets us update the server without reloading the page, making the user experience smooth and responsive. When we pull the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, we can grab it along with other data, like the list of topics, and include it in the request. This way, we don’t have to rely on the URL to know which participant we’re working with, which makes the code cleaner and more adaptable.&lt;br /&gt;
&lt;br /&gt;
If we were to rely on the route (as opposed to using the DOM) for the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt;, every AJAX request would need to be tied to a specific URL structure. That’s limiting, especially if the route doesn’t naturally include the participant ID, as can be the case with collection routes versus member routes in Rails. &lt;br /&gt;
&lt;br /&gt;
Note: One interesting aspect of the application was the mix of JavaScript and CoffeeScript code. It wasn't clear why both types of code were used.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery -&amp;gt;&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: -&amp;gt;&lt;br /&gt;
      $.post($(this).data('update-url'), $(this).sortable('serialize'))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery(document).ready ($) -&amp;gt;&lt;br /&gt;
  sendUpdatedOrder = -&amp;gt;&lt;br /&gt;
    sortedIDs = $(this).sortable('toArray')&lt;br /&gt;
&lt;br /&gt;
    topicIDs = sortedIDs.map (id) -&amp;gt;&lt;br /&gt;
      id.replace 'topic_', ''&lt;br /&gt;
&lt;br /&gt;
    participant_id = $(this).data('participant-id')&lt;br /&gt;
&lt;br /&gt;
    assignment_id = new URL($(this).data('update-url'), window.location.origin).searchParams.get('assignment_id')&lt;br /&gt;
&lt;br /&gt;
    $.ajax&lt;br /&gt;
      type: 'POST'&lt;br /&gt;
      url: $(this).data('update-url')&lt;br /&gt;
      data:&lt;br /&gt;
        topic: topicIDs&lt;br /&gt;
        participant_id: participant_id&lt;br /&gt;
        assignment_id: assignment_id&lt;br /&gt;
      success: (response) -&amp;gt;&lt;br /&gt;
        console.log 'Selections updated successfully.'&lt;br /&gt;
      error: (xhr, status, error) -&amp;gt;&lt;br /&gt;
        alert 'An error occurred while saving your selections. Please try again.'&lt;br /&gt;
        console.error error&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: sendUpdatedOrder&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-1.-signupsheetcontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; Updates ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; method renders the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; action in &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt;. However, the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; does not include the controller action for this view. We need to find the old implementation of the controller action, add it back, and review and update it for any DRY violations.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # provides variables for reviewing page located at views/review_bids/others_work.html.erb&lt;br /&gt;
  def index&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
    return unless current_user_id?(@participant.user_id)&lt;br /&gt;
&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
    # Finding how many reviews have been completed&lt;br /&gt;
    @num_reviews_completed = 0&lt;br /&gt;
    @review_mappings.each do |map|&lt;br /&gt;
      @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # render view for completing reviews after review bidding has been completed&lt;br /&gt;
    render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
  end  &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-2.-reviewbidsotherswork-view-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;review_bids_others_work.html.erb&amp;lt;/code&amp;gt; View Updates ====&lt;br /&gt;
&lt;br /&gt;
The code only contains a view with this name and no associated controller action. Reviewing the code in the view, we noticed some SRP violations where there is much logic in the presentation layer. Set up helper methods in the controller to handle some of that logic or see if the model can handle some of that logic.&lt;br /&gt;
&lt;br /&gt;
Note: The view is technically part of the SignUpSheetController, but it's being rendered by the ReviewBidsController, which initially threw us off. After chatting with the professor, we decided not to change this view. He indicated that the plan is to reuse these views for both assignment bidding and review bidding.&lt;br /&gt;
&lt;br /&gt;
We would suggest building the assignment bidding and review bidding features separately first to make sure each one works on its own. Once that’s done, we can go back and clean things up by combining any shared functionality. This could include creating shared views or even introducing an abstract model to handle both types of bidding in a cleaner way. It could also mean replacing the ReviewBidsController with something more generic, like a BidsController, to keep things reusable and easier to maintain down the line.&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Rename &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; method ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; should be &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt;&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:''' Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The primary activity here is to refactor the method name &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; and fix the hardcoded URL. However, the match topic bidding algorithm no longer works since Heroku is no longer free, per the professor. The reference link below has detailed information about the web service that we can leverage to gain that understanding.&lt;br /&gt;
&lt;br /&gt;
Reference Link for Webservice Details: [https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Updated Implementation ====&lt;br /&gt;
The original requirement was to rename the run_bidding_algorithm method to assign_reviewers, but after digging into the code, we realized that renaming it didn’t make sense. The method runs the bidding algorithm to match topics, and there’s already a separate method to handle assigning review topics.&lt;br /&gt;
&lt;br /&gt;
However, we improved the logic in the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; method (which calls the private &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt;) by introducing a separate &amp;lt;code&amp;gt;BiddingAlgorithmService&amp;lt;/code&amp;gt; object. This keeps the business logic out of the controller action, which makes things cleaner and more maintainable.&lt;br /&gt;
&lt;br /&gt;
==== review_bids_controller ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def run_bidding_algorithm&lt;br /&gt;
    review_bid = ReviewBid.new&lt;br /&gt;
    bidding_data = review_bid.bidding_data&lt;br /&gt;
    matched_topics = BiddingAlgorithmService.new(bidding_data).run&lt;br /&gt;
    raise ArgumentError, 'Failed to assign reviewers. Please try again later.' unless matched_topics&lt;br /&gt;
&lt;br /&gt;
    matched_topics&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== bidding_algorithm_service ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# frozen_string_literal: true&lt;br /&gt;
&lt;br /&gt;
# The `BiddingAlgorithmService` run the bid assignment algorithm&lt;br /&gt;
# Sends student IDs, topic IDs, student preferences, and timestamps to the web service&lt;br /&gt;
# The web service returns the matched assignments in the JSON response body&lt;br /&gt;
class BiddingAlgorithmService&lt;br /&gt;
  SERVICE_URL = 'http://app-csc517.herokuapp.com/match_topics'.freeze&lt;br /&gt;
&lt;br /&gt;
  # MOCK_DATA is based on the documentation detailing how the webservice behaves.&lt;br /&gt;
  # Reference: https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review#Webservice&lt;br /&gt;
  MOCK_DATA = {&lt;br /&gt;
    36239 =&amp;gt; [3970, 3972, 3975],&lt;br /&gt;
    36240 =&amp;gt; [3973, 3974, 3972],&lt;br /&gt;
    36241 =&amp;gt; [3969, 3971, 3972],&lt;br /&gt;
    36242 =&amp;gt; [3969, 3971, 3973],&lt;br /&gt;
    36243 =&amp;gt; [3969, 3970, 3971]&lt;br /&gt;
  }.freeze&lt;br /&gt;
&lt;br /&gt;
  def initialize(bidding_data)&lt;br /&gt;
    @bidding_data = bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def run&lt;br /&gt;
    return MOCK_DATA if Rails.application.config.use_mock_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
    perform_request&lt;br /&gt;
  rescue RestClient::ExceptionWithResponse, JSON::ParserError&lt;br /&gt;
    false&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def perform_request&lt;br /&gt;
    response = RestClient.post(&lt;br /&gt;
      SERVICE_URL,&lt;br /&gt;
      @bidding_data.to_json,&lt;br /&gt;
      content_type: :json,&lt;br /&gt;
      accept: :json&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    validate_response(response)&lt;br /&gt;
    JSON.parse(response.body)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def validate_response(response)&lt;br /&gt;
    raise 'Invalid response format' unless response.headers[:content_type] &amp;amp;&amp;amp; response.headers[:content_type].include?('application/json')&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Resolve Functionality Issues for Non Bidders ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Currently it doesn't work if some student does not bid. In this case, algorithm needs to be fixed to ignore anyone who didn’t bid, and let them choose from what’s left over.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to do several things within this controller. We will set a default state for non-bidders that we can point to to give them options to select topics to review from the leftovers. We will consolidate &amp;lt;code&amp;gt;@sign_up_topics&amp;lt;/code&amp;gt; to resolve the DRY violation. Lastly, we will remove business logic from the show method and push it to a new helper to conform to the SRP.&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # Additional method&lt;br /&gt;
&lt;br /&gt;
  # Provides variables for review bidding page&lt;br /&gt;
  def show&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id].to_i)&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @sign_up_topics = SignUpTopic.where(assignment_id: @assignment.id, private_to: nil)&lt;br /&gt;
    my_topic = SignedUpTeam.topic_id(@participant.parent_id, @participant.user_id)&lt;br /&gt;
    @sign_up_topics -= SignUpTopic.where(assignment_id: @assignment.id, id: my_topic)&lt;br /&gt;
    @num_participants = AssignmentParticipant.where(parent_id: @assignment.id).count&lt;br /&gt;
    @selected_topics = nil # This is used to list the topics assigned to review (i.e., select == assigned)&lt;br /&gt;
    @bids = ReviewBid.where(participant_id: @participant, assignment_id: @assignment.id)&lt;br /&gt;
    signed_up_topics = []&lt;br /&gt;
    &lt;br /&gt;
    @bids.each do |bid|&lt;br /&gt;
      sign_up_topic = SignUpTopic.find_by(id: bid.signuptopic_id)&lt;br /&gt;
      signed_up_topics &amp;lt;&amp;lt; sign_up_topic if sign_up_topic&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    signed_up_topics &amp;amp;= @sign_up_topics&lt;br /&gt;
    @sign_up_topics -= signed_up_topics&lt;br /&gt;
    @bids = signed_up_topics&lt;br /&gt;
    @num_of_topics = @sign_up_topics.size&lt;br /&gt;
    @assigned_review_maps = []&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: @assignment.id, reviewer_id: @participant.id).each do |review_map|&lt;br /&gt;
      @assigned_review_maps &amp;lt;&amp;lt; review_map&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Explicitly render view since it's in the sign up sheet views&lt;br /&gt;
    render 'sign_up_sheet/review_bids_show'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Additional methods&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-2.-reviewbidshelper-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
 # Assigns bidding topics to reviewers&lt;br /&gt;
  def assign_bidding&lt;br /&gt;
      assignment = validate_assignment(params[:assignment_id])&lt;br /&gt;
&lt;br /&gt;
      reviewers = validate_reviewers(assignment.id)&lt;br /&gt;
      reviewer_ids = reviewers.map(&amp;amp;:id)&lt;br /&gt;
&lt;br /&gt;
      matched_topics = run_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
      if matched_topics.blank?&lt;br /&gt;
        flash[:alert] = 'Topic or assignment is missing'&lt;br /&gt;
        redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      leftover_topics = find_leftover_topics(assignment.id, matched_topics)&lt;br /&gt;
      assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.new.assign_review_topics(matched_topics)&lt;br /&gt;
      assignment.update!(can_choose_topic_to_review: false)&lt;br /&gt;
&lt;br /&gt;
      flash[:notice] = 'Reviewers were successfully assigned to topics.'&lt;br /&gt;
      redirect_back fallback_location: root_path&lt;br /&gt;
    rescue ArgumentError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ArgumentError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: e.message&lt;br /&gt;
    rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::RecordInvalid: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue ActiveRecord::ActiveRecordError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::ActiveRecordError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue StandardError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;StandardError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers. Please try again later.'&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  #service methods here ...&lt;br /&gt;
&lt;br /&gt;
  def find_leftover_topics(assignment_id, matched_topics)&lt;br /&gt;
    all_topic_ids = SignUpTopic.where(assignment_id: assignment_id).pluck(:id)&lt;br /&gt;
    assigned_topic_ids = matched_topics.map { |match| match[:topic_id] }&lt;br /&gt;
&lt;br /&gt;
    # Calculate leftover topics&lt;br /&gt;
    all_topic_ids - assigned_topic_ids&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
    return if leftover_topics.blank?&lt;br /&gt;
&lt;br /&gt;
    # Find non-bidders by excluding already matched reviewers&lt;br /&gt;
    non_bidders = reviewer_ids - matched_topics.keys&lt;br /&gt;
&lt;br /&gt;
    # Assign leftover topics to non-bidders in a round-robin fashion&lt;br /&gt;
    non_bidders.each_with_index do |reviewer_id, index|&lt;br /&gt;
      topic_id = leftover_topics[index % leftover_topics.length]&lt;br /&gt;
      ReviewBid.create(priority: 1, signuptopic_id: topic_id, participant_id: reviewer_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirm Functionality for Single-person Teams ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Make sure your code works on individual assignments, i.e., assignments where participants sign up for topics instead of teams. In this case, a team is created for each participant when they sign up. So the code should work for assignments to which either individuals or teams submit.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to make an update to focus on topic rather than team or have it where teams are updated based on topic id.We will probably utilize sign up topic or a similar object to accomplish this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-5-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bid Status Messaging ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| To make the functionality more intuitive, include a message to say how many students are eligible to submit bids, how many have submitted their bids, when the deadline for submitting bids is.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
The logic for these messages will go into the helper file. It is effectively the same reasoning as our migration of other business/functionality code from the controller to the helper to conform to the SRP. It will go hand in hand with the SignUpTopic portion of the code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-6-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Why are the methods in review_bid.rb class methods? Can we change them to instance methods or move it to helpers?&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-1.-reviewbid-model-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model Updates ====&lt;br /&gt;
Since the current class methods are tied to &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model instances, we will change them to instance methods. There is no need to move them to helpers.&lt;br /&gt;
&lt;br /&gt;
===== Review Bid Model Flow Before Refactoring =====&lt;br /&gt;
This sequence diagram describes how the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; interacts with the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model in assigning review topics to reviewers according to their bids. Once the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; action is initiated by the user, the controller gathers the IDs of reviewers and calls &amp;lt;code&amp;gt;ReviewBid.bidding_data&amp;lt;/code&amp;gt; to gather bidding information. That is sent out to an external bidding algorithm, and the matched topics that come back are used by &amp;lt;code&amp;gt;ReviewBid.assign_review_topics&amp;lt;/code&amp;gt; to create &amp;lt;code&amp;gt;ReviewResponseMap&amp;lt;/code&amp;gt; entries, assigning topics to reviewers for the assignment.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
  belongs_to :assignment, class_name: 'Assignment'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  def self.bidding_data(assignment_id, reviewer_ids)&lt;br /&gt;
    # create basic hash and set basic hash data&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'users' =&amp;gt; {}, 'max_accepted_proposals' =&amp;gt; [] }&lt;br /&gt;
    bidding_data['tid'] = SignUpTopic.where(assignment_id: assignment_id).ids&lt;br /&gt;
    bidding_data['max_accepted_proposals'] = Assignment.where(id: assignment_id).pluck(:num_reviews_allowed).first&lt;br /&gt;
&lt;br /&gt;
    # loop through reviewer_ids to get reviewer specific bidding data&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      bidding_data['users'][reviewer_id] = reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # assigns topics to reviews as matched by the webservice algorithm&lt;br /&gt;
  def self.assign_review_topics(assignment_id, reviewer_ids, matched_topics, _min_num_reviews = 2)&lt;br /&gt;
    # if review response map already created, delete it&lt;br /&gt;
    if ReviewResponseMap.where(reviewed_object_id: assignment_id)&lt;br /&gt;
      ReviewResponseMap.where(reviewed_object_id: assignment_id).destroy_all&lt;br /&gt;
    end&lt;br /&gt;
    # loop through reviewer_ids to assign reviews to each reviewer&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      topics_to_assign = matched_topics[reviewer_id.to_s]&lt;br /&gt;
      topics_to_assign.each do |topic|&lt;br /&gt;
        assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method to assign a single topic to a reviewer&lt;br /&gt;
  def self.assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic).pluck(:team_id).first&lt;br /&gt;
    team_to_review.nil? ? [] : ReviewResponseMap.create(reviewed_object_id: assignment_id, reviewer_id: reviewer_id, reviewee_id: team_to_review, type: 'ReviewResponseMap')&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method for getting individual reviewer_ids bidding data&lt;br /&gt;
  # returns user's bidding data hash&lt;br /&gt;
  def self.reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    reviewer_user_id = AssignmentParticipant.find(reviewer_id).user_id&lt;br /&gt;
    self_topic = SignedUpTeam.topic_id(assignment_id, reviewer_user_id)&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'otid' =&amp;gt; self_topic, 'priority' =&amp;gt; [], 'time' =&amp;gt; [] }&lt;br /&gt;
    bids = ReviewBid.where(participant_id: reviewer_id)&lt;br /&gt;
&lt;br /&gt;
    # loop through each bid for a topic to get specific data&lt;br /&gt;
    bids.each do |bid|&lt;br /&gt;
      bidding_data['tid'] &amp;lt;&amp;lt; bid.signuptopic_id&lt;br /&gt;
      bidding_data['priority'] &amp;lt;&amp;lt; bid.priority&lt;br /&gt;
      bidding_data['time'] &amp;lt;&amp;lt; bid.updated_at&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  # Instance method to get bidding data for this review bid&lt;br /&gt;
  def bidding_data&lt;br /&gt;
    {&lt;br /&gt;
      'tid' =&amp;gt; topic_ids,&lt;br /&gt;
      'otid' =&amp;gt; self_topic,&lt;br /&gt;
      'priority' =&amp;gt; priorities,&lt;br /&gt;
      'time' =&amp;gt; updated_times&lt;br /&gt;
    }&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # bid object to limit db touches for similar data acquisitions&lt;br /&gt;
  def bids&lt;br /&gt;
    @bids ||= ReviewBid.where(participant_id: participant_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def topic_ids&lt;br /&gt;
    bids.pluck(:signuptopic_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def priorities&lt;br /&gt;
    bids.pluck(:priority)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def updated_times&lt;br /&gt;
    bids.pluck(:updated_at)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self_topic&lt;br /&gt;
    return nil unless topic &amp;amp;&amp;amp; topic.assignment_id&lt;br /&gt;
&lt;br /&gt;
    SignedUpTeam.topic_id(topic.assignment_id, participant.user_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign topics to reviews&lt;br /&gt;
  def assign_review_topics(matched_topics)&lt;br /&gt;
    validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: topic.assignment_id).destroy_all&lt;br /&gt;
&lt;br /&gt;
    matched_topics.each do |reviewer_id, topics|&lt;br /&gt;
      Array(topics).each { |topic_id| assign_topic_to_reviewer(reviewer_id, topic_id) }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign a single topic to a reviewer&lt;br /&gt;
  def assign_topic_to_reviewer(reviewer_id, topic_id)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic_id).pluck(:team_id).first&lt;br /&gt;
    return if team_to_review.nil?&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.create(&lt;br /&gt;
      reviewed_object_id: topic.assignment_id,&lt;br /&gt;
      reviewer_id: reviewer_id,&lt;br /&gt;
      reviewee_id: team_to_review,&lt;br /&gt;
      type: 'ReviewResponseMap'&lt;br /&gt;
    )&lt;br /&gt;
    # error handling&lt;br /&gt;
  rescue StandardError =&amp;gt; e&lt;br /&gt;
    Rails.logger.error(&amp;quot;Failed to assign topic #{topic_id} to reviewer #{reviewer_id}: #{e.message}&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
    raise ArgumentError, 'Topic or assignment is missing' if topic.nil? || topic.assignment_id.nil?&lt;br /&gt;
    raise ArgumentError, 'Matched topics must be a Hash' unless matched_topics.is_a?(Hash)&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
Once we changed them to instance methods, we modified their use in the controller code to instantiate the object first.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-3.-schema.db-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;Schema.db&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We also need to remove the foreign key reference to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model since the &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model already has access to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;, and that is the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; we should be referencing. This will also involve modifying the database to remove the foreign key reference to the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Steps required:&lt;br /&gt;
&lt;br /&gt;
# Review the records in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table in the database to see if there are any records and if those records have values for &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;&lt;br /&gt;
# Create a migration file using the command: &amp;lt;code&amp;gt;rails generate migration RemoveAssignmentFromReviewBids&amp;lt;/code&amp;gt;&lt;br /&gt;
# In the generated file, indicate the change that needs to be made using the change method and migration columns &amp;lt;code&amp;gt;remove_foreign_key&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;remove_column&amp;lt;/code&amp;gt;. Migration should be straightforward since there is no cascading behavior (i.e., &amp;lt;code&amp;gt;dependent:destroy&amp;lt;/code&amp;gt;) to worry about.&lt;br /&gt;
# Once comfortable with the updates to the generated file, run the migration using the &amp;lt;code&amp;gt;rails db:migrate&amp;lt;/code&amp;gt; command.&lt;br /&gt;
# Run a query in the DB to ensure no orphaned records are in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table after the updates.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-4.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implement Edge Case Testing ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| In the previous implementation wiki, there are edge cases which are not exhaustively tested. Should test those edge cases thoroughly and add more edge case testing - link&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span class=&amp;quot;mark&amp;quot;&amp;gt;TODO&amp;lt;/span&amp;gt;:We just got initial test results back. We still need to figure out why tests are failing and what may be slipping through the cracks.[[File:Test expertiza.png|411x459px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-8-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design Patterns and Principles ===&lt;br /&gt;
Below are the design patterns and principles we plan to use to address the problem statements outlined in the project requirements:&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
** Controller Responsibility&lt;br /&gt;
*** To address the issue of authentication logic residing in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, we will refactor the code to ensure the controller solely handles request and response flows.&lt;br /&gt;
*** Authentication logic will be moved to dedicated methods in &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt;, making the codebase easier to maintain and test.&lt;br /&gt;
** View Simplification&lt;br /&gt;
*** Currently, the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; view contains excessive business logic, violating SRP.&lt;br /&gt;
*** We will move this logic into helper methods or models, ensuring each component has a single responsibility and improving code readability.&lt;br /&gt;
&lt;br /&gt;
* Separation of Concerns (SoC)&lt;br /&gt;
** By relocating authorization logic out of the controller, we separate authentication from request handling.&lt;br /&gt;
** Adhering to SoC enhances readability and maintainability by allowing each part of the application to focus on its specific role.&lt;br /&gt;
* Don't Repeat Yourself (DRY)&lt;br /&gt;
** We will inspect the existing implementation for code duplication, particularly in the controller actions.&lt;br /&gt;
** Any repetitive code will be refactored or removed to reduce redundancy and improve clarity.&lt;br /&gt;
&lt;br /&gt;
* Model-View-Controller (MVC) Adherence&lt;br /&gt;
** Moving business logic from the view aligns with MVC best practices.&lt;br /&gt;
** By ensuring the view only presents data without processing it, we maintain clear boundaries between the model, view, and controller layers.&lt;br /&gt;
&lt;br /&gt;
* Dependency Inversion Principle (DIP)&lt;br /&gt;
** We plan to update class methods to instance methods, allowing the controller to depend on abstractions rather than concrete implementations.&lt;br /&gt;
** This change leverages Ruby's duck typing, providing flexibility and making the code easier to maintain and test.&lt;br /&gt;
&lt;br /&gt;
** Clear and intentional naming during refactoring will enhance code clarity and maintainability.&lt;br /&gt;
&lt;br /&gt;
== Files Modified/Added ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-1.-controllers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
review_bids_controller.rb&lt;br /&gt;
&lt;br /&gt;
signup_sheet_controller.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-2.-models&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
review_bid.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-3.-helpers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
&lt;br /&gt;
authorization_helper.rb&lt;br /&gt;
&lt;br /&gt;
Review_bids_helper.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;viii.-test-plan&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
The Excel spreadsheet below details our test plan for testing the code we changed or created to ensure code coverage. The process involved reviewing the code and determining all the possible scenarios that needed to be covered based on what the method was doing, then creating test cases accordingly.&lt;br /&gt;
&lt;br /&gt;
* [https://docs.google.com/spreadsheets/d/1iHzOdSwlTtxOtf5VsNdwOaCP8NzYMmiJ/edit?usp=drive_link Open Test Matrix Spreadsheet]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/review_bids_controller_spec.rb ReviewBidsController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/sign_up_sheet_controller_spec.rb SignUpSheetController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/models/review_bid_spec.rb ReviewBid model updated test cases]&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-1.-mentor&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Ed Gehringer&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-2.-members&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Members ===&lt;br /&gt;
&lt;br /&gt;
* Gavin Teague&lt;br /&gt;
* Janice Uwujaren&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xi.-links&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
Previous Wiki: [https://wiki.expertiza.ncsu.edu/index.php/CSC/ECE_517_Fall_2021_-_E2151._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;Link&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xii.-references&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/document/d/1LHVacylQmB15I6VcYUA5nuC7GRRU9szHzZhjlPcE-PA/edit?tab=t.0#heading=h.u0oycoj6e0ev &amp;lt;u&amp;gt;Project Instructions&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;appendix-a-uml-class-diagram-plantuml-code-for-review-bids&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Juwujar</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160980</id>
		<title>CSC/ECE 517 Fall 2024 - E2485. Allow reviewers to bid on what to review</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160980"/>
		<updated>2024-12-10T23:19:41Z</updated>

		<summary type="html">&lt;p&gt;Juwujar: /* Schema.db Updates */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
This project will fix the defects and improve the functionality in Expertiza of the Reviewer Bidding feature, a process where reviewers can bid for assignments they want to review. It will make the functionality smoother, more reliable, and align with Expertiza's collaborative learning objectives.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iii.-problem-statements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Problem Statements ==&lt;br /&gt;
&lt;br /&gt;
Assigning reviews to users on Expertiza involves a complex bidding algorithm that requires optimization before production deployment. Below are key areas we plan to address as per the project requirements:&lt;br /&gt;
* Code Refactoring and Best Practices&lt;br /&gt;
** '''Utilize Authentication Utilities''': Modify the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; function in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to use existing authentication utilities, enhancing code reusability and adhering to the Single Responsibility Principle (SRP) and Separation of Concerns (SoC).&lt;br /&gt;
** '''Eliminate Code Duplication''': Refactor &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; to comply with the Don't Repeat Yourself (DRY) principle.&lt;br /&gt;
** '''Rename Functions for Clarity''': Change &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; to better reflect its purpose.&lt;br /&gt;
** '''Refactor Methods in '''&amp;lt;code&amp;gt;review_bid.rb&amp;lt;/code&amp;gt;''':''': Convert class methods to instance methods or relocate them to helper classes for improved code organization.&lt;br /&gt;
* Functionality Enhancements&lt;br /&gt;
** '''Handle Missing Bids Gracefully''': Adjust the bidding algorithm to accommodate students who do not bid, allowing them to select from the remaining options.&lt;br /&gt;
** '''Support Both Individual and Team Assignments''': Ensure the code functions correctly for participant and team-based assignments when signing up for topics.&lt;br /&gt;
* User Interface Improvements&lt;br /&gt;
** '''Increase Transparency in the Bidding Process''': Display messages indicating how many students are eligible to bid, how many have submitted bids, and the deadline.&lt;br /&gt;
** '''Accurate Bid Status Indicators''': Ensure topic colors change correctly based on the number of outstanding bids, providing clear visual feedback to users.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Exhaustive Edge Case Testing''': Conduct thorough testing for edge cases not previously covered, adding additional test cases as needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iv.-design-goals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Design Goals ==&lt;br /&gt;
&lt;br /&gt;
While addressing the existing defects and enhancing the Reviewer Bidding feature, we plan to adhere to the following design guidelines:&lt;br /&gt;
&lt;br /&gt;
* Code Quality and Maintainability&lt;br /&gt;
** '''Improve Code Structure: '''Implement refactoring strategies to enhance code readability and maintainability, ensuring adherence to best practices like the DRY principle.&lt;br /&gt;
** '''Standardize Authentication Logic: '''Utilize a centralized authentication utility to promote consistency and reusability across the application.&lt;br /&gt;
* System Stability and Flexibility&lt;br /&gt;
** '''Enhance Algorithm Robustness: '''Modify the bidding algorithm to handle scenarios where students do not bid, ensuring the system remains stable under all conditions.&lt;br /&gt;
** '''Ensure Assignment Versatility: '''Design the system to seamlessly support both individual and team assignments without additional configuration.&lt;br /&gt;
* User Experience Enhancements&lt;br /&gt;
** '''Increase Bidding Transparency: '''Develop user interface elements that clearly display bidding status, deadlines, and participation metrics.&lt;br /&gt;
** '''Implement Dynamic Visual Feedback: '''Use real-time visual cues, such as color changes, to inform users about the bidding status instantly.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Develop Comprehensive Test Suites: '''Create extensive automated tests covering edge cases and critical functionalities to guarantee reliable system behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;v.-uml-diagram&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== UML Diagram ==&lt;br /&gt;
&lt;br /&gt;
Below is a visual representation of the models associated with the review bids functionality, depicting the data structures and their relationships.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml class diagram2.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi.-implementation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
'''ReviewBid: '''The &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; manages the bidding process, where participants indicate their preferences for reviewing different topics or assignments. This helps distribute review tasks fairly based on participants' interests.&lt;br /&gt;
&lt;br /&gt;
'''Assignment: '''The &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model represents team or individual assignments in Expertiza. It stores all the essential information required to keep the assignment process organized, like managing participants, setting due dates, forming teams, and overseeing the review process.&lt;br /&gt;
&lt;br /&gt;
'''SignUpTopic: '''The &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt; model identifies specific topics or areas within an assignment for which participants can sign up. It manages the availability of these topics, tracks bids, coordinates team sign-ups, and assigns topics based on both bids and participant availability. &lt;br /&gt;
&lt;br /&gt;
'''Participant: '''The &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model is about the individual users who participate in an assignment. This includes students, instructors, teaching assistants, or other relevant roles. This model is responsible for managing key user information, monitoring interactions with the assignment, keeping track of team memberships, and recording things like badges and review grades.&lt;br /&gt;
&lt;br /&gt;
== Implementation ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1.-decoupling-authorization-logic-from-reviewbidscontroller&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Decoupling Authorization Logic from &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| '''&amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; needs to use the authentication utilities'''&lt;br /&gt;
&lt;br /&gt;
The function &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; should use the available authentication utilities to support the Single Responsibility Principle and Separation of Concerns and to support code reusability.&lt;br /&gt;
&lt;br /&gt;
| '''Authentication Consistency:''' Remove authentication logic in the controller and call logic in the authentication utility class instead to ensure consistent use of the same logic across the application, foster reusability, and eliminate DRY.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
==== Authorization Flow Before Refactoring ====&lt;br /&gt;
The sequence diagram depicts the authorization process within the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;. When a user invokes an action, the controller checks if the action is allowed by verifying the user's role through the &amp;lt;code&amp;gt;current_role_name()&amp;lt;/code&amp;gt; method in the &amp;lt;code&amp;gt;ApplicationController&amp;lt;/code&amp;gt;. Depending on the action and the user's role, the controller either authorizes the user to proceed or denies access, ultimately executing the action logic if authorized and responding to the user.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 1.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We simplified the &amp;lt;code&amp;gt;action_allowed?&amp;lt;/code&amp;gt; method by decoupling authorization logic from the controller to use helper methods in the &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt; module. The &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; should not handle authentication, which is a violation of SRP. &lt;br /&gt;
&lt;br /&gt;
Note: We also made similar updates to the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; methods for &amp;lt;code&amp;gt;SignUpController&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;vertical-align:top;&amp;quot; | Original Code !! style=&amp;quot;vertical-align:top;&amp;quot; | Refactored Code&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index'&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator',&lt;br /&gt;
     'Student'].include? current_role_name and&lt;br /&gt;
        ((%w[list].include? action_name) ? are_needed_authorizations_present?(params[:id], &amp;quot;participant&amp;quot;, &amp;quot;reader&amp;quot;, &amp;quot;submitter&amp;quot;, &amp;quot;reviewer&amp;quot;) : true)&lt;br /&gt;
  else&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator'].include? current_role_name&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index', 'list'&lt;br /&gt;
    current_user_has_student_privileges? &amp;amp;&amp;amp; list_authorization_check&lt;br /&gt;
  else&lt;br /&gt;
    current_user_has_ta_privileges?&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; action ===&lt;br /&gt;
Previously, the &amp;lt;code&amp;gt;ReviewBidsController's index&amp;lt;/code&amp;gt; action was overloaded with responsibilities—it handled everything from locating and validating assignments to fetching review mappings and counting completed reviews. This all-in-one approach made the controller action messy. &lt;br /&gt;
&lt;br /&gt;
We have moved the business logic from the controller action and into dedicated service objects. With this change, we introduced two key service objects: &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; focuses on retrieving the participant along with their assignments, while the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; takes on the task of managing review mappings and calculating review-related stats, like how many reviews have been completed. &lt;br /&gt;
&lt;br /&gt;
This separation of responsibilities aligns with the Single Responsibility Principle. The logic for fetching assignments, retrieving review mappings, and counting reviews is now more flexible and reusable throughout the application. For example, we used methods in the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; in both the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; and the &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;, helping to avoid code duplication.The article https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial served as a guide on when and how to use service objects in Rails.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
  return unless current_user_id?(@participant.user_id)&lt;br /&gt;
  @assignment = @participant.assignment&lt;br /&gt;
  @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
  # Finding how many reviews have been completed&lt;br /&gt;
  @num_reviews_completed = 0&lt;br /&gt;
  @review_mappings.each do |map|&lt;br /&gt;
    @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # render view for completing reviews after review bidding has been completed&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @assignment = participant_service.assignment&lt;br /&gt;
  unless @assignment.is_a?(Assignment)&lt;br /&gt;
    flash[:error] = 'Assignment not found.'&lt;br /&gt;
    redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  @review_mappings = review_service.review_mappings&lt;br /&gt;
  @num_reviews_completed = review_service.review_counts[:completed]&lt;br /&gt;
&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Optimize &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Action functionality ===&lt;br /&gt;
In the earlier version of the set_priority action in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, the server only needed a couple of key pieces of information: an array of topic IDs (params[:topic]) and the participant ID (params[:id}). From there, it would figure out the assignment_id by using the first topic as a reference. &lt;br /&gt;
&lt;br /&gt;
With the new implementation, the action now expects clear and structured parameters, including participant_id, an array of topic IDs, and assignment_id. This change ensures that the server always receives complete and reliable information, preventing issues caused by missing or inconsistent data. It also removes the need for the server to perform unnecessary lookups, allowing the controller to focus on its main job: validating inputs (like making sure the topics in params[:topic] belong to the same assignment as the participant), updating the database, handling errors properly, and sending the correct response back to the view.&lt;br /&gt;
&lt;br /&gt;
While these updates have cleaned up the set_priority action, the action still contains a lot of business logic, like validations and priority updates. To make the code even more maintainable, there’s room for further refactoring—perhaps moving some of this logic into a service class or model method to streamline the controller even more.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | Old Code&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    if params[:topic].nil?&lt;br /&gt;
      ReviewBid.where(participant_id: params[:id]).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      participant = AssignmentParticipant.find_by(id: params[:id])&lt;br /&gt;
      assignment_id = SignUpTopic.find(params[:topic].first).assignment.id&lt;br /&gt;
      @bids = ReviewBid.where(participant_id: params[:id])&lt;br /&gt;
      signed_up_topics = ReviewBid.where(participant_id: params[:id]).map(&amp;amp;:signuptopic_id)&lt;br /&gt;
      signed_up_topics -= params[:topic].map(&amp;amp;:to_i)&lt;br /&gt;
      signed_up_topics.each do |topic|&lt;br /&gt;
        ReviewBid.where(signuptopic_id: topic, participant_id: params[:id]).destroy_all&lt;br /&gt;
      end&lt;br /&gt;
      params[:topic].each_with_index do |topic_id, index|&lt;br /&gt;
        bid_existence = ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id])&lt;br /&gt;
        if bid_existence.empty?&lt;br /&gt;
          ReviewBid.create(priority: index + 1, signuptopic_id: topic_id, participant_id: params[:id], assignment_id: assignment_id)&lt;br /&gt;
        else&lt;br /&gt;
          ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id]).update_all(priority: index + 1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to action: 'show', assignment_id: params[:assignment_id], id: params[:id]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    participant_id = params[:participant_id].to_i&lt;br /&gt;
    selected_topic_ids = params[:topic]&amp;amp;.map(&amp;amp;:to_i) || []&lt;br /&gt;
    assignment_id = params[:assignment_id].to_i&lt;br /&gt;
&lt;br /&gt;
    unless current_user_can_modify_participant?(participant_id)&lt;br /&gt;
      flash[:error] = 'You are not authorized to modify these bids.'&lt;br /&gt;
      respond_to do |format|&lt;br /&gt;
        format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
        format.json { render json: { status: 'unauthorized' }, status: :unauthorized }&lt;br /&gt;
      end&lt;br /&gt;
      return&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    if selected_topic_ids.empty?&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      assignment = Assignment.find_by(id: assignment_id)&lt;br /&gt;
      if assignment.nil?&lt;br /&gt;
        flash[:error] = &amp;quot;Invalid assignment.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_assignment' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      unless SignUpTopic.where(id: selected_topic_ids, assignment_id: assignment_id).count == selected_topic_ids.size&lt;br /&gt;
        flash[:error] = &amp;quot;One or more selected topics are invalid.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_topics' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).where.not(signuptopic_id: selected_topic_ids).destroy_all&lt;br /&gt;
&lt;br /&gt;
      selected_topic_ids.each_with_index do |topic_id, index|&lt;br /&gt;
        bid = ReviewBid.find_or_initialize_by(signuptopic_id: topic_id, participant_id: participant_id)&lt;br /&gt;
        bid.priority = index + 1&lt;br /&gt;
        bid.assignment_id = assignment_id&lt;br /&gt;
        bid.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_to action: 'show', assignment_id: assignment_id, id: participant_id, notice: 'Review bids updated successfully.' }&lt;br /&gt;
      format.json { render json: { status: 'success' }, status: :ok }&lt;br /&gt;
    end&lt;br /&gt;
  rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
    flash[:error] = &amp;quot;Failed to update priorities: #{e.message}&amp;quot;&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
      format.json { render json: { status: 'error', message: e.message }, status: :unprocessable_entity }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Client Side Changes to Support &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Changes ====&lt;br /&gt;
After tidying up the server-side logic, we needed to refactor the client-side code to ensure it passed the correct information from the view to the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action. On the client side, we now grab the topicIDs and send them as &amp;lt;code&amp;gt;selected_topic_ids&amp;lt;/code&amp;gt;, allowing the controller to figure out which topics were selected and in what order.  &lt;br /&gt;
&lt;br /&gt;
We also extracted the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, making it a lot easier to send data with AJAX and keeping things flexible. When working with jQuery, AJAX is awesome because it lets us update the server without reloading the page, making the user experience smooth and responsive. When we pull the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, we can grab it along with other data, like the list of topics, and include it in the request. This way, we don’t have to rely on the URL to know which participant we’re working with, which makes the code cleaner and more adaptable.&lt;br /&gt;
&lt;br /&gt;
If we were to rely on the route (as opposed to using the DOM) for the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt;, every AJAX request would need to be tied to a specific URL structure. That’s limiting, especially if the route doesn’t naturally include the participant ID, as can be the case with collection routes versus member routes in Rails. &lt;br /&gt;
&lt;br /&gt;
Note: One interesting aspect of the application was the mix of JavaScript and CoffeeScript code. It wasn't clear why both types of code were used.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery -&amp;gt;&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: -&amp;gt;&lt;br /&gt;
      $.post($(this).data('update-url'), $(this).sortable('serialize'))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery(document).ready ($) -&amp;gt;&lt;br /&gt;
  sendUpdatedOrder = -&amp;gt;&lt;br /&gt;
    sortedIDs = $(this).sortable('toArray')&lt;br /&gt;
&lt;br /&gt;
    topicIDs = sortedIDs.map (id) -&amp;gt;&lt;br /&gt;
      id.replace 'topic_', ''&lt;br /&gt;
&lt;br /&gt;
    participant_id = $(this).data('participant-id')&lt;br /&gt;
&lt;br /&gt;
    assignment_id = new URL($(this).data('update-url'), window.location.origin).searchParams.get('assignment_id')&lt;br /&gt;
&lt;br /&gt;
    $.ajax&lt;br /&gt;
      type: 'POST'&lt;br /&gt;
      url: $(this).data('update-url')&lt;br /&gt;
      data:&lt;br /&gt;
        topic: topicIDs&lt;br /&gt;
        participant_id: participant_id&lt;br /&gt;
        assignment_id: assignment_id&lt;br /&gt;
      success: (response) -&amp;gt;&lt;br /&gt;
        console.log 'Selections updated successfully.'&lt;br /&gt;
      error: (xhr, status, error) -&amp;gt;&lt;br /&gt;
        alert 'An error occurred while saving your selections. Please try again.'&lt;br /&gt;
        console.error error&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: sendUpdatedOrder&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-1.-signupsheetcontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; Updates ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; method renders the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; action in &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt;. However, the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; does not include the controller action for this view. We need to find the old implementation of the controller action, add it back, and review and update it for any DRY violations.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # provides variables for reviewing page located at views/review_bids/others_work.html.erb&lt;br /&gt;
  def index&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
    return unless current_user_id?(@participant.user_id)&lt;br /&gt;
&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
    # Finding how many reviews have been completed&lt;br /&gt;
    @num_reviews_completed = 0&lt;br /&gt;
    @review_mappings.each do |map|&lt;br /&gt;
      @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # render view for completing reviews after review bidding has been completed&lt;br /&gt;
    render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
  end  &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-2.-reviewbidsotherswork-view-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;review_bids_others_work.html.erb&amp;lt;/code&amp;gt; View Updates ====&lt;br /&gt;
&lt;br /&gt;
The code only contains a view with this name and no associated controller action. Reviewing the code in the view, we noticed some SRP violations where there is much logic in the presentation layer. Set up helper methods in the controller to handle some of that logic or see if the model can handle some of that logic.&lt;br /&gt;
&lt;br /&gt;
Note: The view is technically part of the SignUpSheetController, but it's being rendered by the ReviewBidsController, which initially threw us off. After chatting with the professor, we decided not to change this view. He indicated that the plan is to reuse these views for both assignment bidding and review bidding.&lt;br /&gt;
&lt;br /&gt;
We would suggest building the assignment bidding and review bidding features separately first to make sure each one works on its own. Once that’s done, we can go back and clean things up by combining any shared functionality. This could include creating shared views or even introducing an abstract model to handle both types of bidding in a cleaner way. It could also mean replacing the ReviewBidsController with something more generic, like a BidsController, to keep things reusable and easier to maintain down the line.&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Rename &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; method ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; should be &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt;&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:''' Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The primary activity here is to refactor the method name &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; and fix the hardcoded URL. However, the match topic bidding algorithm no longer works since Heroku is no longer free, per the professor. The reference link below has detailed information about the web service that we can leverage to gain that understanding.&lt;br /&gt;
&lt;br /&gt;
Reference Link for Webservice Details: [https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Updated Implementation ====&lt;br /&gt;
The original requirement was to rename the run_bidding_algorithm method to assign_reviewers, but after digging into the code, we realized that renaming it didn’t make sense. The method runs the bidding algorithm to match topics, and there’s already a separate method to handle assigning review topics.&lt;br /&gt;
&lt;br /&gt;
However, we improved the logic in the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; method (which calls the private &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt;) by introducing a separate &amp;lt;code&amp;gt;BiddingAlgorithmService&amp;lt;/code&amp;gt; object. This keeps the business logic out of the controller action, which makes things cleaner and more maintainable.&lt;br /&gt;
&lt;br /&gt;
==== review_bids_controller ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def run_bidding_algorithm&lt;br /&gt;
    review_bid = ReviewBid.new&lt;br /&gt;
    bidding_data = review_bid.bidding_data&lt;br /&gt;
    matched_topics = BiddingAlgorithmService.new(bidding_data).run&lt;br /&gt;
    raise ArgumentError, 'Failed to assign reviewers. Please try again later.' unless matched_topics&lt;br /&gt;
&lt;br /&gt;
    matched_topics&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== bidding_algorithm_service ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# frozen_string_literal: true&lt;br /&gt;
&lt;br /&gt;
# The `BiddingAlgorithmService` run the bid assignment algorithm&lt;br /&gt;
# Sends student IDs, topic IDs, student preferences, and timestamps to the web service&lt;br /&gt;
# The web service returns the matched assignments in the JSON response body&lt;br /&gt;
class BiddingAlgorithmService&lt;br /&gt;
  SERVICE_URL = 'http://app-csc517.herokuapp.com/match_topics'.freeze&lt;br /&gt;
&lt;br /&gt;
  # MOCK_DATA is based on the documentation detailing how the webservice behaves.&lt;br /&gt;
  # Reference: https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review#Webservice&lt;br /&gt;
  MOCK_DATA = {&lt;br /&gt;
    36239 =&amp;gt; [3970, 3972, 3975],&lt;br /&gt;
    36240 =&amp;gt; [3973, 3974, 3972],&lt;br /&gt;
    36241 =&amp;gt; [3969, 3971, 3972],&lt;br /&gt;
    36242 =&amp;gt; [3969, 3971, 3973],&lt;br /&gt;
    36243 =&amp;gt; [3969, 3970, 3971]&lt;br /&gt;
  }.freeze&lt;br /&gt;
&lt;br /&gt;
  def initialize(bidding_data)&lt;br /&gt;
    @bidding_data = bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def run&lt;br /&gt;
    return MOCK_DATA if Rails.application.config.use_mock_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
    perform_request&lt;br /&gt;
  rescue RestClient::ExceptionWithResponse, JSON::ParserError&lt;br /&gt;
    false&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def perform_request&lt;br /&gt;
    response = RestClient.post(&lt;br /&gt;
      SERVICE_URL,&lt;br /&gt;
      @bidding_data.to_json,&lt;br /&gt;
      content_type: :json,&lt;br /&gt;
      accept: :json&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    validate_response(response)&lt;br /&gt;
    JSON.parse(response.body)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def validate_response(response)&lt;br /&gt;
    raise 'Invalid response format' unless response.headers[:content_type] &amp;amp;&amp;amp; response.headers[:content_type].include?('application/json')&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Resolve Functionality Issues for Non Bidders ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Currently it doesn't work if some student does not bid. In this case, algorithm needs to be fixed to ignore anyone who didn’t bid, and let them choose from what’s left over.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to do several things within this controller. We will set a default state for non-bidders that we can point to to give them options to select topics to review from the leftovers. We will consolidate &amp;lt;code&amp;gt;@sign_up_topics&amp;lt;/code&amp;gt; to resolve the DRY violation. Lastly, we will remove business logic from the show method and push it to a new helper to conform to the SRP.&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # Additional method&lt;br /&gt;
&lt;br /&gt;
  # Provides variables for review bidding page&lt;br /&gt;
  def show&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id].to_i)&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @sign_up_topics = SignUpTopic.where(assignment_id: @assignment.id, private_to: nil)&lt;br /&gt;
    my_topic = SignedUpTeam.topic_id(@participant.parent_id, @participant.user_id)&lt;br /&gt;
    @sign_up_topics -= SignUpTopic.where(assignment_id: @assignment.id, id: my_topic)&lt;br /&gt;
    @num_participants = AssignmentParticipant.where(parent_id: @assignment.id).count&lt;br /&gt;
    @selected_topics = nil # This is used to list the topics assigned to review (i.e., select == assigned)&lt;br /&gt;
    @bids = ReviewBid.where(participant_id: @participant, assignment_id: @assignment.id)&lt;br /&gt;
    signed_up_topics = []&lt;br /&gt;
    &lt;br /&gt;
    @bids.each do |bid|&lt;br /&gt;
      sign_up_topic = SignUpTopic.find_by(id: bid.signuptopic_id)&lt;br /&gt;
      signed_up_topics &amp;lt;&amp;lt; sign_up_topic if sign_up_topic&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    signed_up_topics &amp;amp;= @sign_up_topics&lt;br /&gt;
    @sign_up_topics -= signed_up_topics&lt;br /&gt;
    @bids = signed_up_topics&lt;br /&gt;
    @num_of_topics = @sign_up_topics.size&lt;br /&gt;
    @assigned_review_maps = []&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: @assignment.id, reviewer_id: @participant.id).each do |review_map|&lt;br /&gt;
      @assigned_review_maps &amp;lt;&amp;lt; review_map&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Explicitly render view since it's in the sign up sheet views&lt;br /&gt;
    render 'sign_up_sheet/review_bids_show'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Additional methods&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-2.-reviewbidshelper-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
 # Assigns bidding topics to reviewers&lt;br /&gt;
  def assign_bidding&lt;br /&gt;
      assignment = validate_assignment(params[:assignment_id])&lt;br /&gt;
&lt;br /&gt;
      reviewers = validate_reviewers(assignment.id)&lt;br /&gt;
      reviewer_ids = reviewers.map(&amp;amp;:id)&lt;br /&gt;
&lt;br /&gt;
      matched_topics = run_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
      if matched_topics.blank?&lt;br /&gt;
        flash[:alert] = 'Topic or assignment is missing'&lt;br /&gt;
        redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      leftover_topics = find_leftover_topics(assignment.id, matched_topics)&lt;br /&gt;
      assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.new.assign_review_topics(matched_topics)&lt;br /&gt;
      assignment.update!(can_choose_topic_to_review: false)&lt;br /&gt;
&lt;br /&gt;
      flash[:notice] = 'Reviewers were successfully assigned to topics.'&lt;br /&gt;
      redirect_back fallback_location: root_path&lt;br /&gt;
    rescue ArgumentError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ArgumentError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: e.message&lt;br /&gt;
    rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::RecordInvalid: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue ActiveRecord::ActiveRecordError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::ActiveRecordError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue StandardError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;StandardError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers. Please try again later.'&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  #service methods here ...&lt;br /&gt;
&lt;br /&gt;
  def find_leftover_topics(assignment_id, matched_topics)&lt;br /&gt;
    all_topic_ids = SignUpTopic.where(assignment_id: assignment_id).pluck(:id)&lt;br /&gt;
    assigned_topic_ids = matched_topics.map { |match| match[:topic_id] }&lt;br /&gt;
&lt;br /&gt;
    # Calculate leftover topics&lt;br /&gt;
    all_topic_ids - assigned_topic_ids&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
    return if leftover_topics.blank?&lt;br /&gt;
&lt;br /&gt;
    # Find non-bidders by excluding already matched reviewers&lt;br /&gt;
    non_bidders = reviewer_ids - matched_topics.keys&lt;br /&gt;
&lt;br /&gt;
    # Assign leftover topics to non-bidders in a round-robin fashion&lt;br /&gt;
    non_bidders.each_with_index do |reviewer_id, index|&lt;br /&gt;
      topic_id = leftover_topics[index % leftover_topics.length]&lt;br /&gt;
      ReviewBid.create(priority: 1, signuptopic_id: topic_id, participant_id: reviewer_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirm Functionality for Single-person Teams ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Make sure your code works on individual assignments, i.e., assignments where participants sign up for topics instead of teams. In this case, a team is created for each participant when they sign up. So the code should work for assignments to which either individuals or teams submit.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to make an update to focus on topic rather than team or have it where teams are updated based on topic id.We will probably utilize sign up topic or a similar object to accomplish this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-5-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bid Status Messaging ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| To make the functionality more intuitive, include a message to say how many students are eligible to submit bids, how many have submitted their bids, when the deadline for submitting bids is.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
The logic for these messages will go into the helper file. It is effectively the same reasoning as our migration of other business/functionality code from the controller to the helper to conform to the SRP. It will go hand in hand with the SignUpTopic portion of the code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-6-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Why are the methods in review_bid.rb class methods? Can we change them to instance methods or move it to helpers?&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-1.-reviewbid-model-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model Updates ====&lt;br /&gt;
Since the current class methods are tied to &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model instances, we will change them to instance methods. There is no need to move them to helpers.&lt;br /&gt;
&lt;br /&gt;
===== Review Bid Model Flow Before Refactoring =====&lt;br /&gt;
This sequence diagram describes how the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; interacts with the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model in assigning review topics to reviewers according to their bids. Once the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; action is initiated by the user, the controller gathers the IDs of reviewers and calls &amp;lt;code&amp;gt;ReviewBid.bidding_data&amp;lt;/code&amp;gt; to gather bidding information. That is sent out to an external bidding algorithm, and the matched topics that come back are used by &amp;lt;code&amp;gt;ReviewBid.assign_review_topics&amp;lt;/code&amp;gt; to create &amp;lt;code&amp;gt;ReviewResponseMap&amp;lt;/code&amp;gt; entries, assigning topics to reviewers for the assignment.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
  belongs_to :assignment, class_name: 'Assignment'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  def self.bidding_data(assignment_id, reviewer_ids)&lt;br /&gt;
    # create basic hash and set basic hash data&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'users' =&amp;gt; {}, 'max_accepted_proposals' =&amp;gt; [] }&lt;br /&gt;
    bidding_data['tid'] = SignUpTopic.where(assignment_id: assignment_id).ids&lt;br /&gt;
    bidding_data['max_accepted_proposals'] = Assignment.where(id: assignment_id).pluck(:num_reviews_allowed).first&lt;br /&gt;
&lt;br /&gt;
    # loop through reviewer_ids to get reviewer specific bidding data&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      bidding_data['users'][reviewer_id] = reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # assigns topics to reviews as matched by the webservice algorithm&lt;br /&gt;
  def self.assign_review_topics(assignment_id, reviewer_ids, matched_topics, _min_num_reviews = 2)&lt;br /&gt;
    # if review response map already created, delete it&lt;br /&gt;
    if ReviewResponseMap.where(reviewed_object_id: assignment_id)&lt;br /&gt;
      ReviewResponseMap.where(reviewed_object_id: assignment_id).destroy_all&lt;br /&gt;
    end&lt;br /&gt;
    # loop through reviewer_ids to assign reviews to each reviewer&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      topics_to_assign = matched_topics[reviewer_id.to_s]&lt;br /&gt;
      topics_to_assign.each do |topic|&lt;br /&gt;
        assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method to assign a single topic to a reviewer&lt;br /&gt;
  def self.assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic).pluck(:team_id).first&lt;br /&gt;
    team_to_review.nil? ? [] : ReviewResponseMap.create(reviewed_object_id: assignment_id, reviewer_id: reviewer_id, reviewee_id: team_to_review, type: 'ReviewResponseMap')&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method for getting individual reviewer_ids bidding data&lt;br /&gt;
  # returns user's bidding data hash&lt;br /&gt;
  def self.reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    reviewer_user_id = AssignmentParticipant.find(reviewer_id).user_id&lt;br /&gt;
    self_topic = SignedUpTeam.topic_id(assignment_id, reviewer_user_id)&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'otid' =&amp;gt; self_topic, 'priority' =&amp;gt; [], 'time' =&amp;gt; [] }&lt;br /&gt;
    bids = ReviewBid.where(participant_id: reviewer_id)&lt;br /&gt;
&lt;br /&gt;
    # loop through each bid for a topic to get specific data&lt;br /&gt;
    bids.each do |bid|&lt;br /&gt;
      bidding_data['tid'] &amp;lt;&amp;lt; bid.signuptopic_id&lt;br /&gt;
      bidding_data['priority'] &amp;lt;&amp;lt; bid.priority&lt;br /&gt;
      bidding_data['time'] &amp;lt;&amp;lt; bid.updated_at&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  # Instance method to get bidding data for this review bid&lt;br /&gt;
  def bidding_data&lt;br /&gt;
    {&lt;br /&gt;
      'tid' =&amp;gt; topic_ids,&lt;br /&gt;
      'otid' =&amp;gt; self_topic,&lt;br /&gt;
      'priority' =&amp;gt; priorities,&lt;br /&gt;
      'time' =&amp;gt; updated_times&lt;br /&gt;
    }&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # bid object to limit db touches for similar data acquisitions&lt;br /&gt;
  def bids&lt;br /&gt;
    @bids ||= ReviewBid.where(participant_id: participant_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def topic_ids&lt;br /&gt;
    bids.pluck(:signuptopic_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def priorities&lt;br /&gt;
    bids.pluck(:priority)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def updated_times&lt;br /&gt;
    bids.pluck(:updated_at)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self_topic&lt;br /&gt;
    return nil unless topic &amp;amp;&amp;amp; topic.assignment_id&lt;br /&gt;
&lt;br /&gt;
    SignedUpTeam.topic_id(topic.assignment_id, participant.user_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign topics to reviews&lt;br /&gt;
  def assign_review_topics(matched_topics)&lt;br /&gt;
    validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: topic.assignment_id).destroy_all&lt;br /&gt;
&lt;br /&gt;
    matched_topics.each do |reviewer_id, topics|&lt;br /&gt;
      Array(topics).each { |topic_id| assign_topic_to_reviewer(reviewer_id, topic_id) }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign a single topic to a reviewer&lt;br /&gt;
  def assign_topic_to_reviewer(reviewer_id, topic_id)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic_id).pluck(:team_id).first&lt;br /&gt;
    return if team_to_review.nil?&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.create(&lt;br /&gt;
      reviewed_object_id: topic.assignment_id,&lt;br /&gt;
      reviewer_id: reviewer_id,&lt;br /&gt;
      reviewee_id: team_to_review,&lt;br /&gt;
      type: 'ReviewResponseMap'&lt;br /&gt;
    )&lt;br /&gt;
    # error handling&lt;br /&gt;
  rescue StandardError =&amp;gt; e&lt;br /&gt;
    Rails.logger.error(&amp;quot;Failed to assign topic #{topic_id} to reviewer #{reviewer_id}: #{e.message}&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
    raise ArgumentError, 'Topic or assignment is missing' if topic.nil? || topic.assignment_id.nil?&lt;br /&gt;
    raise ArgumentError, 'Matched topics must be a Hash' unless matched_topics.is_a?(Hash)&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
Once we changed them to instance methods, we modified their use in the controller code to instantiate the object first.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-3.-schema.db-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;Schema.db&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We also need to remove the foreign key reference to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model since the &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model already has access to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;, and that is the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; we should be referencing. This will also involve modifying the database to remove the foreign key reference to the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Steps required:&lt;br /&gt;
&lt;br /&gt;
# Review the records in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table in the database to see if there are any records and if those records have values for &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;&lt;br /&gt;
# Create a migration file using the command: &amp;lt;code&amp;gt;rails generate migration RemoveAssignmentFromReviewBids&amp;lt;/code&amp;gt;&lt;br /&gt;
# In the generated file, indicate the change that needs to be made using the change method and migration columns &amp;lt;code&amp;gt;remove_foreign_key&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;remove_column&amp;lt;/code&amp;gt;. Migration should be straightforward since there is no cascading behavior (i.e., &amp;lt;code&amp;gt;dependent:destroy&amp;lt;/code&amp;gt;) to worry about.&lt;br /&gt;
# Once comfortable with the updates to the generated file, run the migration using the &amp;lt;code&amp;gt;rails db:migrate&amp;lt;/code&amp;gt; command.&lt;br /&gt;
# Run a query in the DB to ensure no orphaned records are in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table after the updates.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-4.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implement Edge Case Testing ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| In the previous implementation wiki, there are edge cases which are not exhaustively tested. Should test those edge cases thoroughly and add more edge case testing - link&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span class=&amp;quot;mark&amp;quot;&amp;gt;TODO&amp;lt;/span&amp;gt;:We just got initial test results back. We still need to figure out why tests are failing and what may be slipping through the cracks.[[File:Test expertiza.png|411x459px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-8-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design Patterns and Principles ===&lt;br /&gt;
Below are the design patterns and principles we plan to use to address the problem statements outlined in the project requirements:&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
** Controller Responsibility&lt;br /&gt;
*** To address the issue of authentication logic residing in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, we will refactor the code to ensure the controller solely handles request and response flows.&lt;br /&gt;
*** Authentication logic will be moved to dedicated methods in &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt;, making the codebase easier to maintain and test.&lt;br /&gt;
** View Simplification&lt;br /&gt;
*** Currently, the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; view contains excessive business logic, violating SRP.&lt;br /&gt;
*** We will move this logic into helper methods or models, ensuring each component has a single responsibility and improving code readability.&lt;br /&gt;
&lt;br /&gt;
* Separation of Concerns (SoC)&lt;br /&gt;
** By relocating authorization logic out of the controller, we separate authentication from request handling.&lt;br /&gt;
** Adhering to SoC enhances readability and maintainability by allowing each part of the application to focus on its specific role.&lt;br /&gt;
* Don't Repeat Yourself (DRY)&lt;br /&gt;
** We will inspect the existing implementation for code duplication, particularly in the controller actions.&lt;br /&gt;
** Any repetitive code will be refactored or removed to reduce redundancy and improve clarity.&lt;br /&gt;
&lt;br /&gt;
* Model-View-Controller (MVC) Adherence&lt;br /&gt;
** Moving business logic from the view aligns with MVC best practices.&lt;br /&gt;
** By ensuring the view only presents data without processing it, we maintain clear boundaries between the model, view, and controller layers.&lt;br /&gt;
&lt;br /&gt;
* Dependency Inversion Principle (DIP)&lt;br /&gt;
** We plan to update class methods to instance methods, allowing the controller to depend on abstractions rather than concrete implementations.&lt;br /&gt;
** This change leverages Ruby's duck typing, providing flexibility and making the code easier to maintain and test.&lt;br /&gt;
&lt;br /&gt;
* Factory Pattern&lt;br /&gt;
** We will implement a factory pattern for the common processing of &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpBid&amp;lt;/code&amp;gt;, encapsulating object creation and promoting code reuse.&lt;br /&gt;
&lt;br /&gt;
* Law of Demeter (Principle of Least Knowledge)&lt;br /&gt;
** We'll delegate interactions appropriately to prevent tightly coupled code, especially when dealing with topics and teams.&lt;br /&gt;
** Clear and intentional naming during refactoring will enhance code clarity and maintainability.&lt;br /&gt;
&lt;br /&gt;
* Adapter Pattern&lt;br /&gt;
** To add messaging to bid states for users, we'll use the adapter pattern to allow different components to work together without modifying their existing code.&lt;br /&gt;
&lt;br /&gt;
== Files Modified/Added ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-1.-controllers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
review_bids_controller.rb&lt;br /&gt;
&lt;br /&gt;
signup_sheet_controller.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-2.-models&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
review_bid.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-3.-helpers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
&lt;br /&gt;
authorization_helper.rb&lt;br /&gt;
&lt;br /&gt;
Review_bids_helper.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;viii.-test-plan&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
The Excel spreadsheet below details our test plan for testing the code we changed or created to ensure code coverage. The process involved reviewing the code and determining all the possible scenarios that needed to be covered based on what the method was doing, then creating test cases accordingly.&lt;br /&gt;
&lt;br /&gt;
* [https://docs.google.com/spreadsheets/d/1iHzOdSwlTtxOtf5VsNdwOaCP8NzYMmiJ/edit?usp=drive_link Open Test Matrix Spreadsheet]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/review_bids_controller_spec.rb ReviewBidsController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/sign_up_sheet_controller_spec.rb SignUpSheetController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/models/review_bid_spec.rb ReviewBid model updated test cases]&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-1.-mentor&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Ed Gehringer&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-2.-members&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Members ===&lt;br /&gt;
&lt;br /&gt;
* Gavin Teague&lt;br /&gt;
* Janice Uwujaren&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xi.-links&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
Previous Wiki: [https://wiki.expertiza.ncsu.edu/index.php/CSC/ECE_517_Fall_2021_-_E2151._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;Link&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xii.-references&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/document/d/1LHVacylQmB15I6VcYUA5nuC7GRRU9szHzZhjlPcE-PA/edit?tab=t.0#heading=h.u0oycoj6e0ev &amp;lt;u&amp;gt;Project Instructions&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;appendix-a-uml-class-diagram-plantuml-code-for-review-bids&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Juwujar</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160979</id>
		<title>CSC/ECE 517 Fall 2024 - E2485. Allow reviewers to bid on what to review</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160979"/>
		<updated>2024-12-10T23:18:33Z</updated>

		<summary type="html">&lt;p&gt;Juwujar: /* ReviewBidsController Updates */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
This project will fix the defects and improve the functionality in Expertiza of the Reviewer Bidding feature, a process where reviewers can bid for assignments they want to review. It will make the functionality smoother, more reliable, and align with Expertiza's collaborative learning objectives.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iii.-problem-statements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Problem Statements ==&lt;br /&gt;
&lt;br /&gt;
Assigning reviews to users on Expertiza involves a complex bidding algorithm that requires optimization before production deployment. Below are key areas we plan to address as per the project requirements:&lt;br /&gt;
* Code Refactoring and Best Practices&lt;br /&gt;
** '''Utilize Authentication Utilities''': Modify the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; function in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to use existing authentication utilities, enhancing code reusability and adhering to the Single Responsibility Principle (SRP) and Separation of Concerns (SoC).&lt;br /&gt;
** '''Eliminate Code Duplication''': Refactor &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; to comply with the Don't Repeat Yourself (DRY) principle.&lt;br /&gt;
** '''Rename Functions for Clarity''': Change &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; to better reflect its purpose.&lt;br /&gt;
** '''Refactor Methods in '''&amp;lt;code&amp;gt;review_bid.rb&amp;lt;/code&amp;gt;''':''': Convert class methods to instance methods or relocate them to helper classes for improved code organization.&lt;br /&gt;
* Functionality Enhancements&lt;br /&gt;
** '''Handle Missing Bids Gracefully''': Adjust the bidding algorithm to accommodate students who do not bid, allowing them to select from the remaining options.&lt;br /&gt;
** '''Support Both Individual and Team Assignments''': Ensure the code functions correctly for participant and team-based assignments when signing up for topics.&lt;br /&gt;
* User Interface Improvements&lt;br /&gt;
** '''Increase Transparency in the Bidding Process''': Display messages indicating how many students are eligible to bid, how many have submitted bids, and the deadline.&lt;br /&gt;
** '''Accurate Bid Status Indicators''': Ensure topic colors change correctly based on the number of outstanding bids, providing clear visual feedback to users.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Exhaustive Edge Case Testing''': Conduct thorough testing for edge cases not previously covered, adding additional test cases as needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iv.-design-goals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Design Goals ==&lt;br /&gt;
&lt;br /&gt;
While addressing the existing defects and enhancing the Reviewer Bidding feature, we plan to adhere to the following design guidelines:&lt;br /&gt;
&lt;br /&gt;
* Code Quality and Maintainability&lt;br /&gt;
** '''Improve Code Structure: '''Implement refactoring strategies to enhance code readability and maintainability, ensuring adherence to best practices like the DRY principle.&lt;br /&gt;
** '''Standardize Authentication Logic: '''Utilize a centralized authentication utility to promote consistency and reusability across the application.&lt;br /&gt;
* System Stability and Flexibility&lt;br /&gt;
** '''Enhance Algorithm Robustness: '''Modify the bidding algorithm to handle scenarios where students do not bid, ensuring the system remains stable under all conditions.&lt;br /&gt;
** '''Ensure Assignment Versatility: '''Design the system to seamlessly support both individual and team assignments without additional configuration.&lt;br /&gt;
* User Experience Enhancements&lt;br /&gt;
** '''Increase Bidding Transparency: '''Develop user interface elements that clearly display bidding status, deadlines, and participation metrics.&lt;br /&gt;
** '''Implement Dynamic Visual Feedback: '''Use real-time visual cues, such as color changes, to inform users about the bidding status instantly.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Develop Comprehensive Test Suites: '''Create extensive automated tests covering edge cases and critical functionalities to guarantee reliable system behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;v.-uml-diagram&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== UML Diagram ==&lt;br /&gt;
&lt;br /&gt;
Below is a visual representation of the models associated with the review bids functionality, depicting the data structures and their relationships.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml class diagram2.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi.-implementation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
'''ReviewBid: '''The &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; manages the bidding process, where participants indicate their preferences for reviewing different topics or assignments. This helps distribute review tasks fairly based on participants' interests.&lt;br /&gt;
&lt;br /&gt;
'''Assignment: '''The &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model represents team or individual assignments in Expertiza. It stores all the essential information required to keep the assignment process organized, like managing participants, setting due dates, forming teams, and overseeing the review process.&lt;br /&gt;
&lt;br /&gt;
'''SignUpTopic: '''The &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt; model identifies specific topics or areas within an assignment for which participants can sign up. It manages the availability of these topics, tracks bids, coordinates team sign-ups, and assigns topics based on both bids and participant availability. &lt;br /&gt;
&lt;br /&gt;
'''Participant: '''The &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model is about the individual users who participate in an assignment. This includes students, instructors, teaching assistants, or other relevant roles. This model is responsible for managing key user information, monitoring interactions with the assignment, keeping track of team memberships, and recording things like badges and review grades.&lt;br /&gt;
&lt;br /&gt;
== Implementation ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1.-decoupling-authorization-logic-from-reviewbidscontroller&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Decoupling Authorization Logic from &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| '''&amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; needs to use the authentication utilities'''&lt;br /&gt;
&lt;br /&gt;
The function &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; should use the available authentication utilities to support the Single Responsibility Principle and Separation of Concerns and to support code reusability.&lt;br /&gt;
&lt;br /&gt;
| '''Authentication Consistency:''' Remove authentication logic in the controller and call logic in the authentication utility class instead to ensure consistent use of the same logic across the application, foster reusability, and eliminate DRY.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
==== Authorization Flow Before Refactoring ====&lt;br /&gt;
The sequence diagram depicts the authorization process within the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;. When a user invokes an action, the controller checks if the action is allowed by verifying the user's role through the &amp;lt;code&amp;gt;current_role_name()&amp;lt;/code&amp;gt; method in the &amp;lt;code&amp;gt;ApplicationController&amp;lt;/code&amp;gt;. Depending on the action and the user's role, the controller either authorizes the user to proceed or denies access, ultimately executing the action logic if authorized and responding to the user.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 1.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We simplified the &amp;lt;code&amp;gt;action_allowed?&amp;lt;/code&amp;gt; method by decoupling authorization logic from the controller to use helper methods in the &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt; module. The &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; should not handle authentication, which is a violation of SRP. &lt;br /&gt;
&lt;br /&gt;
Note: We also made similar updates to the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; methods for &amp;lt;code&amp;gt;SignUpController&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;vertical-align:top;&amp;quot; | Original Code !! style=&amp;quot;vertical-align:top;&amp;quot; | Refactored Code&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index'&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator',&lt;br /&gt;
     'Student'].include? current_role_name and&lt;br /&gt;
        ((%w[list].include? action_name) ? are_needed_authorizations_present?(params[:id], &amp;quot;participant&amp;quot;, &amp;quot;reader&amp;quot;, &amp;quot;submitter&amp;quot;, &amp;quot;reviewer&amp;quot;) : true)&lt;br /&gt;
  else&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator'].include? current_role_name&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index', 'list'&lt;br /&gt;
    current_user_has_student_privileges? &amp;amp;&amp;amp; list_authorization_check&lt;br /&gt;
  else&lt;br /&gt;
    current_user_has_ta_privileges?&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; action ===&lt;br /&gt;
Previously, the &amp;lt;code&amp;gt;ReviewBidsController's index&amp;lt;/code&amp;gt; action was overloaded with responsibilities—it handled everything from locating and validating assignments to fetching review mappings and counting completed reviews. This all-in-one approach made the controller action messy. &lt;br /&gt;
&lt;br /&gt;
We have moved the business logic from the controller action and into dedicated service objects. With this change, we introduced two key service objects: &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; focuses on retrieving the participant along with their assignments, while the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; takes on the task of managing review mappings and calculating review-related stats, like how many reviews have been completed. &lt;br /&gt;
&lt;br /&gt;
This separation of responsibilities aligns with the Single Responsibility Principle. The logic for fetching assignments, retrieving review mappings, and counting reviews is now more flexible and reusable throughout the application. For example, we used methods in the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; in both the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; and the &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;, helping to avoid code duplication.The article https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial served as a guide on when and how to use service objects in Rails.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
  return unless current_user_id?(@participant.user_id)&lt;br /&gt;
  @assignment = @participant.assignment&lt;br /&gt;
  @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
  # Finding how many reviews have been completed&lt;br /&gt;
  @num_reviews_completed = 0&lt;br /&gt;
  @review_mappings.each do |map|&lt;br /&gt;
    @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # render view for completing reviews after review bidding has been completed&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @assignment = participant_service.assignment&lt;br /&gt;
  unless @assignment.is_a?(Assignment)&lt;br /&gt;
    flash[:error] = 'Assignment not found.'&lt;br /&gt;
    redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  @review_mappings = review_service.review_mappings&lt;br /&gt;
  @num_reviews_completed = review_service.review_counts[:completed]&lt;br /&gt;
&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Optimize &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Action functionality ===&lt;br /&gt;
In the earlier version of the set_priority action in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, the server only needed a couple of key pieces of information: an array of topic IDs (params[:topic]) and the participant ID (params[:id}). From there, it would figure out the assignment_id by using the first topic as a reference. &lt;br /&gt;
&lt;br /&gt;
With the new implementation, the action now expects clear and structured parameters, including participant_id, an array of topic IDs, and assignment_id. This change ensures that the server always receives complete and reliable information, preventing issues caused by missing or inconsistent data. It also removes the need for the server to perform unnecessary lookups, allowing the controller to focus on its main job: validating inputs (like making sure the topics in params[:topic] belong to the same assignment as the participant), updating the database, handling errors properly, and sending the correct response back to the view.&lt;br /&gt;
&lt;br /&gt;
While these updates have cleaned up the set_priority action, the action still contains a lot of business logic, like validations and priority updates. To make the code even more maintainable, there’s room for further refactoring—perhaps moving some of this logic into a service class or model method to streamline the controller even more.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | Old Code&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    if params[:topic].nil?&lt;br /&gt;
      ReviewBid.where(participant_id: params[:id]).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      participant = AssignmentParticipant.find_by(id: params[:id])&lt;br /&gt;
      assignment_id = SignUpTopic.find(params[:topic].first).assignment.id&lt;br /&gt;
      @bids = ReviewBid.where(participant_id: params[:id])&lt;br /&gt;
      signed_up_topics = ReviewBid.where(participant_id: params[:id]).map(&amp;amp;:signuptopic_id)&lt;br /&gt;
      signed_up_topics -= params[:topic].map(&amp;amp;:to_i)&lt;br /&gt;
      signed_up_topics.each do |topic|&lt;br /&gt;
        ReviewBid.where(signuptopic_id: topic, participant_id: params[:id]).destroy_all&lt;br /&gt;
      end&lt;br /&gt;
      params[:topic].each_with_index do |topic_id, index|&lt;br /&gt;
        bid_existence = ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id])&lt;br /&gt;
        if bid_existence.empty?&lt;br /&gt;
          ReviewBid.create(priority: index + 1, signuptopic_id: topic_id, participant_id: params[:id], assignment_id: assignment_id)&lt;br /&gt;
        else&lt;br /&gt;
          ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id]).update_all(priority: index + 1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to action: 'show', assignment_id: params[:assignment_id], id: params[:id]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    participant_id = params[:participant_id].to_i&lt;br /&gt;
    selected_topic_ids = params[:topic]&amp;amp;.map(&amp;amp;:to_i) || []&lt;br /&gt;
    assignment_id = params[:assignment_id].to_i&lt;br /&gt;
&lt;br /&gt;
    unless current_user_can_modify_participant?(participant_id)&lt;br /&gt;
      flash[:error] = 'You are not authorized to modify these bids.'&lt;br /&gt;
      respond_to do |format|&lt;br /&gt;
        format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
        format.json { render json: { status: 'unauthorized' }, status: :unauthorized }&lt;br /&gt;
      end&lt;br /&gt;
      return&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    if selected_topic_ids.empty?&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      assignment = Assignment.find_by(id: assignment_id)&lt;br /&gt;
      if assignment.nil?&lt;br /&gt;
        flash[:error] = &amp;quot;Invalid assignment.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_assignment' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      unless SignUpTopic.where(id: selected_topic_ids, assignment_id: assignment_id).count == selected_topic_ids.size&lt;br /&gt;
        flash[:error] = &amp;quot;One or more selected topics are invalid.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_topics' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).where.not(signuptopic_id: selected_topic_ids).destroy_all&lt;br /&gt;
&lt;br /&gt;
      selected_topic_ids.each_with_index do |topic_id, index|&lt;br /&gt;
        bid = ReviewBid.find_or_initialize_by(signuptopic_id: topic_id, participant_id: participant_id)&lt;br /&gt;
        bid.priority = index + 1&lt;br /&gt;
        bid.assignment_id = assignment_id&lt;br /&gt;
        bid.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_to action: 'show', assignment_id: assignment_id, id: participant_id, notice: 'Review bids updated successfully.' }&lt;br /&gt;
      format.json { render json: { status: 'success' }, status: :ok }&lt;br /&gt;
    end&lt;br /&gt;
  rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
    flash[:error] = &amp;quot;Failed to update priorities: #{e.message}&amp;quot;&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
      format.json { render json: { status: 'error', message: e.message }, status: :unprocessable_entity }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Client Side Changes to Support &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Changes ====&lt;br /&gt;
After tidying up the server-side logic, we needed to refactor the client-side code to ensure it passed the correct information from the view to the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action. On the client side, we now grab the topicIDs and send them as &amp;lt;code&amp;gt;selected_topic_ids&amp;lt;/code&amp;gt;, allowing the controller to figure out which topics were selected and in what order.  &lt;br /&gt;
&lt;br /&gt;
We also extracted the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, making it a lot easier to send data with AJAX and keeping things flexible. When working with jQuery, AJAX is awesome because it lets us update the server without reloading the page, making the user experience smooth and responsive. When we pull the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, we can grab it along with other data, like the list of topics, and include it in the request. This way, we don’t have to rely on the URL to know which participant we’re working with, which makes the code cleaner and more adaptable.&lt;br /&gt;
&lt;br /&gt;
If we were to rely on the route (as opposed to using the DOM) for the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt;, every AJAX request would need to be tied to a specific URL structure. That’s limiting, especially if the route doesn’t naturally include the participant ID, as can be the case with collection routes versus member routes in Rails. &lt;br /&gt;
&lt;br /&gt;
Note: One interesting aspect of the application was the mix of JavaScript and CoffeeScript code. It wasn't clear why both types of code were used.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery -&amp;gt;&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: -&amp;gt;&lt;br /&gt;
      $.post($(this).data('update-url'), $(this).sortable('serialize'))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery(document).ready ($) -&amp;gt;&lt;br /&gt;
  sendUpdatedOrder = -&amp;gt;&lt;br /&gt;
    sortedIDs = $(this).sortable('toArray')&lt;br /&gt;
&lt;br /&gt;
    topicIDs = sortedIDs.map (id) -&amp;gt;&lt;br /&gt;
      id.replace 'topic_', ''&lt;br /&gt;
&lt;br /&gt;
    participant_id = $(this).data('participant-id')&lt;br /&gt;
&lt;br /&gt;
    assignment_id = new URL($(this).data('update-url'), window.location.origin).searchParams.get('assignment_id')&lt;br /&gt;
&lt;br /&gt;
    $.ajax&lt;br /&gt;
      type: 'POST'&lt;br /&gt;
      url: $(this).data('update-url')&lt;br /&gt;
      data:&lt;br /&gt;
        topic: topicIDs&lt;br /&gt;
        participant_id: participant_id&lt;br /&gt;
        assignment_id: assignment_id&lt;br /&gt;
      success: (response) -&amp;gt;&lt;br /&gt;
        console.log 'Selections updated successfully.'&lt;br /&gt;
      error: (xhr, status, error) -&amp;gt;&lt;br /&gt;
        alert 'An error occurred while saving your selections. Please try again.'&lt;br /&gt;
        console.error error&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: sendUpdatedOrder&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-1.-signupsheetcontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; Updates ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; method renders the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; action in &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt;. However, the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; does not include the controller action for this view. We need to find the old implementation of the controller action, add it back, and review and update it for any DRY violations.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # provides variables for reviewing page located at views/review_bids/others_work.html.erb&lt;br /&gt;
  def index&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
    return unless current_user_id?(@participant.user_id)&lt;br /&gt;
&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
    # Finding how many reviews have been completed&lt;br /&gt;
    @num_reviews_completed = 0&lt;br /&gt;
    @review_mappings.each do |map|&lt;br /&gt;
      @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # render view for completing reviews after review bidding has been completed&lt;br /&gt;
    render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
  end  &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-2.-reviewbidsotherswork-view-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;review_bids_others_work.html.erb&amp;lt;/code&amp;gt; View Updates ====&lt;br /&gt;
&lt;br /&gt;
The code only contains a view with this name and no associated controller action. Reviewing the code in the view, we noticed some SRP violations where there is much logic in the presentation layer. Set up helper methods in the controller to handle some of that logic or see if the model can handle some of that logic.&lt;br /&gt;
&lt;br /&gt;
Note: The view is technically part of the SignUpSheetController, but it's being rendered by the ReviewBidsController, which initially threw us off. After chatting with the professor, we decided not to change this view. He indicated that the plan is to reuse these views for both assignment bidding and review bidding.&lt;br /&gt;
&lt;br /&gt;
We would suggest building the assignment bidding and review bidding features separately first to make sure each one works on its own. Once that’s done, we can go back and clean things up by combining any shared functionality. This could include creating shared views or even introducing an abstract model to handle both types of bidding in a cleaner way. It could also mean replacing the ReviewBidsController with something more generic, like a BidsController, to keep things reusable and easier to maintain down the line.&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Rename &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; method ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; should be &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt;&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:''' Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The primary activity here is to refactor the method name &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; and fix the hardcoded URL. However, the match topic bidding algorithm no longer works since Heroku is no longer free, per the professor. The reference link below has detailed information about the web service that we can leverage to gain that understanding.&lt;br /&gt;
&lt;br /&gt;
Reference Link for Webservice Details: [https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Updated Implementation ====&lt;br /&gt;
The original requirement was to rename the run_bidding_algorithm method to assign_reviewers, but after digging into the code, we realized that renaming it didn’t make sense. The method runs the bidding algorithm to match topics, and there’s already a separate method to handle assigning review topics.&lt;br /&gt;
&lt;br /&gt;
However, we improved the logic in the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; method (which calls the private &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt;) by introducing a separate &amp;lt;code&amp;gt;BiddingAlgorithmService&amp;lt;/code&amp;gt; object. This keeps the business logic out of the controller action, which makes things cleaner and more maintainable.&lt;br /&gt;
&lt;br /&gt;
==== review_bids_controller ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def run_bidding_algorithm&lt;br /&gt;
    review_bid = ReviewBid.new&lt;br /&gt;
    bidding_data = review_bid.bidding_data&lt;br /&gt;
    matched_topics = BiddingAlgorithmService.new(bidding_data).run&lt;br /&gt;
    raise ArgumentError, 'Failed to assign reviewers. Please try again later.' unless matched_topics&lt;br /&gt;
&lt;br /&gt;
    matched_topics&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== bidding_algorithm_service ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# frozen_string_literal: true&lt;br /&gt;
&lt;br /&gt;
# The `BiddingAlgorithmService` run the bid assignment algorithm&lt;br /&gt;
# Sends student IDs, topic IDs, student preferences, and timestamps to the web service&lt;br /&gt;
# The web service returns the matched assignments in the JSON response body&lt;br /&gt;
class BiddingAlgorithmService&lt;br /&gt;
  SERVICE_URL = 'http://app-csc517.herokuapp.com/match_topics'.freeze&lt;br /&gt;
&lt;br /&gt;
  # MOCK_DATA is based on the documentation detailing how the webservice behaves.&lt;br /&gt;
  # Reference: https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review#Webservice&lt;br /&gt;
  MOCK_DATA = {&lt;br /&gt;
    36239 =&amp;gt; [3970, 3972, 3975],&lt;br /&gt;
    36240 =&amp;gt; [3973, 3974, 3972],&lt;br /&gt;
    36241 =&amp;gt; [3969, 3971, 3972],&lt;br /&gt;
    36242 =&amp;gt; [3969, 3971, 3973],&lt;br /&gt;
    36243 =&amp;gt; [3969, 3970, 3971]&lt;br /&gt;
  }.freeze&lt;br /&gt;
&lt;br /&gt;
  def initialize(bidding_data)&lt;br /&gt;
    @bidding_data = bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def run&lt;br /&gt;
    return MOCK_DATA if Rails.application.config.use_mock_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
    perform_request&lt;br /&gt;
  rescue RestClient::ExceptionWithResponse, JSON::ParserError&lt;br /&gt;
    false&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def perform_request&lt;br /&gt;
    response = RestClient.post(&lt;br /&gt;
      SERVICE_URL,&lt;br /&gt;
      @bidding_data.to_json,&lt;br /&gt;
      content_type: :json,&lt;br /&gt;
      accept: :json&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    validate_response(response)&lt;br /&gt;
    JSON.parse(response.body)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def validate_response(response)&lt;br /&gt;
    raise 'Invalid response format' unless response.headers[:content_type] &amp;amp;&amp;amp; response.headers[:content_type].include?('application/json')&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Resolve Functionality Issues for Non Bidders ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Currently it doesn't work if some student does not bid. In this case, algorithm needs to be fixed to ignore anyone who didn’t bid, and let them choose from what’s left over.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to do several things within this controller. We will set a default state for non-bidders that we can point to to give them options to select topics to review from the leftovers. We will consolidate &amp;lt;code&amp;gt;@sign_up_topics&amp;lt;/code&amp;gt; to resolve the DRY violation. Lastly, we will remove business logic from the show method and push it to a new helper to conform to the SRP.&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # Additional method&lt;br /&gt;
&lt;br /&gt;
  # Provides variables for review bidding page&lt;br /&gt;
  def show&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id].to_i)&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @sign_up_topics = SignUpTopic.where(assignment_id: @assignment.id, private_to: nil)&lt;br /&gt;
    my_topic = SignedUpTeam.topic_id(@participant.parent_id, @participant.user_id)&lt;br /&gt;
    @sign_up_topics -= SignUpTopic.where(assignment_id: @assignment.id, id: my_topic)&lt;br /&gt;
    @num_participants = AssignmentParticipant.where(parent_id: @assignment.id).count&lt;br /&gt;
    @selected_topics = nil # This is used to list the topics assigned to review (i.e., select == assigned)&lt;br /&gt;
    @bids = ReviewBid.where(participant_id: @participant, assignment_id: @assignment.id)&lt;br /&gt;
    signed_up_topics = []&lt;br /&gt;
    &lt;br /&gt;
    @bids.each do |bid|&lt;br /&gt;
      sign_up_topic = SignUpTopic.find_by(id: bid.signuptopic_id)&lt;br /&gt;
      signed_up_topics &amp;lt;&amp;lt; sign_up_topic if sign_up_topic&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    signed_up_topics &amp;amp;= @sign_up_topics&lt;br /&gt;
    @sign_up_topics -= signed_up_topics&lt;br /&gt;
    @bids = signed_up_topics&lt;br /&gt;
    @num_of_topics = @sign_up_topics.size&lt;br /&gt;
    @assigned_review_maps = []&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: @assignment.id, reviewer_id: @participant.id).each do |review_map|&lt;br /&gt;
      @assigned_review_maps &amp;lt;&amp;lt; review_map&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Explicitly render view since it's in the sign up sheet views&lt;br /&gt;
    render 'sign_up_sheet/review_bids_show'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Additional methods&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-2.-reviewbidshelper-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
 # Assigns bidding topics to reviewers&lt;br /&gt;
  def assign_bidding&lt;br /&gt;
      assignment = validate_assignment(params[:assignment_id])&lt;br /&gt;
&lt;br /&gt;
      reviewers = validate_reviewers(assignment.id)&lt;br /&gt;
      reviewer_ids = reviewers.map(&amp;amp;:id)&lt;br /&gt;
&lt;br /&gt;
      matched_topics = run_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
      if matched_topics.blank?&lt;br /&gt;
        flash[:alert] = 'Topic or assignment is missing'&lt;br /&gt;
        redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      leftover_topics = find_leftover_topics(assignment.id, matched_topics)&lt;br /&gt;
      assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.new.assign_review_topics(matched_topics)&lt;br /&gt;
      assignment.update!(can_choose_topic_to_review: false)&lt;br /&gt;
&lt;br /&gt;
      flash[:notice] = 'Reviewers were successfully assigned to topics.'&lt;br /&gt;
      redirect_back fallback_location: root_path&lt;br /&gt;
    rescue ArgumentError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ArgumentError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: e.message&lt;br /&gt;
    rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::RecordInvalid: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue ActiveRecord::ActiveRecordError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::ActiveRecordError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue StandardError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;StandardError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers. Please try again later.'&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  #service methods here ...&lt;br /&gt;
&lt;br /&gt;
  def find_leftover_topics(assignment_id, matched_topics)&lt;br /&gt;
    all_topic_ids = SignUpTopic.where(assignment_id: assignment_id).pluck(:id)&lt;br /&gt;
    assigned_topic_ids = matched_topics.map { |match| match[:topic_id] }&lt;br /&gt;
&lt;br /&gt;
    # Calculate leftover topics&lt;br /&gt;
    all_topic_ids - assigned_topic_ids&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
    return if leftover_topics.blank?&lt;br /&gt;
&lt;br /&gt;
    # Find non-bidders by excluding already matched reviewers&lt;br /&gt;
    non_bidders = reviewer_ids - matched_topics.keys&lt;br /&gt;
&lt;br /&gt;
    # Assign leftover topics to non-bidders in a round-robin fashion&lt;br /&gt;
    non_bidders.each_with_index do |reviewer_id, index|&lt;br /&gt;
      topic_id = leftover_topics[index % leftover_topics.length]&lt;br /&gt;
      ReviewBid.create(priority: 1, signuptopic_id: topic_id, participant_id: reviewer_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirm Functionality for Single-person Teams ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Make sure your code works on individual assignments, i.e., assignments where participants sign up for topics instead of teams. In this case, a team is created for each participant when they sign up. So the code should work for assignments to which either individuals or teams submit.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to make an update to focus on topic rather than team or have it where teams are updated based on topic id.We will probably utilize sign up topic or a similar object to accomplish this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-5-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bid Status Messaging ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| To make the functionality more intuitive, include a message to say how many students are eligible to submit bids, how many have submitted their bids, when the deadline for submitting bids is.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
The logic for these messages will go into the helper file. It is effectively the same reasoning as our migration of other business/functionality code from the controller to the helper to conform to the SRP. It will go hand in hand with the SignUpTopic portion of the code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-6-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Why are the methods in review_bid.rb class methods? Can we change them to instance methods or move it to helpers?&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-1.-reviewbid-model-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model Updates ====&lt;br /&gt;
Since the current class methods are tied to &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model instances, we will change them to instance methods. There is no need to move them to helpers.&lt;br /&gt;
&lt;br /&gt;
===== Review Bid Model Flow Before Refactoring =====&lt;br /&gt;
This sequence diagram describes how the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; interacts with the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model in assigning review topics to reviewers according to their bids. Once the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; action is initiated by the user, the controller gathers the IDs of reviewers and calls &amp;lt;code&amp;gt;ReviewBid.bidding_data&amp;lt;/code&amp;gt; to gather bidding information. That is sent out to an external bidding algorithm, and the matched topics that come back are used by &amp;lt;code&amp;gt;ReviewBid.assign_review_topics&amp;lt;/code&amp;gt; to create &amp;lt;code&amp;gt;ReviewResponseMap&amp;lt;/code&amp;gt; entries, assigning topics to reviewers for the assignment.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
  belongs_to :assignment, class_name: 'Assignment'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  def self.bidding_data(assignment_id, reviewer_ids)&lt;br /&gt;
    # create basic hash and set basic hash data&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'users' =&amp;gt; {}, 'max_accepted_proposals' =&amp;gt; [] }&lt;br /&gt;
    bidding_data['tid'] = SignUpTopic.where(assignment_id: assignment_id).ids&lt;br /&gt;
    bidding_data['max_accepted_proposals'] = Assignment.where(id: assignment_id).pluck(:num_reviews_allowed).first&lt;br /&gt;
&lt;br /&gt;
    # loop through reviewer_ids to get reviewer specific bidding data&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      bidding_data['users'][reviewer_id] = reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # assigns topics to reviews as matched by the webservice algorithm&lt;br /&gt;
  def self.assign_review_topics(assignment_id, reviewer_ids, matched_topics, _min_num_reviews = 2)&lt;br /&gt;
    # if review response map already created, delete it&lt;br /&gt;
    if ReviewResponseMap.where(reviewed_object_id: assignment_id)&lt;br /&gt;
      ReviewResponseMap.where(reviewed_object_id: assignment_id).destroy_all&lt;br /&gt;
    end&lt;br /&gt;
    # loop through reviewer_ids to assign reviews to each reviewer&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      topics_to_assign = matched_topics[reviewer_id.to_s]&lt;br /&gt;
      topics_to_assign.each do |topic|&lt;br /&gt;
        assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method to assign a single topic to a reviewer&lt;br /&gt;
  def self.assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic).pluck(:team_id).first&lt;br /&gt;
    team_to_review.nil? ? [] : ReviewResponseMap.create(reviewed_object_id: assignment_id, reviewer_id: reviewer_id, reviewee_id: team_to_review, type: 'ReviewResponseMap')&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method for getting individual reviewer_ids bidding data&lt;br /&gt;
  # returns user's bidding data hash&lt;br /&gt;
  def self.reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    reviewer_user_id = AssignmentParticipant.find(reviewer_id).user_id&lt;br /&gt;
    self_topic = SignedUpTeam.topic_id(assignment_id, reviewer_user_id)&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'otid' =&amp;gt; self_topic, 'priority' =&amp;gt; [], 'time' =&amp;gt; [] }&lt;br /&gt;
    bids = ReviewBid.where(participant_id: reviewer_id)&lt;br /&gt;
&lt;br /&gt;
    # loop through each bid for a topic to get specific data&lt;br /&gt;
    bids.each do |bid|&lt;br /&gt;
      bidding_data['tid'] &amp;lt;&amp;lt; bid.signuptopic_id&lt;br /&gt;
      bidding_data['priority'] &amp;lt;&amp;lt; bid.priority&lt;br /&gt;
      bidding_data['time'] &amp;lt;&amp;lt; bid.updated_at&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  # Instance method to get bidding data for this review bid&lt;br /&gt;
  def bidding_data&lt;br /&gt;
    {&lt;br /&gt;
      'tid' =&amp;gt; topic_ids,&lt;br /&gt;
      'otid' =&amp;gt; self_topic,&lt;br /&gt;
      'priority' =&amp;gt; priorities,&lt;br /&gt;
      'time' =&amp;gt; updated_times&lt;br /&gt;
    }&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # bid object to limit db touches for similar data acquisitions&lt;br /&gt;
  def bids&lt;br /&gt;
    @bids ||= ReviewBid.where(participant_id: participant_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def topic_ids&lt;br /&gt;
    bids.pluck(:signuptopic_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def priorities&lt;br /&gt;
    bids.pluck(:priority)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def updated_times&lt;br /&gt;
    bids.pluck(:updated_at)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self_topic&lt;br /&gt;
    return nil unless topic &amp;amp;&amp;amp; topic.assignment_id&lt;br /&gt;
&lt;br /&gt;
    SignedUpTeam.topic_id(topic.assignment_id, participant.user_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign topics to reviews&lt;br /&gt;
  def assign_review_topics(matched_topics)&lt;br /&gt;
    validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: topic.assignment_id).destroy_all&lt;br /&gt;
&lt;br /&gt;
    matched_topics.each do |reviewer_id, topics|&lt;br /&gt;
      Array(topics).each { |topic_id| assign_topic_to_reviewer(reviewer_id, topic_id) }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign a single topic to a reviewer&lt;br /&gt;
  def assign_topic_to_reviewer(reviewer_id, topic_id)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic_id).pluck(:team_id).first&lt;br /&gt;
    return if team_to_review.nil?&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.create(&lt;br /&gt;
      reviewed_object_id: topic.assignment_id,&lt;br /&gt;
      reviewer_id: reviewer_id,&lt;br /&gt;
      reviewee_id: team_to_review,&lt;br /&gt;
      type: 'ReviewResponseMap'&lt;br /&gt;
    )&lt;br /&gt;
    # error handling&lt;br /&gt;
  rescue StandardError =&amp;gt; e&lt;br /&gt;
    Rails.logger.error(&amp;quot;Failed to assign topic #{topic_id} to reviewer #{reviewer_id}: #{e.message}&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
    raise ArgumentError, 'Topic or assignment is missing' if topic.nil? || topic.assignment_id.nil?&lt;br /&gt;
    raise ArgumentError, 'Matched topics must be a Hash' unless matched_topics.is_a?(Hash)&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
Once we changed them to instance methods, we modified their use in the controller code to instantiate the object first.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-3.-schema.db-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;Schema.db&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We also need to remove the foreign key reference to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model since the &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model already has access to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;, and that is the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; we should be referencing. This will also involve modifying the database to remove the foreign key reference to the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Steps required:&lt;br /&gt;
&lt;br /&gt;
# Review the records in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table in the database to see if there are any records and if those records have values for &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;&lt;br /&gt;
# Create a migration file using the command: &amp;lt;code&amp;gt;rails generate migration RemoveAssignmentFromReviewBids&amp;lt;/code&amp;gt;&lt;br /&gt;
# In the generated file, indicate the change that needs to be made using the change method and migration columns &amp;lt;code&amp;gt;remove_foreign_key&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;remove_column&amp;lt;/code&amp;gt;. Migration should be straightforward since there is no cascading behavior (i.e., &amp;lt;code&amp;gt;dependent:destroy&amp;lt;/code&amp;gt;) to worry about.&lt;br /&gt;
# Once comfortable with the updates to the generated file, run the migration using the &amp;lt;code&amp;gt;rails db:migrate&amp;lt;/code&amp;gt; command.&lt;br /&gt;
# Run a query in the DB to ensure no orphaned records are in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table after the updates.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;Placeholder for implementation screenshots (before and after)&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-4.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implement Edge Case Testing ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| In the previous implementation wiki, there are edge cases which are not exhaustively tested. Should test those edge cases thoroughly and add more edge case testing - link&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span class=&amp;quot;mark&amp;quot;&amp;gt;TODO&amp;lt;/span&amp;gt;:We just got initial test results back. We still need to figure out why tests are failing and what may be slipping through the cracks.[[File:Test expertiza.png|411x459px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-8-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design Patterns and Principles ===&lt;br /&gt;
Below are the design patterns and principles we plan to use to address the problem statements outlined in the project requirements:&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
** Controller Responsibility&lt;br /&gt;
*** To address the issue of authentication logic residing in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, we will refactor the code to ensure the controller solely handles request and response flows.&lt;br /&gt;
*** Authentication logic will be moved to dedicated methods in &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt;, making the codebase easier to maintain and test.&lt;br /&gt;
** View Simplification&lt;br /&gt;
*** Currently, the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; view contains excessive business logic, violating SRP.&lt;br /&gt;
*** We will move this logic into helper methods or models, ensuring each component has a single responsibility and improving code readability.&lt;br /&gt;
&lt;br /&gt;
* Separation of Concerns (SoC)&lt;br /&gt;
** By relocating authorization logic out of the controller, we separate authentication from request handling.&lt;br /&gt;
** Adhering to SoC enhances readability and maintainability by allowing each part of the application to focus on its specific role.&lt;br /&gt;
* Don't Repeat Yourself (DRY)&lt;br /&gt;
** We will inspect the existing implementation for code duplication, particularly in the controller actions.&lt;br /&gt;
** Any repetitive code will be refactored or removed to reduce redundancy and improve clarity.&lt;br /&gt;
&lt;br /&gt;
* Model-View-Controller (MVC) Adherence&lt;br /&gt;
** Moving business logic from the view aligns with MVC best practices.&lt;br /&gt;
** By ensuring the view only presents data without processing it, we maintain clear boundaries between the model, view, and controller layers.&lt;br /&gt;
&lt;br /&gt;
* Dependency Inversion Principle (DIP)&lt;br /&gt;
** We plan to update class methods to instance methods, allowing the controller to depend on abstractions rather than concrete implementations.&lt;br /&gt;
** This change leverages Ruby's duck typing, providing flexibility and making the code easier to maintain and test.&lt;br /&gt;
&lt;br /&gt;
* Factory Pattern&lt;br /&gt;
** We will implement a factory pattern for the common processing of &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpBid&amp;lt;/code&amp;gt;, encapsulating object creation and promoting code reuse.&lt;br /&gt;
&lt;br /&gt;
* Law of Demeter (Principle of Least Knowledge)&lt;br /&gt;
** We'll delegate interactions appropriately to prevent tightly coupled code, especially when dealing with topics and teams.&lt;br /&gt;
** Clear and intentional naming during refactoring will enhance code clarity and maintainability.&lt;br /&gt;
&lt;br /&gt;
* Adapter Pattern&lt;br /&gt;
** To add messaging to bid states for users, we'll use the adapter pattern to allow different components to work together without modifying their existing code.&lt;br /&gt;
&lt;br /&gt;
== Files Modified/Added ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-1.-controllers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
review_bids_controller.rb&lt;br /&gt;
&lt;br /&gt;
signup_sheet_controller.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-2.-models&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
review_bid.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-3.-helpers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
&lt;br /&gt;
authorization_helper.rb&lt;br /&gt;
&lt;br /&gt;
Review_bids_helper.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;viii.-test-plan&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
The Excel spreadsheet below details our test plan for testing the code we changed or created to ensure code coverage. The process involved reviewing the code and determining all the possible scenarios that needed to be covered based on what the method was doing, then creating test cases accordingly.&lt;br /&gt;
&lt;br /&gt;
* [https://docs.google.com/spreadsheets/d/1iHzOdSwlTtxOtf5VsNdwOaCP8NzYMmiJ/edit?usp=drive_link Open Test Matrix Spreadsheet]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/review_bids_controller_spec.rb ReviewBidsController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/sign_up_sheet_controller_spec.rb SignUpSheetController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/models/review_bid_spec.rb ReviewBid model updated test cases]&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-1.-mentor&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Ed Gehringer&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-2.-members&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Members ===&lt;br /&gt;
&lt;br /&gt;
* Gavin Teague&lt;br /&gt;
* Janice Uwujaren&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xi.-links&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
Previous Wiki: [https://wiki.expertiza.ncsu.edu/index.php/CSC/ECE_517_Fall_2021_-_E2151._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;Link&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xii.-references&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/document/d/1LHVacylQmB15I6VcYUA5nuC7GRRU9szHzZhjlPcE-PA/edit?tab=t.0#heading=h.u0oycoj6e0ev &amp;lt;u&amp;gt;Project Instructions&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;appendix-a-uml-class-diagram-plantuml-code-for-review-bids&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Juwujar</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160978</id>
		<title>CSC/ECE 517 Fall 2024 - E2485. Allow reviewers to bid on what to review</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160978"/>
		<updated>2024-12-10T23:12:51Z</updated>

		<summary type="html">&lt;p&gt;Juwujar: /* Updated Implementation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
This project will fix the defects and improve the functionality in Expertiza of the Reviewer Bidding feature, a process where reviewers can bid for assignments they want to review. It will make the functionality smoother, more reliable, and align with Expertiza's collaborative learning objectives.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iii.-problem-statements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Problem Statements ==&lt;br /&gt;
&lt;br /&gt;
Assigning reviews to users on Expertiza involves a complex bidding algorithm that requires optimization before production deployment. Below are key areas we plan to address as per the project requirements:&lt;br /&gt;
* Code Refactoring and Best Practices&lt;br /&gt;
** '''Utilize Authentication Utilities''': Modify the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; function in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to use existing authentication utilities, enhancing code reusability and adhering to the Single Responsibility Principle (SRP) and Separation of Concerns (SoC).&lt;br /&gt;
** '''Eliminate Code Duplication''': Refactor &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; to comply with the Don't Repeat Yourself (DRY) principle.&lt;br /&gt;
** '''Rename Functions for Clarity''': Change &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; to better reflect its purpose.&lt;br /&gt;
** '''Refactor Methods in '''&amp;lt;code&amp;gt;review_bid.rb&amp;lt;/code&amp;gt;''':''': Convert class methods to instance methods or relocate them to helper classes for improved code organization.&lt;br /&gt;
* Functionality Enhancements&lt;br /&gt;
** '''Handle Missing Bids Gracefully''': Adjust the bidding algorithm to accommodate students who do not bid, allowing them to select from the remaining options.&lt;br /&gt;
** '''Support Both Individual and Team Assignments''': Ensure the code functions correctly for participant and team-based assignments when signing up for topics.&lt;br /&gt;
* User Interface Improvements&lt;br /&gt;
** '''Increase Transparency in the Bidding Process''': Display messages indicating how many students are eligible to bid, how many have submitted bids, and the deadline.&lt;br /&gt;
** '''Accurate Bid Status Indicators''': Ensure topic colors change correctly based on the number of outstanding bids, providing clear visual feedback to users.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Exhaustive Edge Case Testing''': Conduct thorough testing for edge cases not previously covered, adding additional test cases as needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iv.-design-goals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Design Goals ==&lt;br /&gt;
&lt;br /&gt;
While addressing the existing defects and enhancing the Reviewer Bidding feature, we plan to adhere to the following design guidelines:&lt;br /&gt;
&lt;br /&gt;
* Code Quality and Maintainability&lt;br /&gt;
** '''Improve Code Structure: '''Implement refactoring strategies to enhance code readability and maintainability, ensuring adherence to best practices like the DRY principle.&lt;br /&gt;
** '''Standardize Authentication Logic: '''Utilize a centralized authentication utility to promote consistency and reusability across the application.&lt;br /&gt;
* System Stability and Flexibility&lt;br /&gt;
** '''Enhance Algorithm Robustness: '''Modify the bidding algorithm to handle scenarios where students do not bid, ensuring the system remains stable under all conditions.&lt;br /&gt;
** '''Ensure Assignment Versatility: '''Design the system to seamlessly support both individual and team assignments without additional configuration.&lt;br /&gt;
* User Experience Enhancements&lt;br /&gt;
** '''Increase Bidding Transparency: '''Develop user interface elements that clearly display bidding status, deadlines, and participation metrics.&lt;br /&gt;
** '''Implement Dynamic Visual Feedback: '''Use real-time visual cues, such as color changes, to inform users about the bidding status instantly.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Develop Comprehensive Test Suites: '''Create extensive automated tests covering edge cases and critical functionalities to guarantee reliable system behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;v.-uml-diagram&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== UML Diagram ==&lt;br /&gt;
&lt;br /&gt;
Below is a visual representation of the models associated with the review bids functionality, depicting the data structures and their relationships.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml class diagram2.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi.-implementation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
'''ReviewBid: '''The &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; manages the bidding process, where participants indicate their preferences for reviewing different topics or assignments. This helps distribute review tasks fairly based on participants' interests.&lt;br /&gt;
&lt;br /&gt;
'''Assignment: '''The &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model represents team or individual assignments in Expertiza. It stores all the essential information required to keep the assignment process organized, like managing participants, setting due dates, forming teams, and overseeing the review process.&lt;br /&gt;
&lt;br /&gt;
'''SignUpTopic: '''The &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt; model identifies specific topics or areas within an assignment for which participants can sign up. It manages the availability of these topics, tracks bids, coordinates team sign-ups, and assigns topics based on both bids and participant availability. &lt;br /&gt;
&lt;br /&gt;
'''Participant: '''The &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model is about the individual users who participate in an assignment. This includes students, instructors, teaching assistants, or other relevant roles. This model is responsible for managing key user information, monitoring interactions with the assignment, keeping track of team memberships, and recording things like badges and review grades.&lt;br /&gt;
&lt;br /&gt;
== Implementation ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1.-decoupling-authorization-logic-from-reviewbidscontroller&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Decoupling Authorization Logic from &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| '''&amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; needs to use the authentication utilities'''&lt;br /&gt;
&lt;br /&gt;
The function &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; should use the available authentication utilities to support the Single Responsibility Principle and Separation of Concerns and to support code reusability.&lt;br /&gt;
&lt;br /&gt;
| '''Authentication Consistency:''' Remove authentication logic in the controller and call logic in the authentication utility class instead to ensure consistent use of the same logic across the application, foster reusability, and eliminate DRY.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
==== Authorization Flow Before Refactoring ====&lt;br /&gt;
The sequence diagram depicts the authorization process within the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;. When a user invokes an action, the controller checks if the action is allowed by verifying the user's role through the &amp;lt;code&amp;gt;current_role_name()&amp;lt;/code&amp;gt; method in the &amp;lt;code&amp;gt;ApplicationController&amp;lt;/code&amp;gt;. Depending on the action and the user's role, the controller either authorizes the user to proceed or denies access, ultimately executing the action logic if authorized and responding to the user.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 1.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We simplified the &amp;lt;code&amp;gt;action_allowed?&amp;lt;/code&amp;gt; method by decoupling authorization logic from the controller to use helper methods in the &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt; module. The &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; should not handle authentication, which is a violation of SRP. &lt;br /&gt;
&lt;br /&gt;
Note: We also made similar updates to the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; methods for &amp;lt;code&amp;gt;SignUpController&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;vertical-align:top;&amp;quot; | Original Code !! style=&amp;quot;vertical-align:top;&amp;quot; | Refactored Code&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index'&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator',&lt;br /&gt;
     'Student'].include? current_role_name and&lt;br /&gt;
        ((%w[list].include? action_name) ? are_needed_authorizations_present?(params[:id], &amp;quot;participant&amp;quot;, &amp;quot;reader&amp;quot;, &amp;quot;submitter&amp;quot;, &amp;quot;reviewer&amp;quot;) : true)&lt;br /&gt;
  else&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator'].include? current_role_name&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index', 'list'&lt;br /&gt;
    current_user_has_student_privileges? &amp;amp;&amp;amp; list_authorization_check&lt;br /&gt;
  else&lt;br /&gt;
    current_user_has_ta_privileges?&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; action ===&lt;br /&gt;
Previously, the &amp;lt;code&amp;gt;ReviewBidsController's index&amp;lt;/code&amp;gt; action was overloaded with responsibilities—it handled everything from locating and validating assignments to fetching review mappings and counting completed reviews. This all-in-one approach made the controller action messy. &lt;br /&gt;
&lt;br /&gt;
We have moved the business logic from the controller action and into dedicated service objects. With this change, we introduced two key service objects: &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; focuses on retrieving the participant along with their assignments, while the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; takes on the task of managing review mappings and calculating review-related stats, like how many reviews have been completed. &lt;br /&gt;
&lt;br /&gt;
This separation of responsibilities aligns with the Single Responsibility Principle. The logic for fetching assignments, retrieving review mappings, and counting reviews is now more flexible and reusable throughout the application. For example, we used methods in the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; in both the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; and the &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;, helping to avoid code duplication.The article https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial served as a guide on when and how to use service objects in Rails.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
  return unless current_user_id?(@participant.user_id)&lt;br /&gt;
  @assignment = @participant.assignment&lt;br /&gt;
  @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
  # Finding how many reviews have been completed&lt;br /&gt;
  @num_reviews_completed = 0&lt;br /&gt;
  @review_mappings.each do |map|&lt;br /&gt;
    @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # render view for completing reviews after review bidding has been completed&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @assignment = participant_service.assignment&lt;br /&gt;
  unless @assignment.is_a?(Assignment)&lt;br /&gt;
    flash[:error] = 'Assignment not found.'&lt;br /&gt;
    redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  @review_mappings = review_service.review_mappings&lt;br /&gt;
  @num_reviews_completed = review_service.review_counts[:completed]&lt;br /&gt;
&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Optimize &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Action functionality ===&lt;br /&gt;
In the earlier version of the set_priority action in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, the server only needed a couple of key pieces of information: an array of topic IDs (params[:topic]) and the participant ID (params[:id}). From there, it would figure out the assignment_id by using the first topic as a reference. &lt;br /&gt;
&lt;br /&gt;
With the new implementation, the action now expects clear and structured parameters, including participant_id, an array of topic IDs, and assignment_id. This change ensures that the server always receives complete and reliable information, preventing issues caused by missing or inconsistent data. It also removes the need for the server to perform unnecessary lookups, allowing the controller to focus on its main job: validating inputs (like making sure the topics in params[:topic] belong to the same assignment as the participant), updating the database, handling errors properly, and sending the correct response back to the view.&lt;br /&gt;
&lt;br /&gt;
While these updates have cleaned up the set_priority action, the action still contains a lot of business logic, like validations and priority updates. To make the code even more maintainable, there’s room for further refactoring—perhaps moving some of this logic into a service class or model method to streamline the controller even more.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | Old Code&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    if params[:topic].nil?&lt;br /&gt;
      ReviewBid.where(participant_id: params[:id]).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      participant = AssignmentParticipant.find_by(id: params[:id])&lt;br /&gt;
      assignment_id = SignUpTopic.find(params[:topic].first).assignment.id&lt;br /&gt;
      @bids = ReviewBid.where(participant_id: params[:id])&lt;br /&gt;
      signed_up_topics = ReviewBid.where(participant_id: params[:id]).map(&amp;amp;:signuptopic_id)&lt;br /&gt;
      signed_up_topics -= params[:topic].map(&amp;amp;:to_i)&lt;br /&gt;
      signed_up_topics.each do |topic|&lt;br /&gt;
        ReviewBid.where(signuptopic_id: topic, participant_id: params[:id]).destroy_all&lt;br /&gt;
      end&lt;br /&gt;
      params[:topic].each_with_index do |topic_id, index|&lt;br /&gt;
        bid_existence = ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id])&lt;br /&gt;
        if bid_existence.empty?&lt;br /&gt;
          ReviewBid.create(priority: index + 1, signuptopic_id: topic_id, participant_id: params[:id], assignment_id: assignment_id)&lt;br /&gt;
        else&lt;br /&gt;
          ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id]).update_all(priority: index + 1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to action: 'show', assignment_id: params[:assignment_id], id: params[:id]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    participant_id = params[:participant_id].to_i&lt;br /&gt;
    selected_topic_ids = params[:topic]&amp;amp;.map(&amp;amp;:to_i) || []&lt;br /&gt;
    assignment_id = params[:assignment_id].to_i&lt;br /&gt;
&lt;br /&gt;
    unless current_user_can_modify_participant?(participant_id)&lt;br /&gt;
      flash[:error] = 'You are not authorized to modify these bids.'&lt;br /&gt;
      respond_to do |format|&lt;br /&gt;
        format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
        format.json { render json: { status: 'unauthorized' }, status: :unauthorized }&lt;br /&gt;
      end&lt;br /&gt;
      return&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    if selected_topic_ids.empty?&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      assignment = Assignment.find_by(id: assignment_id)&lt;br /&gt;
      if assignment.nil?&lt;br /&gt;
        flash[:error] = &amp;quot;Invalid assignment.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_assignment' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      unless SignUpTopic.where(id: selected_topic_ids, assignment_id: assignment_id).count == selected_topic_ids.size&lt;br /&gt;
        flash[:error] = &amp;quot;One or more selected topics are invalid.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_topics' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).where.not(signuptopic_id: selected_topic_ids).destroy_all&lt;br /&gt;
&lt;br /&gt;
      selected_topic_ids.each_with_index do |topic_id, index|&lt;br /&gt;
        bid = ReviewBid.find_or_initialize_by(signuptopic_id: topic_id, participant_id: participant_id)&lt;br /&gt;
        bid.priority = index + 1&lt;br /&gt;
        bid.assignment_id = assignment_id&lt;br /&gt;
        bid.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_to action: 'show', assignment_id: assignment_id, id: participant_id, notice: 'Review bids updated successfully.' }&lt;br /&gt;
      format.json { render json: { status: 'success' }, status: :ok }&lt;br /&gt;
    end&lt;br /&gt;
  rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
    flash[:error] = &amp;quot;Failed to update priorities: #{e.message}&amp;quot;&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
      format.json { render json: { status: 'error', message: e.message }, status: :unprocessable_entity }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Client Side Changes to Support &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Changes ====&lt;br /&gt;
After tidying up the server-side logic, we needed to refactor the client-side code to ensure it passed the correct information from the view to the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action. On the client side, we now grab the topicIDs and send them as &amp;lt;code&amp;gt;selected_topic_ids&amp;lt;/code&amp;gt;, allowing the controller to figure out which topics were selected and in what order.  &lt;br /&gt;
&lt;br /&gt;
We also extracted the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, making it a lot easier to send data with AJAX and keeping things flexible. When working with jQuery, AJAX is awesome because it lets us update the server without reloading the page, making the user experience smooth and responsive. When we pull the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, we can grab it along with other data, like the list of topics, and include it in the request. This way, we don’t have to rely on the URL to know which participant we’re working with, which makes the code cleaner and more adaptable.&lt;br /&gt;
&lt;br /&gt;
If we were to rely on the route (as opposed to using the DOM) for the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt;, every AJAX request would need to be tied to a specific URL structure. That’s limiting, especially if the route doesn’t naturally include the participant ID, as can be the case with collection routes versus member routes in Rails. &lt;br /&gt;
&lt;br /&gt;
Note: One interesting aspect of the application was the mix of JavaScript and CoffeeScript code. It wasn't clear why both types of code were used.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery -&amp;gt;&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: -&amp;gt;&lt;br /&gt;
      $.post($(this).data('update-url'), $(this).sortable('serialize'))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery(document).ready ($) -&amp;gt;&lt;br /&gt;
  sendUpdatedOrder = -&amp;gt;&lt;br /&gt;
    sortedIDs = $(this).sortable('toArray')&lt;br /&gt;
&lt;br /&gt;
    topicIDs = sortedIDs.map (id) -&amp;gt;&lt;br /&gt;
      id.replace 'topic_', ''&lt;br /&gt;
&lt;br /&gt;
    participant_id = $(this).data('participant-id')&lt;br /&gt;
&lt;br /&gt;
    assignment_id = new URL($(this).data('update-url'), window.location.origin).searchParams.get('assignment_id')&lt;br /&gt;
&lt;br /&gt;
    $.ajax&lt;br /&gt;
      type: 'POST'&lt;br /&gt;
      url: $(this).data('update-url')&lt;br /&gt;
      data:&lt;br /&gt;
        topic: topicIDs&lt;br /&gt;
        participant_id: participant_id&lt;br /&gt;
        assignment_id: assignment_id&lt;br /&gt;
      success: (response) -&amp;gt;&lt;br /&gt;
        console.log 'Selections updated successfully.'&lt;br /&gt;
      error: (xhr, status, error) -&amp;gt;&lt;br /&gt;
        alert 'An error occurred while saving your selections. Please try again.'&lt;br /&gt;
        console.error error&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: sendUpdatedOrder&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-1.-signupsheetcontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; Updates ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; method renders the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; action in &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt;. However, the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; does not include the controller action for this view. We need to find the old implementation of the controller action, add it back, and review and update it for any DRY violations.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # provides variables for reviewing page located at views/review_bids/others_work.html.erb&lt;br /&gt;
  def index&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
    return unless current_user_id?(@participant.user_id)&lt;br /&gt;
&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
    # Finding how many reviews have been completed&lt;br /&gt;
    @num_reviews_completed = 0&lt;br /&gt;
    @review_mappings.each do |map|&lt;br /&gt;
      @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # render view for completing reviews after review bidding has been completed&lt;br /&gt;
    render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
  end  &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-2.-reviewbidsotherswork-view-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;review_bids_others_work.html.erb&amp;lt;/code&amp;gt; View Updates ====&lt;br /&gt;
&lt;br /&gt;
The code only contains a view with this name and no associated controller action. Reviewing the code in the view, we noticed some SRP violations where there is much logic in the presentation layer. Set up helper methods in the controller to handle some of that logic or see if the model can handle some of that logic.&lt;br /&gt;
&lt;br /&gt;
Note: The view is technically part of the SignUpSheetController, but it's being rendered by the ReviewBidsController, which initially threw us off. After chatting with the professor, we decided not to change this view. He indicated that the plan is to reuse these views for both assignment bidding and review bidding.&lt;br /&gt;
&lt;br /&gt;
We would suggest building the assignment bidding and review bidding features separately first to make sure each one works on its own. Once that’s done, we can go back and clean things up by combining any shared functionality. This could include creating shared views or even introducing an abstract model to handle both types of bidding in a cleaner way. It could also mean replacing the ReviewBidsController with something more generic, like a BidsController, to keep things reusable and easier to maintain down the line.&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Rename &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; method ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; should be &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt;&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:''' Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The primary activity here is to refactor the method name &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; and fix the hardcoded URL. However, the match topic bidding algorithm no longer works since Heroku is no longer free, per the professor. The reference link below has detailed information about the web service that we can leverage to gain that understanding.&lt;br /&gt;
&lt;br /&gt;
Reference Link for Webservice Details: [https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Updated Implementation ====&lt;br /&gt;
The original requirement was to rename the run_bidding_algorithm method to assign_reviewers, but after digging into the code, we realized that renaming it didn’t make sense. The method runs the bidding algorithm to match topics, and there’s already a separate method to handle assigning review topics.&lt;br /&gt;
&lt;br /&gt;
However, we improved the logic in the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; method (which calls the private &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt;) by introducing a separate &amp;lt;code&amp;gt;BiddingAlgorithmService&amp;lt;/code&amp;gt; object. This keeps the business logic out of the controller action, which makes things cleaner and more maintainable.&lt;br /&gt;
&lt;br /&gt;
==== review_bids_controller ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def run_bidding_algorithm&lt;br /&gt;
    review_bid = ReviewBid.new&lt;br /&gt;
    bidding_data = review_bid.bidding_data&lt;br /&gt;
    matched_topics = BiddingAlgorithmService.new(bidding_data).run&lt;br /&gt;
    raise ArgumentError, 'Failed to assign reviewers. Please try again later.' unless matched_topics&lt;br /&gt;
&lt;br /&gt;
    matched_topics&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== bidding_algorithm_service ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# frozen_string_literal: true&lt;br /&gt;
&lt;br /&gt;
# The `BiddingAlgorithmService` run the bid assignment algorithm&lt;br /&gt;
# Sends student IDs, topic IDs, student preferences, and timestamps to the web service&lt;br /&gt;
# The web service returns the matched assignments in the JSON response body&lt;br /&gt;
class BiddingAlgorithmService&lt;br /&gt;
  SERVICE_URL = 'http://app-csc517.herokuapp.com/match_topics'.freeze&lt;br /&gt;
&lt;br /&gt;
  # MOCK_DATA is based on the documentation detailing how the webservice behaves.&lt;br /&gt;
  # Reference: https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review#Webservice&lt;br /&gt;
  MOCK_DATA = {&lt;br /&gt;
    36239 =&amp;gt; [3970, 3972, 3975],&lt;br /&gt;
    36240 =&amp;gt; [3973, 3974, 3972],&lt;br /&gt;
    36241 =&amp;gt; [3969, 3971, 3972],&lt;br /&gt;
    36242 =&amp;gt; [3969, 3971, 3973],&lt;br /&gt;
    36243 =&amp;gt; [3969, 3970, 3971]&lt;br /&gt;
  }.freeze&lt;br /&gt;
&lt;br /&gt;
  def initialize(bidding_data)&lt;br /&gt;
    @bidding_data = bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def run&lt;br /&gt;
    return MOCK_DATA if Rails.application.config.use_mock_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
    perform_request&lt;br /&gt;
  rescue RestClient::ExceptionWithResponse, JSON::ParserError&lt;br /&gt;
    false&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def perform_request&lt;br /&gt;
    response = RestClient.post(&lt;br /&gt;
      SERVICE_URL,&lt;br /&gt;
      @bidding_data.to_json,&lt;br /&gt;
      content_type: :json,&lt;br /&gt;
      accept: :json&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    validate_response(response)&lt;br /&gt;
    JSON.parse(response.body)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def validate_response(response)&lt;br /&gt;
    raise 'Invalid response format' unless response.headers[:content_type] &amp;amp;&amp;amp; response.headers[:content_type].include?('application/json')&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Resolve Functionality Issues for Non Bidders ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Currently it doesn't work if some student does not bid. In this case, algorithm needs to be fixed to ignore anyone who didn’t bid, and let them choose from what’s left over.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to do several things within this controller. We will set a default state for non-bidders that we can point to to give them options to select topics to review from the leftovers. We will consolidate &amp;lt;code&amp;gt;@sign_up_topics&amp;lt;/code&amp;gt; to resolve the DRY violation. Lastly, we will remove business logic from the show method and push it to a new helper to conform to the SRP.&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # Additional method&lt;br /&gt;
&lt;br /&gt;
  # Provides variables for review bidding page&lt;br /&gt;
  def show&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id].to_i)&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @sign_up_topics = SignUpTopic.where(assignment_id: @assignment.id, private_to: nil)&lt;br /&gt;
    my_topic = SignedUpTeam.topic_id(@participant.parent_id, @participant.user_id)&lt;br /&gt;
    @sign_up_topics -= SignUpTopic.where(assignment_id: @assignment.id, id: my_topic)&lt;br /&gt;
    @num_participants = AssignmentParticipant.where(parent_id: @assignment.id).count&lt;br /&gt;
    @selected_topics = nil # This is used to list the topics assigned to review (i.e., select == assigned)&lt;br /&gt;
    @bids = ReviewBid.where(participant_id: @participant, assignment_id: @assignment.id)&lt;br /&gt;
    signed_up_topics = []&lt;br /&gt;
    &lt;br /&gt;
    @bids.each do |bid|&lt;br /&gt;
      sign_up_topic = SignUpTopic.find_by(id: bid.signuptopic_id)&lt;br /&gt;
      signed_up_topics &amp;lt;&amp;lt; sign_up_topic if sign_up_topic&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    signed_up_topics &amp;amp;= @sign_up_topics&lt;br /&gt;
    @sign_up_topics -= signed_up_topics&lt;br /&gt;
    @bids = signed_up_topics&lt;br /&gt;
    @num_of_topics = @sign_up_topics.size&lt;br /&gt;
    @assigned_review_maps = []&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: @assignment.id, reviewer_id: @participant.id).each do |review_map|&lt;br /&gt;
      @assigned_review_maps &amp;lt;&amp;lt; review_map&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Explicitly render view since it's in the sign up sheet views&lt;br /&gt;
    render 'sign_up_sheet/review_bids_show'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Additional methods&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-2.-reviewbidshelper-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
 # Assigns bidding topics to reviewers&lt;br /&gt;
  def assign_bidding&lt;br /&gt;
      assignment = validate_assignment(params[:assignment_id])&lt;br /&gt;
&lt;br /&gt;
      reviewers = validate_reviewers(assignment.id)&lt;br /&gt;
      reviewer_ids = reviewers.map(&amp;amp;:id)&lt;br /&gt;
&lt;br /&gt;
      matched_topics = run_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
      if matched_topics.blank?&lt;br /&gt;
        flash[:alert] = 'Topic or assignment is missing'&lt;br /&gt;
        redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      leftover_topics = find_leftover_topics(assignment.id, matched_topics)&lt;br /&gt;
      assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.new.assign_review_topics(matched_topics)&lt;br /&gt;
      assignment.update!(can_choose_topic_to_review: false)&lt;br /&gt;
&lt;br /&gt;
      flash[:notice] = 'Reviewers were successfully assigned to topics.'&lt;br /&gt;
      redirect_back fallback_location: root_path&lt;br /&gt;
    rescue ArgumentError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ArgumentError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: e.message&lt;br /&gt;
    rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::RecordInvalid: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue ActiveRecord::ActiveRecordError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::ActiveRecordError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue StandardError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;StandardError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers. Please try again later.'&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  #service methods here ...&lt;br /&gt;
&lt;br /&gt;
  def find_leftover_topics(assignment_id, matched_topics)&lt;br /&gt;
    all_topic_ids = SignUpTopic.where(assignment_id: assignment_id).pluck(:id)&lt;br /&gt;
    assigned_topic_ids = matched_topics.map { |match| match[:topic_id] }&lt;br /&gt;
&lt;br /&gt;
    # Calculate leftover topics&lt;br /&gt;
    all_topic_ids - assigned_topic_ids&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
    return if leftover_topics.blank?&lt;br /&gt;
&lt;br /&gt;
    # Find non-bidders by excluding already matched reviewers&lt;br /&gt;
    non_bidders = reviewer_ids - matched_topics.keys&lt;br /&gt;
&lt;br /&gt;
    # Assign leftover topics to non-bidders in a round-robin fashion&lt;br /&gt;
    non_bidders.each_with_index do |reviewer_id, index|&lt;br /&gt;
      topic_id = leftover_topics[index % leftover_topics.length]&lt;br /&gt;
      ReviewBid.create(priority: 1, signuptopic_id: topic_id, participant_id: reviewer_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirm Functionality for Single-person Teams ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Make sure your code works on individual assignments, i.e., assignments where participants sign up for topics instead of teams. In this case, a team is created for each participant when they sign up. So the code should work for assignments to which either individuals or teams submit.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to make an update to focus on topic rather than team or have it where teams are updated based on topic id.We will probably utilize sign up topic or a similar object to accomplish this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-5-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bid Status Messaging ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| To make the functionality more intuitive, include a message to say how many students are eligible to submit bids, how many have submitted their bids, when the deadline for submitting bids is.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
The logic for these messages will go into the helper file. It is effectively the same reasoning as our migration of other business/functionality code from the controller to the helper to conform to the SRP. It will go hand in hand with the SignUpTopic portion of the code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-6-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Why are the methods in review_bid.rb class methods? Can we change them to instance methods or move it to helpers?&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-1.-reviewbid-model-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model Updates ====&lt;br /&gt;
Since the current class methods are tied to &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model instances, we will change them to instance methods. There is no need to move them to helpers.&lt;br /&gt;
&lt;br /&gt;
===== Review Bid Model Flow Before Refactoring =====&lt;br /&gt;
This sequence diagram describes how the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; interacts with the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model in assigning review topics to reviewers according to their bids. Once the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; action is initiated by the user, the controller gathers the IDs of reviewers and calls &amp;lt;code&amp;gt;ReviewBid.bidding_data&amp;lt;/code&amp;gt; to gather bidding information. That is sent out to an external bidding algorithm, and the matched topics that come back are used by &amp;lt;code&amp;gt;ReviewBid.assign_review_topics&amp;lt;/code&amp;gt; to create &amp;lt;code&amp;gt;ReviewResponseMap&amp;lt;/code&amp;gt; entries, assigning topics to reviewers for the assignment.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
  belongs_to :assignment, class_name: 'Assignment'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  def self.bidding_data(assignment_id, reviewer_ids)&lt;br /&gt;
    # create basic hash and set basic hash data&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'users' =&amp;gt; {}, 'max_accepted_proposals' =&amp;gt; [] }&lt;br /&gt;
    bidding_data['tid'] = SignUpTopic.where(assignment_id: assignment_id).ids&lt;br /&gt;
    bidding_data['max_accepted_proposals'] = Assignment.where(id: assignment_id).pluck(:num_reviews_allowed).first&lt;br /&gt;
&lt;br /&gt;
    # loop through reviewer_ids to get reviewer specific bidding data&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      bidding_data['users'][reviewer_id] = reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # assigns topics to reviews as matched by the webservice algorithm&lt;br /&gt;
  def self.assign_review_topics(assignment_id, reviewer_ids, matched_topics, _min_num_reviews = 2)&lt;br /&gt;
    # if review response map already created, delete it&lt;br /&gt;
    if ReviewResponseMap.where(reviewed_object_id: assignment_id)&lt;br /&gt;
      ReviewResponseMap.where(reviewed_object_id: assignment_id).destroy_all&lt;br /&gt;
    end&lt;br /&gt;
    # loop through reviewer_ids to assign reviews to each reviewer&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      topics_to_assign = matched_topics[reviewer_id.to_s]&lt;br /&gt;
      topics_to_assign.each do |topic|&lt;br /&gt;
        assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method to assign a single topic to a reviewer&lt;br /&gt;
  def self.assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic).pluck(:team_id).first&lt;br /&gt;
    team_to_review.nil? ? [] : ReviewResponseMap.create(reviewed_object_id: assignment_id, reviewer_id: reviewer_id, reviewee_id: team_to_review, type: 'ReviewResponseMap')&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method for getting individual reviewer_ids bidding data&lt;br /&gt;
  # returns user's bidding data hash&lt;br /&gt;
  def self.reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    reviewer_user_id = AssignmentParticipant.find(reviewer_id).user_id&lt;br /&gt;
    self_topic = SignedUpTeam.topic_id(assignment_id, reviewer_user_id)&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'otid' =&amp;gt; self_topic, 'priority' =&amp;gt; [], 'time' =&amp;gt; [] }&lt;br /&gt;
    bids = ReviewBid.where(participant_id: reviewer_id)&lt;br /&gt;
&lt;br /&gt;
    # loop through each bid for a topic to get specific data&lt;br /&gt;
    bids.each do |bid|&lt;br /&gt;
      bidding_data['tid'] &amp;lt;&amp;lt; bid.signuptopic_id&lt;br /&gt;
      bidding_data['priority'] &amp;lt;&amp;lt; bid.priority&lt;br /&gt;
      bidding_data['time'] &amp;lt;&amp;lt; bid.updated_at&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  # Instance method to get bidding data for this review bid&lt;br /&gt;
  def bidding_data&lt;br /&gt;
    {&lt;br /&gt;
      'tid' =&amp;gt; topic_ids,&lt;br /&gt;
      'otid' =&amp;gt; self_topic,&lt;br /&gt;
      'priority' =&amp;gt; priorities,&lt;br /&gt;
      'time' =&amp;gt; updated_times&lt;br /&gt;
    }&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # bid object to limit db touches for similar data acquisitions&lt;br /&gt;
  def bids&lt;br /&gt;
    @bids ||= ReviewBid.where(participant_id: participant_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def topic_ids&lt;br /&gt;
    bids.pluck(:signuptopic_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def priorities&lt;br /&gt;
    bids.pluck(:priority)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def updated_times&lt;br /&gt;
    bids.pluck(:updated_at)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self_topic&lt;br /&gt;
    return nil unless topic &amp;amp;&amp;amp; topic.assignment_id&lt;br /&gt;
&lt;br /&gt;
    SignedUpTeam.topic_id(topic.assignment_id, participant.user_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign topics to reviews&lt;br /&gt;
  def assign_review_topics(matched_topics)&lt;br /&gt;
    validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: topic.assignment_id).destroy_all&lt;br /&gt;
&lt;br /&gt;
    matched_topics.each do |reviewer_id, topics|&lt;br /&gt;
      Array(topics).each { |topic_id| assign_topic_to_reviewer(reviewer_id, topic_id) }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign a single topic to a reviewer&lt;br /&gt;
  def assign_topic_to_reviewer(reviewer_id, topic_id)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic_id).pluck(:team_id).first&lt;br /&gt;
    return if team_to_review.nil?&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.create(&lt;br /&gt;
      reviewed_object_id: topic.assignment_id,&lt;br /&gt;
      reviewer_id: reviewer_id,&lt;br /&gt;
      reviewee_id: team_to_review,&lt;br /&gt;
      type: 'ReviewResponseMap'&lt;br /&gt;
    )&lt;br /&gt;
    # error handling&lt;br /&gt;
  rescue StandardError =&amp;gt; e&lt;br /&gt;
    Rails.logger.error(&amp;quot;Failed to assign topic #{topic_id} to reviewer #{reviewer_id}: #{e.message}&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
    raise ArgumentError, 'Topic or assignment is missing' if topic.nil? || topic.assignment_id.nil?&lt;br /&gt;
    raise ArgumentError, 'Matched topics must be a Hash' unless matched_topics.is_a?(Hash)&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
Once we change them to instance methods, we’ll need to modify their use in the code to instantiate the object first.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;Placeholder for implementation screenshots (before and after)&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-3.-schema.db-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;Schema.db&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We also need to remove the foreign key reference to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model since the &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model already has access to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;, and that is the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; we should be referencing. This will also involve modifying the database to remove the foreign key reference to the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Steps required:&lt;br /&gt;
&lt;br /&gt;
# Review the records in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table in the database to see if there are any records and if those records have values for &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;&lt;br /&gt;
# Create a migration file using the command: &amp;lt;code&amp;gt;rails generate migration RemoveAssignmentFromReviewBids&amp;lt;/code&amp;gt;&lt;br /&gt;
# In the generated file, indicate the change that needs to be made using the change method and migration columns &amp;lt;code&amp;gt;remove_foreign_key&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;remove_column&amp;lt;/code&amp;gt;. Migration should be straightforward since there is no cascading behavior (i.e., &amp;lt;code&amp;gt;dependent:destroy&amp;lt;/code&amp;gt;) to worry about.&lt;br /&gt;
# Once comfortable with the updates to the generated file, run the migration using the &amp;lt;code&amp;gt;rails db:migrate&amp;lt;/code&amp;gt; command.&lt;br /&gt;
# Run a query in the DB to ensure no orphaned records are in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table after the updates.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;Placeholder for implementation screenshots (before and after)&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-4.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implement Edge Case Testing ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| In the previous implementation wiki, there are edge cases which are not exhaustively tested. Should test those edge cases thoroughly and add more edge case testing - link&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span class=&amp;quot;mark&amp;quot;&amp;gt;TODO&amp;lt;/span&amp;gt;:We just got initial test results back. We still need to figure out why tests are failing and what may be slipping through the cracks.[[File:Test expertiza.png|411x459px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-8-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design Patterns and Principles ===&lt;br /&gt;
Below are the design patterns and principles we plan to use to address the problem statements outlined in the project requirements:&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
** Controller Responsibility&lt;br /&gt;
*** To address the issue of authentication logic residing in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, we will refactor the code to ensure the controller solely handles request and response flows.&lt;br /&gt;
*** Authentication logic will be moved to dedicated methods in &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt;, making the codebase easier to maintain and test.&lt;br /&gt;
** View Simplification&lt;br /&gt;
*** Currently, the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; view contains excessive business logic, violating SRP.&lt;br /&gt;
*** We will move this logic into helper methods or models, ensuring each component has a single responsibility and improving code readability.&lt;br /&gt;
&lt;br /&gt;
* Separation of Concerns (SoC)&lt;br /&gt;
** By relocating authorization logic out of the controller, we separate authentication from request handling.&lt;br /&gt;
** Adhering to SoC enhances readability and maintainability by allowing each part of the application to focus on its specific role.&lt;br /&gt;
* Don't Repeat Yourself (DRY)&lt;br /&gt;
** We will inspect the existing implementation for code duplication, particularly in the controller actions.&lt;br /&gt;
** Any repetitive code will be refactored or removed to reduce redundancy and improve clarity.&lt;br /&gt;
&lt;br /&gt;
* Model-View-Controller (MVC) Adherence&lt;br /&gt;
** Moving business logic from the view aligns with MVC best practices.&lt;br /&gt;
** By ensuring the view only presents data without processing it, we maintain clear boundaries between the model, view, and controller layers.&lt;br /&gt;
&lt;br /&gt;
* Dependency Inversion Principle (DIP)&lt;br /&gt;
** We plan to update class methods to instance methods, allowing the controller to depend on abstractions rather than concrete implementations.&lt;br /&gt;
** This change leverages Ruby's duck typing, providing flexibility and making the code easier to maintain and test.&lt;br /&gt;
&lt;br /&gt;
* Factory Pattern&lt;br /&gt;
** We will implement a factory pattern for the common processing of &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpBid&amp;lt;/code&amp;gt;, encapsulating object creation and promoting code reuse.&lt;br /&gt;
&lt;br /&gt;
* Law of Demeter (Principle of Least Knowledge)&lt;br /&gt;
** We'll delegate interactions appropriately to prevent tightly coupled code, especially when dealing with topics and teams.&lt;br /&gt;
** Clear and intentional naming during refactoring will enhance code clarity and maintainability.&lt;br /&gt;
&lt;br /&gt;
* Adapter Pattern&lt;br /&gt;
** To add messaging to bid states for users, we'll use the adapter pattern to allow different components to work together without modifying their existing code.&lt;br /&gt;
&lt;br /&gt;
== Files Modified/Added ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-1.-controllers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
review_bids_controller.rb&lt;br /&gt;
&lt;br /&gt;
signup_sheet_controller.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-2.-models&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
review_bid.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-3.-helpers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
&lt;br /&gt;
authorization_helper.rb&lt;br /&gt;
&lt;br /&gt;
Review_bids_helper.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;viii.-test-plan&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
The Excel spreadsheet below details our test plan for testing the code we changed or created to ensure code coverage. The process involved reviewing the code and determining all the possible scenarios that needed to be covered based on what the method was doing, then creating test cases accordingly.&lt;br /&gt;
&lt;br /&gt;
* [https://docs.google.com/spreadsheets/d/1iHzOdSwlTtxOtf5VsNdwOaCP8NzYMmiJ/edit?usp=drive_link Open Test Matrix Spreadsheet]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/review_bids_controller_spec.rb ReviewBidsController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/sign_up_sheet_controller_spec.rb SignUpSheetController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/models/review_bid_spec.rb ReviewBid model updated test cases]&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-1.-mentor&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Ed Gehringer&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-2.-members&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Members ===&lt;br /&gt;
&lt;br /&gt;
* Gavin Teague&lt;br /&gt;
* Janice Uwujaren&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xi.-links&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
Previous Wiki: [https://wiki.expertiza.ncsu.edu/index.php/CSC/ECE_517_Fall_2021_-_E2151._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;Link&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xii.-references&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/document/d/1LHVacylQmB15I6VcYUA5nuC7GRRU9szHzZhjlPcE-PA/edit?tab=t.0#heading=h.u0oycoj6e0ev &amp;lt;u&amp;gt;Project Instructions&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;appendix-a-uml-class-diagram-plantuml-code-for-review-bids&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Juwujar</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160977</id>
		<title>CSC/ECE 517 Fall 2024 - E2485. Allow reviewers to bid on what to review</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160977"/>
		<updated>2024-12-10T23:10:47Z</updated>

		<summary type="html">&lt;p&gt;Juwujar: /* SignUpSheetController Updates */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
This project will fix the defects and improve the functionality in Expertiza of the Reviewer Bidding feature, a process where reviewers can bid for assignments they want to review. It will make the functionality smoother, more reliable, and align with Expertiza's collaborative learning objectives.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iii.-problem-statements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Problem Statements ==&lt;br /&gt;
&lt;br /&gt;
Assigning reviews to users on Expertiza involves a complex bidding algorithm that requires optimization before production deployment. Below are key areas we plan to address as per the project requirements:&lt;br /&gt;
* Code Refactoring and Best Practices&lt;br /&gt;
** '''Utilize Authentication Utilities''': Modify the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; function in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to use existing authentication utilities, enhancing code reusability and adhering to the Single Responsibility Principle (SRP) and Separation of Concerns (SoC).&lt;br /&gt;
** '''Eliminate Code Duplication''': Refactor &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; to comply with the Don't Repeat Yourself (DRY) principle.&lt;br /&gt;
** '''Rename Functions for Clarity''': Change &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; to better reflect its purpose.&lt;br /&gt;
** '''Refactor Methods in '''&amp;lt;code&amp;gt;review_bid.rb&amp;lt;/code&amp;gt;''':''': Convert class methods to instance methods or relocate them to helper classes for improved code organization.&lt;br /&gt;
* Functionality Enhancements&lt;br /&gt;
** '''Handle Missing Bids Gracefully''': Adjust the bidding algorithm to accommodate students who do not bid, allowing them to select from the remaining options.&lt;br /&gt;
** '''Support Both Individual and Team Assignments''': Ensure the code functions correctly for participant and team-based assignments when signing up for topics.&lt;br /&gt;
* User Interface Improvements&lt;br /&gt;
** '''Increase Transparency in the Bidding Process''': Display messages indicating how many students are eligible to bid, how many have submitted bids, and the deadline.&lt;br /&gt;
** '''Accurate Bid Status Indicators''': Ensure topic colors change correctly based on the number of outstanding bids, providing clear visual feedback to users.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Exhaustive Edge Case Testing''': Conduct thorough testing for edge cases not previously covered, adding additional test cases as needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iv.-design-goals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Design Goals ==&lt;br /&gt;
&lt;br /&gt;
While addressing the existing defects and enhancing the Reviewer Bidding feature, we plan to adhere to the following design guidelines:&lt;br /&gt;
&lt;br /&gt;
* Code Quality and Maintainability&lt;br /&gt;
** '''Improve Code Structure: '''Implement refactoring strategies to enhance code readability and maintainability, ensuring adherence to best practices like the DRY principle.&lt;br /&gt;
** '''Standardize Authentication Logic: '''Utilize a centralized authentication utility to promote consistency and reusability across the application.&lt;br /&gt;
* System Stability and Flexibility&lt;br /&gt;
** '''Enhance Algorithm Robustness: '''Modify the bidding algorithm to handle scenarios where students do not bid, ensuring the system remains stable under all conditions.&lt;br /&gt;
** '''Ensure Assignment Versatility: '''Design the system to seamlessly support both individual and team assignments without additional configuration.&lt;br /&gt;
* User Experience Enhancements&lt;br /&gt;
** '''Increase Bidding Transparency: '''Develop user interface elements that clearly display bidding status, deadlines, and participation metrics.&lt;br /&gt;
** '''Implement Dynamic Visual Feedback: '''Use real-time visual cues, such as color changes, to inform users about the bidding status instantly.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Develop Comprehensive Test Suites: '''Create extensive automated tests covering edge cases and critical functionalities to guarantee reliable system behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;v.-uml-diagram&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== UML Diagram ==&lt;br /&gt;
&lt;br /&gt;
Below is a visual representation of the models associated with the review bids functionality, depicting the data structures and their relationships.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml class diagram2.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi.-implementation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
'''ReviewBid: '''The &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; manages the bidding process, where participants indicate their preferences for reviewing different topics or assignments. This helps distribute review tasks fairly based on participants' interests.&lt;br /&gt;
&lt;br /&gt;
'''Assignment: '''The &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model represents team or individual assignments in Expertiza. It stores all the essential information required to keep the assignment process organized, like managing participants, setting due dates, forming teams, and overseeing the review process.&lt;br /&gt;
&lt;br /&gt;
'''SignUpTopic: '''The &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt; model identifies specific topics or areas within an assignment for which participants can sign up. It manages the availability of these topics, tracks bids, coordinates team sign-ups, and assigns topics based on both bids and participant availability. &lt;br /&gt;
&lt;br /&gt;
'''Participant: '''The &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model is about the individual users who participate in an assignment. This includes students, instructors, teaching assistants, or other relevant roles. This model is responsible for managing key user information, monitoring interactions with the assignment, keeping track of team memberships, and recording things like badges and review grades.&lt;br /&gt;
&lt;br /&gt;
== Implementation ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1.-decoupling-authorization-logic-from-reviewbidscontroller&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Decoupling Authorization Logic from &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| '''&amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; needs to use the authentication utilities'''&lt;br /&gt;
&lt;br /&gt;
The function &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; should use the available authentication utilities to support the Single Responsibility Principle and Separation of Concerns and to support code reusability.&lt;br /&gt;
&lt;br /&gt;
| '''Authentication Consistency:''' Remove authentication logic in the controller and call logic in the authentication utility class instead to ensure consistent use of the same logic across the application, foster reusability, and eliminate DRY.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
==== Authorization Flow Before Refactoring ====&lt;br /&gt;
The sequence diagram depicts the authorization process within the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;. When a user invokes an action, the controller checks if the action is allowed by verifying the user's role through the &amp;lt;code&amp;gt;current_role_name()&amp;lt;/code&amp;gt; method in the &amp;lt;code&amp;gt;ApplicationController&amp;lt;/code&amp;gt;. Depending on the action and the user's role, the controller either authorizes the user to proceed or denies access, ultimately executing the action logic if authorized and responding to the user.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 1.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We simplified the &amp;lt;code&amp;gt;action_allowed?&amp;lt;/code&amp;gt; method by decoupling authorization logic from the controller to use helper methods in the &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt; module. The &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; should not handle authentication, which is a violation of SRP. &lt;br /&gt;
&lt;br /&gt;
Note: We also made similar updates to the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; methods for &amp;lt;code&amp;gt;SignUpController&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;vertical-align:top;&amp;quot; | Original Code !! style=&amp;quot;vertical-align:top;&amp;quot; | Refactored Code&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index'&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator',&lt;br /&gt;
     'Student'].include? current_role_name and&lt;br /&gt;
        ((%w[list].include? action_name) ? are_needed_authorizations_present?(params[:id], &amp;quot;participant&amp;quot;, &amp;quot;reader&amp;quot;, &amp;quot;submitter&amp;quot;, &amp;quot;reviewer&amp;quot;) : true)&lt;br /&gt;
  else&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator'].include? current_role_name&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index', 'list'&lt;br /&gt;
    current_user_has_student_privileges? &amp;amp;&amp;amp; list_authorization_check&lt;br /&gt;
  else&lt;br /&gt;
    current_user_has_ta_privileges?&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; action ===&lt;br /&gt;
Previously, the &amp;lt;code&amp;gt;ReviewBidsController's index&amp;lt;/code&amp;gt; action was overloaded with responsibilities—it handled everything from locating and validating assignments to fetching review mappings and counting completed reviews. This all-in-one approach made the controller action messy. &lt;br /&gt;
&lt;br /&gt;
We have moved the business logic from the controller action and into dedicated service objects. With this change, we introduced two key service objects: &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; focuses on retrieving the participant along with their assignments, while the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; takes on the task of managing review mappings and calculating review-related stats, like how many reviews have been completed. &lt;br /&gt;
&lt;br /&gt;
This separation of responsibilities aligns with the Single Responsibility Principle. The logic for fetching assignments, retrieving review mappings, and counting reviews is now more flexible and reusable throughout the application. For example, we used methods in the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; in both the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; and the &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;, helping to avoid code duplication.The article https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial served as a guide on when and how to use service objects in Rails.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
  return unless current_user_id?(@participant.user_id)&lt;br /&gt;
  @assignment = @participant.assignment&lt;br /&gt;
  @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
  # Finding how many reviews have been completed&lt;br /&gt;
  @num_reviews_completed = 0&lt;br /&gt;
  @review_mappings.each do |map|&lt;br /&gt;
    @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # render view for completing reviews after review bidding has been completed&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @assignment = participant_service.assignment&lt;br /&gt;
  unless @assignment.is_a?(Assignment)&lt;br /&gt;
    flash[:error] = 'Assignment not found.'&lt;br /&gt;
    redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  @review_mappings = review_service.review_mappings&lt;br /&gt;
  @num_reviews_completed = review_service.review_counts[:completed]&lt;br /&gt;
&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Optimize &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Action functionality ===&lt;br /&gt;
In the earlier version of the set_priority action in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, the server only needed a couple of key pieces of information: an array of topic IDs (params[:topic]) and the participant ID (params[:id}). From there, it would figure out the assignment_id by using the first topic as a reference. &lt;br /&gt;
&lt;br /&gt;
With the new implementation, the action now expects clear and structured parameters, including participant_id, an array of topic IDs, and assignment_id. This change ensures that the server always receives complete and reliable information, preventing issues caused by missing or inconsistent data. It also removes the need for the server to perform unnecessary lookups, allowing the controller to focus on its main job: validating inputs (like making sure the topics in params[:topic] belong to the same assignment as the participant), updating the database, handling errors properly, and sending the correct response back to the view.&lt;br /&gt;
&lt;br /&gt;
While these updates have cleaned up the set_priority action, the action still contains a lot of business logic, like validations and priority updates. To make the code even more maintainable, there’s room for further refactoring—perhaps moving some of this logic into a service class or model method to streamline the controller even more.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | Old Code&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    if params[:topic].nil?&lt;br /&gt;
      ReviewBid.where(participant_id: params[:id]).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      participant = AssignmentParticipant.find_by(id: params[:id])&lt;br /&gt;
      assignment_id = SignUpTopic.find(params[:topic].first).assignment.id&lt;br /&gt;
      @bids = ReviewBid.where(participant_id: params[:id])&lt;br /&gt;
      signed_up_topics = ReviewBid.where(participant_id: params[:id]).map(&amp;amp;:signuptopic_id)&lt;br /&gt;
      signed_up_topics -= params[:topic].map(&amp;amp;:to_i)&lt;br /&gt;
      signed_up_topics.each do |topic|&lt;br /&gt;
        ReviewBid.where(signuptopic_id: topic, participant_id: params[:id]).destroy_all&lt;br /&gt;
      end&lt;br /&gt;
      params[:topic].each_with_index do |topic_id, index|&lt;br /&gt;
        bid_existence = ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id])&lt;br /&gt;
        if bid_existence.empty?&lt;br /&gt;
          ReviewBid.create(priority: index + 1, signuptopic_id: topic_id, participant_id: params[:id], assignment_id: assignment_id)&lt;br /&gt;
        else&lt;br /&gt;
          ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id]).update_all(priority: index + 1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to action: 'show', assignment_id: params[:assignment_id], id: params[:id]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    participant_id = params[:participant_id].to_i&lt;br /&gt;
    selected_topic_ids = params[:topic]&amp;amp;.map(&amp;amp;:to_i) || []&lt;br /&gt;
    assignment_id = params[:assignment_id].to_i&lt;br /&gt;
&lt;br /&gt;
    unless current_user_can_modify_participant?(participant_id)&lt;br /&gt;
      flash[:error] = 'You are not authorized to modify these bids.'&lt;br /&gt;
      respond_to do |format|&lt;br /&gt;
        format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
        format.json { render json: { status: 'unauthorized' }, status: :unauthorized }&lt;br /&gt;
      end&lt;br /&gt;
      return&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    if selected_topic_ids.empty?&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      assignment = Assignment.find_by(id: assignment_id)&lt;br /&gt;
      if assignment.nil?&lt;br /&gt;
        flash[:error] = &amp;quot;Invalid assignment.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_assignment' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      unless SignUpTopic.where(id: selected_topic_ids, assignment_id: assignment_id).count == selected_topic_ids.size&lt;br /&gt;
        flash[:error] = &amp;quot;One or more selected topics are invalid.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_topics' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).where.not(signuptopic_id: selected_topic_ids).destroy_all&lt;br /&gt;
&lt;br /&gt;
      selected_topic_ids.each_with_index do |topic_id, index|&lt;br /&gt;
        bid = ReviewBid.find_or_initialize_by(signuptopic_id: topic_id, participant_id: participant_id)&lt;br /&gt;
        bid.priority = index + 1&lt;br /&gt;
        bid.assignment_id = assignment_id&lt;br /&gt;
        bid.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_to action: 'show', assignment_id: assignment_id, id: participant_id, notice: 'Review bids updated successfully.' }&lt;br /&gt;
      format.json { render json: { status: 'success' }, status: :ok }&lt;br /&gt;
    end&lt;br /&gt;
  rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
    flash[:error] = &amp;quot;Failed to update priorities: #{e.message}&amp;quot;&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
      format.json { render json: { status: 'error', message: e.message }, status: :unprocessable_entity }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Client Side Changes to Support &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Changes ====&lt;br /&gt;
After tidying up the server-side logic, we needed to refactor the client-side code to ensure it passed the correct information from the view to the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action. On the client side, we now grab the topicIDs and send them as &amp;lt;code&amp;gt;selected_topic_ids&amp;lt;/code&amp;gt;, allowing the controller to figure out which topics were selected and in what order.  &lt;br /&gt;
&lt;br /&gt;
We also extracted the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, making it a lot easier to send data with AJAX and keeping things flexible. When working with jQuery, AJAX is awesome because it lets us update the server without reloading the page, making the user experience smooth and responsive. When we pull the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, we can grab it along with other data, like the list of topics, and include it in the request. This way, we don’t have to rely on the URL to know which participant we’re working with, which makes the code cleaner and more adaptable.&lt;br /&gt;
&lt;br /&gt;
If we were to rely on the route (as opposed to using the DOM) for the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt;, every AJAX request would need to be tied to a specific URL structure. That’s limiting, especially if the route doesn’t naturally include the participant ID, as can be the case with collection routes versus member routes in Rails. &lt;br /&gt;
&lt;br /&gt;
Note: One interesting aspect of the application was the mix of JavaScript and CoffeeScript code. It wasn't clear why both types of code were used.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery -&amp;gt;&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: -&amp;gt;&lt;br /&gt;
      $.post($(this).data('update-url'), $(this).sortable('serialize'))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery(document).ready ($) -&amp;gt;&lt;br /&gt;
  sendUpdatedOrder = -&amp;gt;&lt;br /&gt;
    sortedIDs = $(this).sortable('toArray')&lt;br /&gt;
&lt;br /&gt;
    topicIDs = sortedIDs.map (id) -&amp;gt;&lt;br /&gt;
      id.replace 'topic_', ''&lt;br /&gt;
&lt;br /&gt;
    participant_id = $(this).data('participant-id')&lt;br /&gt;
&lt;br /&gt;
    assignment_id = new URL($(this).data('update-url'), window.location.origin).searchParams.get('assignment_id')&lt;br /&gt;
&lt;br /&gt;
    $.ajax&lt;br /&gt;
      type: 'POST'&lt;br /&gt;
      url: $(this).data('update-url')&lt;br /&gt;
      data:&lt;br /&gt;
        topic: topicIDs&lt;br /&gt;
        participant_id: participant_id&lt;br /&gt;
        assignment_id: assignment_id&lt;br /&gt;
      success: (response) -&amp;gt;&lt;br /&gt;
        console.log 'Selections updated successfully.'&lt;br /&gt;
      error: (xhr, status, error) -&amp;gt;&lt;br /&gt;
        alert 'An error occurred while saving your selections. Please try again.'&lt;br /&gt;
        console.error error&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: sendUpdatedOrder&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-1.-signupsheetcontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; Updates ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; method renders the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; action in &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt;. However, the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; does not include the controller action for this view. We need to find the old implementation of the controller action, add it back, and review and update it for any DRY violations.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # provides variables for reviewing page located at views/review_bids/others_work.html.erb&lt;br /&gt;
  def index&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
    return unless current_user_id?(@participant.user_id)&lt;br /&gt;
&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
    # Finding how many reviews have been completed&lt;br /&gt;
    @num_reviews_completed = 0&lt;br /&gt;
    @review_mappings.each do |map|&lt;br /&gt;
      @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # render view for completing reviews after review bidding has been completed&lt;br /&gt;
    render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
  end  &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-2.-reviewbidsotherswork-view-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;review_bids_others_work.html.erb&amp;lt;/code&amp;gt; View Updates ====&lt;br /&gt;
&lt;br /&gt;
The code only contains a view with this name and no associated controller action. Reviewing the code in the view, we noticed some SRP violations where there is much logic in the presentation layer. Set up helper methods in the controller to handle some of that logic or see if the model can handle some of that logic.&lt;br /&gt;
&lt;br /&gt;
Note: The view is technically part of the SignUpSheetController, but it's being rendered by the ReviewBidsController, which initially threw us off. After chatting with the professor, we decided not to change this view. He indicated that the plan is to reuse these views for both assignment bidding and review bidding.&lt;br /&gt;
&lt;br /&gt;
We would suggest building the assignment bidding and review bidding features separately first to make sure each one works on its own. Once that’s done, we can go back and clean things up by combining any shared functionality. This could include creating shared views or even introducing an abstract model to handle both types of bidding in a cleaner way. It could also mean replacing the ReviewBidsController with something more generic, like a BidsController, to keep things reusable and easier to maintain down the line.&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Rename &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; method ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; should be &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt;&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:''' Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The primary activity here is to refactor the method name &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; and fix the hardcoded URL. However, the match topic bidding algorithm no longer works since Heroku is no longer free, per the professor. The reference link below has detailed information about the web service that we can leverage to gain that understanding.&lt;br /&gt;
&lt;br /&gt;
Reference Link for Webservice Details: [https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Updated Implementation ====&lt;br /&gt;
The original requirement was to rename the run_bidding_algorithm method to assign_reviewers, but after digging into the code, we realized that renaming it didn’t make sense. The method runs the bidding algorithm to match topics, and there’s already a separate method to handle assigning review topics.&lt;br /&gt;
&lt;br /&gt;
However, we improved the logic in the assign_bidding method (which is called by the run_bidding_algorithm) by introducing a separate BiddingAlgorithmService object. This keeps the business logic out of the controller action, which makes things cleaner and more maintainable.&lt;br /&gt;
&lt;br /&gt;
==== review_bids_controller ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def run_bidding_algorithm&lt;br /&gt;
    review_bid = ReviewBid.new&lt;br /&gt;
    bidding_data = review_bid.bidding_data&lt;br /&gt;
    matched_topics = BiddingAlgorithmService.new(bidding_data).run&lt;br /&gt;
    raise ArgumentError, 'Failed to assign reviewers. Please try again later.' unless matched_topics&lt;br /&gt;
&lt;br /&gt;
    matched_topics&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== bidding_algorithm_service ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# frozen_string_literal: true&lt;br /&gt;
&lt;br /&gt;
# The `BiddingAlgorithmService` run the bid assignment algorithm&lt;br /&gt;
# Sends student IDs, topic IDs, student preferences, and timestamps to the web service&lt;br /&gt;
# The web service returns the matched assignments in the JSON response body&lt;br /&gt;
class BiddingAlgorithmService&lt;br /&gt;
  SERVICE_URL = 'http://app-csc517.herokuapp.com/match_topics'.freeze&lt;br /&gt;
&lt;br /&gt;
  # MOCK_DATA is based on the documentation detailing how the webservice behaves.&lt;br /&gt;
  # Reference: https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review#Webservice&lt;br /&gt;
  MOCK_DATA = {&lt;br /&gt;
    36239 =&amp;gt; [3970, 3972, 3975],&lt;br /&gt;
    36240 =&amp;gt; [3973, 3974, 3972],&lt;br /&gt;
    36241 =&amp;gt; [3969, 3971, 3972],&lt;br /&gt;
    36242 =&amp;gt; [3969, 3971, 3973],&lt;br /&gt;
    36243 =&amp;gt; [3969, 3970, 3971]&lt;br /&gt;
  }.freeze&lt;br /&gt;
&lt;br /&gt;
  def initialize(bidding_data)&lt;br /&gt;
    @bidding_data = bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def run&lt;br /&gt;
    return MOCK_DATA if Rails.application.config.use_mock_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
    perform_request&lt;br /&gt;
  rescue RestClient::ExceptionWithResponse, JSON::ParserError&lt;br /&gt;
    false&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def perform_request&lt;br /&gt;
    response = RestClient.post(&lt;br /&gt;
      SERVICE_URL,&lt;br /&gt;
      @bidding_data.to_json,&lt;br /&gt;
      content_type: :json,&lt;br /&gt;
      accept: :json&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    validate_response(response)&lt;br /&gt;
    JSON.parse(response.body)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def validate_response(response)&lt;br /&gt;
    raise 'Invalid response format' unless response.headers[:content_type] &amp;amp;&amp;amp; response.headers[:content_type].include?('application/json')&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Resolve Functionality Issues for Non Bidders ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Currently it doesn't work if some student does not bid. In this case, algorithm needs to be fixed to ignore anyone who didn’t bid, and let them choose from what’s left over.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to do several things within this controller. We will set a default state for non-bidders that we can point to to give them options to select topics to review from the leftovers. We will consolidate &amp;lt;code&amp;gt;@sign_up_topics&amp;lt;/code&amp;gt; to resolve the DRY violation. Lastly, we will remove business logic from the show method and push it to a new helper to conform to the SRP.&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # Additional method&lt;br /&gt;
&lt;br /&gt;
  # Provides variables for review bidding page&lt;br /&gt;
  def show&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id].to_i)&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @sign_up_topics = SignUpTopic.where(assignment_id: @assignment.id, private_to: nil)&lt;br /&gt;
    my_topic = SignedUpTeam.topic_id(@participant.parent_id, @participant.user_id)&lt;br /&gt;
    @sign_up_topics -= SignUpTopic.where(assignment_id: @assignment.id, id: my_topic)&lt;br /&gt;
    @num_participants = AssignmentParticipant.where(parent_id: @assignment.id).count&lt;br /&gt;
    @selected_topics = nil # This is used to list the topics assigned to review (i.e., select == assigned)&lt;br /&gt;
    @bids = ReviewBid.where(participant_id: @participant, assignment_id: @assignment.id)&lt;br /&gt;
    signed_up_topics = []&lt;br /&gt;
    &lt;br /&gt;
    @bids.each do |bid|&lt;br /&gt;
      sign_up_topic = SignUpTopic.find_by(id: bid.signuptopic_id)&lt;br /&gt;
      signed_up_topics &amp;lt;&amp;lt; sign_up_topic if sign_up_topic&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    signed_up_topics &amp;amp;= @sign_up_topics&lt;br /&gt;
    @sign_up_topics -= signed_up_topics&lt;br /&gt;
    @bids = signed_up_topics&lt;br /&gt;
    @num_of_topics = @sign_up_topics.size&lt;br /&gt;
    @assigned_review_maps = []&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: @assignment.id, reviewer_id: @participant.id).each do |review_map|&lt;br /&gt;
      @assigned_review_maps &amp;lt;&amp;lt; review_map&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Explicitly render view since it's in the sign up sheet views&lt;br /&gt;
    render 'sign_up_sheet/review_bids_show'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Additional methods&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-2.-reviewbidshelper-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
 # Assigns bidding topics to reviewers&lt;br /&gt;
  def assign_bidding&lt;br /&gt;
      assignment = validate_assignment(params[:assignment_id])&lt;br /&gt;
&lt;br /&gt;
      reviewers = validate_reviewers(assignment.id)&lt;br /&gt;
      reviewer_ids = reviewers.map(&amp;amp;:id)&lt;br /&gt;
&lt;br /&gt;
      matched_topics = run_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
      if matched_topics.blank?&lt;br /&gt;
        flash[:alert] = 'Topic or assignment is missing'&lt;br /&gt;
        redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      leftover_topics = find_leftover_topics(assignment.id, matched_topics)&lt;br /&gt;
      assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.new.assign_review_topics(matched_topics)&lt;br /&gt;
      assignment.update!(can_choose_topic_to_review: false)&lt;br /&gt;
&lt;br /&gt;
      flash[:notice] = 'Reviewers were successfully assigned to topics.'&lt;br /&gt;
      redirect_back fallback_location: root_path&lt;br /&gt;
    rescue ArgumentError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ArgumentError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: e.message&lt;br /&gt;
    rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::RecordInvalid: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue ActiveRecord::ActiveRecordError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::ActiveRecordError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue StandardError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;StandardError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers. Please try again later.'&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  #service methods here ...&lt;br /&gt;
&lt;br /&gt;
  def find_leftover_topics(assignment_id, matched_topics)&lt;br /&gt;
    all_topic_ids = SignUpTopic.where(assignment_id: assignment_id).pluck(:id)&lt;br /&gt;
    assigned_topic_ids = matched_topics.map { |match| match[:topic_id] }&lt;br /&gt;
&lt;br /&gt;
    # Calculate leftover topics&lt;br /&gt;
    all_topic_ids - assigned_topic_ids&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
    return if leftover_topics.blank?&lt;br /&gt;
&lt;br /&gt;
    # Find non-bidders by excluding already matched reviewers&lt;br /&gt;
    non_bidders = reviewer_ids - matched_topics.keys&lt;br /&gt;
&lt;br /&gt;
    # Assign leftover topics to non-bidders in a round-robin fashion&lt;br /&gt;
    non_bidders.each_with_index do |reviewer_id, index|&lt;br /&gt;
      topic_id = leftover_topics[index % leftover_topics.length]&lt;br /&gt;
      ReviewBid.create(priority: 1, signuptopic_id: topic_id, participant_id: reviewer_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirm Functionality for Single-person Teams ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Make sure your code works on individual assignments, i.e., assignments where participants sign up for topics instead of teams. In this case, a team is created for each participant when they sign up. So the code should work for assignments to which either individuals or teams submit.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to make an update to focus on topic rather than team or have it where teams are updated based on topic id.We will probably utilize sign up topic or a similar object to accomplish this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-5-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bid Status Messaging ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| To make the functionality more intuitive, include a message to say how many students are eligible to submit bids, how many have submitted their bids, when the deadline for submitting bids is.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
The logic for these messages will go into the helper file. It is effectively the same reasoning as our migration of other business/functionality code from the controller to the helper to conform to the SRP. It will go hand in hand with the SignUpTopic portion of the code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-6-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Why are the methods in review_bid.rb class methods? Can we change them to instance methods or move it to helpers?&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-1.-reviewbid-model-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model Updates ====&lt;br /&gt;
Since the current class methods are tied to &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model instances, we will change them to instance methods. There is no need to move them to helpers.&lt;br /&gt;
&lt;br /&gt;
===== Review Bid Model Flow Before Refactoring =====&lt;br /&gt;
This sequence diagram describes how the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; interacts with the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model in assigning review topics to reviewers according to their bids. Once the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; action is initiated by the user, the controller gathers the IDs of reviewers and calls &amp;lt;code&amp;gt;ReviewBid.bidding_data&amp;lt;/code&amp;gt; to gather bidding information. That is sent out to an external bidding algorithm, and the matched topics that come back are used by &amp;lt;code&amp;gt;ReviewBid.assign_review_topics&amp;lt;/code&amp;gt; to create &amp;lt;code&amp;gt;ReviewResponseMap&amp;lt;/code&amp;gt; entries, assigning topics to reviewers for the assignment.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
  belongs_to :assignment, class_name: 'Assignment'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  def self.bidding_data(assignment_id, reviewer_ids)&lt;br /&gt;
    # create basic hash and set basic hash data&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'users' =&amp;gt; {}, 'max_accepted_proposals' =&amp;gt; [] }&lt;br /&gt;
    bidding_data['tid'] = SignUpTopic.where(assignment_id: assignment_id).ids&lt;br /&gt;
    bidding_data['max_accepted_proposals'] = Assignment.where(id: assignment_id).pluck(:num_reviews_allowed).first&lt;br /&gt;
&lt;br /&gt;
    # loop through reviewer_ids to get reviewer specific bidding data&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      bidding_data['users'][reviewer_id] = reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # assigns topics to reviews as matched by the webservice algorithm&lt;br /&gt;
  def self.assign_review_topics(assignment_id, reviewer_ids, matched_topics, _min_num_reviews = 2)&lt;br /&gt;
    # if review response map already created, delete it&lt;br /&gt;
    if ReviewResponseMap.where(reviewed_object_id: assignment_id)&lt;br /&gt;
      ReviewResponseMap.where(reviewed_object_id: assignment_id).destroy_all&lt;br /&gt;
    end&lt;br /&gt;
    # loop through reviewer_ids to assign reviews to each reviewer&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      topics_to_assign = matched_topics[reviewer_id.to_s]&lt;br /&gt;
      topics_to_assign.each do |topic|&lt;br /&gt;
        assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method to assign a single topic to a reviewer&lt;br /&gt;
  def self.assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic).pluck(:team_id).first&lt;br /&gt;
    team_to_review.nil? ? [] : ReviewResponseMap.create(reviewed_object_id: assignment_id, reviewer_id: reviewer_id, reviewee_id: team_to_review, type: 'ReviewResponseMap')&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method for getting individual reviewer_ids bidding data&lt;br /&gt;
  # returns user's bidding data hash&lt;br /&gt;
  def self.reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    reviewer_user_id = AssignmentParticipant.find(reviewer_id).user_id&lt;br /&gt;
    self_topic = SignedUpTeam.topic_id(assignment_id, reviewer_user_id)&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'otid' =&amp;gt; self_topic, 'priority' =&amp;gt; [], 'time' =&amp;gt; [] }&lt;br /&gt;
    bids = ReviewBid.where(participant_id: reviewer_id)&lt;br /&gt;
&lt;br /&gt;
    # loop through each bid for a topic to get specific data&lt;br /&gt;
    bids.each do |bid|&lt;br /&gt;
      bidding_data['tid'] &amp;lt;&amp;lt; bid.signuptopic_id&lt;br /&gt;
      bidding_data['priority'] &amp;lt;&amp;lt; bid.priority&lt;br /&gt;
      bidding_data['time'] &amp;lt;&amp;lt; bid.updated_at&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  # Instance method to get bidding data for this review bid&lt;br /&gt;
  def bidding_data&lt;br /&gt;
    {&lt;br /&gt;
      'tid' =&amp;gt; topic_ids,&lt;br /&gt;
      'otid' =&amp;gt; self_topic,&lt;br /&gt;
      'priority' =&amp;gt; priorities,&lt;br /&gt;
      'time' =&amp;gt; updated_times&lt;br /&gt;
    }&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # bid object to limit db touches for similar data acquisitions&lt;br /&gt;
  def bids&lt;br /&gt;
    @bids ||= ReviewBid.where(participant_id: participant_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def topic_ids&lt;br /&gt;
    bids.pluck(:signuptopic_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def priorities&lt;br /&gt;
    bids.pluck(:priority)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def updated_times&lt;br /&gt;
    bids.pluck(:updated_at)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self_topic&lt;br /&gt;
    return nil unless topic &amp;amp;&amp;amp; topic.assignment_id&lt;br /&gt;
&lt;br /&gt;
    SignedUpTeam.topic_id(topic.assignment_id, participant.user_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign topics to reviews&lt;br /&gt;
  def assign_review_topics(matched_topics)&lt;br /&gt;
    validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: topic.assignment_id).destroy_all&lt;br /&gt;
&lt;br /&gt;
    matched_topics.each do |reviewer_id, topics|&lt;br /&gt;
      Array(topics).each { |topic_id| assign_topic_to_reviewer(reviewer_id, topic_id) }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign a single topic to a reviewer&lt;br /&gt;
  def assign_topic_to_reviewer(reviewer_id, topic_id)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic_id).pluck(:team_id).first&lt;br /&gt;
    return if team_to_review.nil?&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.create(&lt;br /&gt;
      reviewed_object_id: topic.assignment_id,&lt;br /&gt;
      reviewer_id: reviewer_id,&lt;br /&gt;
      reviewee_id: team_to_review,&lt;br /&gt;
      type: 'ReviewResponseMap'&lt;br /&gt;
    )&lt;br /&gt;
    # error handling&lt;br /&gt;
  rescue StandardError =&amp;gt; e&lt;br /&gt;
    Rails.logger.error(&amp;quot;Failed to assign topic #{topic_id} to reviewer #{reviewer_id}: #{e.message}&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
    raise ArgumentError, 'Topic or assignment is missing' if topic.nil? || topic.assignment_id.nil?&lt;br /&gt;
    raise ArgumentError, 'Matched topics must be a Hash' unless matched_topics.is_a?(Hash)&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
Once we change them to instance methods, we’ll need to modify their use in the code to instantiate the object first.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;Placeholder for implementation screenshots (before and after)&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-3.-schema.db-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;Schema.db&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We also need to remove the foreign key reference to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model since the &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model already has access to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;, and that is the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; we should be referencing. This will also involve modifying the database to remove the foreign key reference to the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Steps required:&lt;br /&gt;
&lt;br /&gt;
# Review the records in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table in the database to see if there are any records and if those records have values for &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;&lt;br /&gt;
# Create a migration file using the command: &amp;lt;code&amp;gt;rails generate migration RemoveAssignmentFromReviewBids&amp;lt;/code&amp;gt;&lt;br /&gt;
# In the generated file, indicate the change that needs to be made using the change method and migration columns &amp;lt;code&amp;gt;remove_foreign_key&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;remove_column&amp;lt;/code&amp;gt;. Migration should be straightforward since there is no cascading behavior (i.e., &amp;lt;code&amp;gt;dependent:destroy&amp;lt;/code&amp;gt;) to worry about.&lt;br /&gt;
# Once comfortable with the updates to the generated file, run the migration using the &amp;lt;code&amp;gt;rails db:migrate&amp;lt;/code&amp;gt; command.&lt;br /&gt;
# Run a query in the DB to ensure no orphaned records are in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table after the updates.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;Placeholder for implementation screenshots (before and after)&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-4.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implement Edge Case Testing ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| In the previous implementation wiki, there are edge cases which are not exhaustively tested. Should test those edge cases thoroughly and add more edge case testing - link&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span class=&amp;quot;mark&amp;quot;&amp;gt;TODO&amp;lt;/span&amp;gt;:We just got initial test results back. We still need to figure out why tests are failing and what may be slipping through the cracks.[[File:Test expertiza.png|411x459px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-8-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design Patterns and Principles ===&lt;br /&gt;
Below are the design patterns and principles we plan to use to address the problem statements outlined in the project requirements:&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
** Controller Responsibility&lt;br /&gt;
*** To address the issue of authentication logic residing in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, we will refactor the code to ensure the controller solely handles request and response flows.&lt;br /&gt;
*** Authentication logic will be moved to dedicated methods in &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt;, making the codebase easier to maintain and test.&lt;br /&gt;
** View Simplification&lt;br /&gt;
*** Currently, the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; view contains excessive business logic, violating SRP.&lt;br /&gt;
*** We will move this logic into helper methods or models, ensuring each component has a single responsibility and improving code readability.&lt;br /&gt;
&lt;br /&gt;
* Separation of Concerns (SoC)&lt;br /&gt;
** By relocating authorization logic out of the controller, we separate authentication from request handling.&lt;br /&gt;
** Adhering to SoC enhances readability and maintainability by allowing each part of the application to focus on its specific role.&lt;br /&gt;
* Don't Repeat Yourself (DRY)&lt;br /&gt;
** We will inspect the existing implementation for code duplication, particularly in the controller actions.&lt;br /&gt;
** Any repetitive code will be refactored or removed to reduce redundancy and improve clarity.&lt;br /&gt;
&lt;br /&gt;
* Model-View-Controller (MVC) Adherence&lt;br /&gt;
** Moving business logic from the view aligns with MVC best practices.&lt;br /&gt;
** By ensuring the view only presents data without processing it, we maintain clear boundaries between the model, view, and controller layers.&lt;br /&gt;
&lt;br /&gt;
* Dependency Inversion Principle (DIP)&lt;br /&gt;
** We plan to update class methods to instance methods, allowing the controller to depend on abstractions rather than concrete implementations.&lt;br /&gt;
** This change leverages Ruby's duck typing, providing flexibility and making the code easier to maintain and test.&lt;br /&gt;
&lt;br /&gt;
* Factory Pattern&lt;br /&gt;
** We will implement a factory pattern for the common processing of &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpBid&amp;lt;/code&amp;gt;, encapsulating object creation and promoting code reuse.&lt;br /&gt;
&lt;br /&gt;
* Law of Demeter (Principle of Least Knowledge)&lt;br /&gt;
** We'll delegate interactions appropriately to prevent tightly coupled code, especially when dealing with topics and teams.&lt;br /&gt;
** Clear and intentional naming during refactoring will enhance code clarity and maintainability.&lt;br /&gt;
&lt;br /&gt;
* Adapter Pattern&lt;br /&gt;
** To add messaging to bid states for users, we'll use the adapter pattern to allow different components to work together without modifying their existing code.&lt;br /&gt;
&lt;br /&gt;
== Files Modified/Added ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-1.-controllers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
review_bids_controller.rb&lt;br /&gt;
&lt;br /&gt;
signup_sheet_controller.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-2.-models&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
review_bid.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-3.-helpers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
&lt;br /&gt;
authorization_helper.rb&lt;br /&gt;
&lt;br /&gt;
Review_bids_helper.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;viii.-test-plan&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
The Excel spreadsheet below details our test plan for testing the code we changed or created to ensure code coverage. The process involved reviewing the code and determining all the possible scenarios that needed to be covered based on what the method was doing, then creating test cases accordingly.&lt;br /&gt;
&lt;br /&gt;
* [https://docs.google.com/spreadsheets/d/1iHzOdSwlTtxOtf5VsNdwOaCP8NzYMmiJ/edit?usp=drive_link Open Test Matrix Spreadsheet]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/review_bids_controller_spec.rb ReviewBidsController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/sign_up_sheet_controller_spec.rb SignUpSheetController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/models/review_bid_spec.rb ReviewBid model updated test cases]&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-1.-mentor&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Ed Gehringer&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-2.-members&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Members ===&lt;br /&gt;
&lt;br /&gt;
* Gavin Teague&lt;br /&gt;
* Janice Uwujaren&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xi.-links&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
Previous Wiki: [https://wiki.expertiza.ncsu.edu/index.php/CSC/ECE_517_Fall_2021_-_E2151._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;Link&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xii.-references&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/document/d/1LHVacylQmB15I6VcYUA5nuC7GRRU9szHzZhjlPcE-PA/edit?tab=t.0#heading=h.u0oycoj6e0ev &amp;lt;u&amp;gt;Project Instructions&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;appendix-a-uml-class-diagram-plantuml-code-for-review-bids&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Juwujar</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160976</id>
		<title>CSC/ECE 517 Fall 2024 - E2485. Allow reviewers to bid on what to review</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160976"/>
		<updated>2024-12-10T23:09:41Z</updated>

		<summary type="html">&lt;p&gt;Juwujar: /* Client Side Changes to Support ReviewBidsController#set_priority Changes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
This project will fix the defects and improve the functionality in Expertiza of the Reviewer Bidding feature, a process where reviewers can bid for assignments they want to review. It will make the functionality smoother, more reliable, and align with Expertiza's collaborative learning objectives.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iii.-problem-statements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Problem Statements ==&lt;br /&gt;
&lt;br /&gt;
Assigning reviews to users on Expertiza involves a complex bidding algorithm that requires optimization before production deployment. Below are key areas we plan to address as per the project requirements:&lt;br /&gt;
* Code Refactoring and Best Practices&lt;br /&gt;
** '''Utilize Authentication Utilities''': Modify the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; function in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to use existing authentication utilities, enhancing code reusability and adhering to the Single Responsibility Principle (SRP) and Separation of Concerns (SoC).&lt;br /&gt;
** '''Eliminate Code Duplication''': Refactor &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; to comply with the Don't Repeat Yourself (DRY) principle.&lt;br /&gt;
** '''Rename Functions for Clarity''': Change &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; to better reflect its purpose.&lt;br /&gt;
** '''Refactor Methods in '''&amp;lt;code&amp;gt;review_bid.rb&amp;lt;/code&amp;gt;''':''': Convert class methods to instance methods or relocate them to helper classes for improved code organization.&lt;br /&gt;
* Functionality Enhancements&lt;br /&gt;
** '''Handle Missing Bids Gracefully''': Adjust the bidding algorithm to accommodate students who do not bid, allowing them to select from the remaining options.&lt;br /&gt;
** '''Support Both Individual and Team Assignments''': Ensure the code functions correctly for participant and team-based assignments when signing up for topics.&lt;br /&gt;
* User Interface Improvements&lt;br /&gt;
** '''Increase Transparency in the Bidding Process''': Display messages indicating how many students are eligible to bid, how many have submitted bids, and the deadline.&lt;br /&gt;
** '''Accurate Bid Status Indicators''': Ensure topic colors change correctly based on the number of outstanding bids, providing clear visual feedback to users.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Exhaustive Edge Case Testing''': Conduct thorough testing for edge cases not previously covered, adding additional test cases as needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iv.-design-goals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Design Goals ==&lt;br /&gt;
&lt;br /&gt;
While addressing the existing defects and enhancing the Reviewer Bidding feature, we plan to adhere to the following design guidelines:&lt;br /&gt;
&lt;br /&gt;
* Code Quality and Maintainability&lt;br /&gt;
** '''Improve Code Structure: '''Implement refactoring strategies to enhance code readability and maintainability, ensuring adherence to best practices like the DRY principle.&lt;br /&gt;
** '''Standardize Authentication Logic: '''Utilize a centralized authentication utility to promote consistency and reusability across the application.&lt;br /&gt;
* System Stability and Flexibility&lt;br /&gt;
** '''Enhance Algorithm Robustness: '''Modify the bidding algorithm to handle scenarios where students do not bid, ensuring the system remains stable under all conditions.&lt;br /&gt;
** '''Ensure Assignment Versatility: '''Design the system to seamlessly support both individual and team assignments without additional configuration.&lt;br /&gt;
* User Experience Enhancements&lt;br /&gt;
** '''Increase Bidding Transparency: '''Develop user interface elements that clearly display bidding status, deadlines, and participation metrics.&lt;br /&gt;
** '''Implement Dynamic Visual Feedback: '''Use real-time visual cues, such as color changes, to inform users about the bidding status instantly.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Develop Comprehensive Test Suites: '''Create extensive automated tests covering edge cases and critical functionalities to guarantee reliable system behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;v.-uml-diagram&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== UML Diagram ==&lt;br /&gt;
&lt;br /&gt;
Below is a visual representation of the models associated with the review bids functionality, depicting the data structures and their relationships.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml class diagram2.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi.-implementation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
'''ReviewBid: '''The &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; manages the bidding process, where participants indicate their preferences for reviewing different topics or assignments. This helps distribute review tasks fairly based on participants' interests.&lt;br /&gt;
&lt;br /&gt;
'''Assignment: '''The &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model represents team or individual assignments in Expertiza. It stores all the essential information required to keep the assignment process organized, like managing participants, setting due dates, forming teams, and overseeing the review process.&lt;br /&gt;
&lt;br /&gt;
'''SignUpTopic: '''The &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt; model identifies specific topics or areas within an assignment for which participants can sign up. It manages the availability of these topics, tracks bids, coordinates team sign-ups, and assigns topics based on both bids and participant availability. &lt;br /&gt;
&lt;br /&gt;
'''Participant: '''The &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model is about the individual users who participate in an assignment. This includes students, instructors, teaching assistants, or other relevant roles. This model is responsible for managing key user information, monitoring interactions with the assignment, keeping track of team memberships, and recording things like badges and review grades.&lt;br /&gt;
&lt;br /&gt;
== Implementation ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1.-decoupling-authorization-logic-from-reviewbidscontroller&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Decoupling Authorization Logic from &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| '''&amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; needs to use the authentication utilities'''&lt;br /&gt;
&lt;br /&gt;
The function &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; should use the available authentication utilities to support the Single Responsibility Principle and Separation of Concerns and to support code reusability.&lt;br /&gt;
&lt;br /&gt;
| '''Authentication Consistency:''' Remove authentication logic in the controller and call logic in the authentication utility class instead to ensure consistent use of the same logic across the application, foster reusability, and eliminate DRY.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
==== Authorization Flow Before Refactoring ====&lt;br /&gt;
The sequence diagram depicts the authorization process within the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;. When a user invokes an action, the controller checks if the action is allowed by verifying the user's role through the &amp;lt;code&amp;gt;current_role_name()&amp;lt;/code&amp;gt; method in the &amp;lt;code&amp;gt;ApplicationController&amp;lt;/code&amp;gt;. Depending on the action and the user's role, the controller either authorizes the user to proceed or denies access, ultimately executing the action logic if authorized and responding to the user.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 1.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We simplified the &amp;lt;code&amp;gt;action_allowed?&amp;lt;/code&amp;gt; method by decoupling authorization logic from the controller to use helper methods in the &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt; module. The &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; should not handle authentication, which is a violation of SRP. &lt;br /&gt;
&lt;br /&gt;
Note: We also made similar updates to the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; methods for &amp;lt;code&amp;gt;SignUpController&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;vertical-align:top;&amp;quot; | Original Code !! style=&amp;quot;vertical-align:top;&amp;quot; | Refactored Code&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index'&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator',&lt;br /&gt;
     'Student'].include? current_role_name and&lt;br /&gt;
        ((%w[list].include? action_name) ? are_needed_authorizations_present?(params[:id], &amp;quot;participant&amp;quot;, &amp;quot;reader&amp;quot;, &amp;quot;submitter&amp;quot;, &amp;quot;reviewer&amp;quot;) : true)&lt;br /&gt;
  else&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator'].include? current_role_name&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index', 'list'&lt;br /&gt;
    current_user_has_student_privileges? &amp;amp;&amp;amp; list_authorization_check&lt;br /&gt;
  else&lt;br /&gt;
    current_user_has_ta_privileges?&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; action ===&lt;br /&gt;
Previously, the &amp;lt;code&amp;gt;ReviewBidsController's index&amp;lt;/code&amp;gt; action was overloaded with responsibilities—it handled everything from locating and validating assignments to fetching review mappings and counting completed reviews. This all-in-one approach made the controller action messy. &lt;br /&gt;
&lt;br /&gt;
We have moved the business logic from the controller action and into dedicated service objects. With this change, we introduced two key service objects: &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; focuses on retrieving the participant along with their assignments, while the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; takes on the task of managing review mappings and calculating review-related stats, like how many reviews have been completed. &lt;br /&gt;
&lt;br /&gt;
This separation of responsibilities aligns with the Single Responsibility Principle. The logic for fetching assignments, retrieving review mappings, and counting reviews is now more flexible and reusable throughout the application. For example, we used methods in the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; in both the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; and the &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;, helping to avoid code duplication.The article https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial served as a guide on when and how to use service objects in Rails.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
  return unless current_user_id?(@participant.user_id)&lt;br /&gt;
  @assignment = @participant.assignment&lt;br /&gt;
  @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
  # Finding how many reviews have been completed&lt;br /&gt;
  @num_reviews_completed = 0&lt;br /&gt;
  @review_mappings.each do |map|&lt;br /&gt;
    @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # render view for completing reviews after review bidding has been completed&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @assignment = participant_service.assignment&lt;br /&gt;
  unless @assignment.is_a?(Assignment)&lt;br /&gt;
    flash[:error] = 'Assignment not found.'&lt;br /&gt;
    redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  @review_mappings = review_service.review_mappings&lt;br /&gt;
  @num_reviews_completed = review_service.review_counts[:completed]&lt;br /&gt;
&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Optimize &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Action functionality ===&lt;br /&gt;
In the earlier version of the set_priority action in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, the server only needed a couple of key pieces of information: an array of topic IDs (params[:topic]) and the participant ID (params[:id}). From there, it would figure out the assignment_id by using the first topic as a reference. &lt;br /&gt;
&lt;br /&gt;
With the new implementation, the action now expects clear and structured parameters, including participant_id, an array of topic IDs, and assignment_id. This change ensures that the server always receives complete and reliable information, preventing issues caused by missing or inconsistent data. It also removes the need for the server to perform unnecessary lookups, allowing the controller to focus on its main job: validating inputs (like making sure the topics in params[:topic] belong to the same assignment as the participant), updating the database, handling errors properly, and sending the correct response back to the view.&lt;br /&gt;
&lt;br /&gt;
While these updates have cleaned up the set_priority action, the action still contains a lot of business logic, like validations and priority updates. To make the code even more maintainable, there’s room for further refactoring—perhaps moving some of this logic into a service class or model method to streamline the controller even more.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | Old Code&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    if params[:topic].nil?&lt;br /&gt;
      ReviewBid.where(participant_id: params[:id]).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      participant = AssignmentParticipant.find_by(id: params[:id])&lt;br /&gt;
      assignment_id = SignUpTopic.find(params[:topic].first).assignment.id&lt;br /&gt;
      @bids = ReviewBid.where(participant_id: params[:id])&lt;br /&gt;
      signed_up_topics = ReviewBid.where(participant_id: params[:id]).map(&amp;amp;:signuptopic_id)&lt;br /&gt;
      signed_up_topics -= params[:topic].map(&amp;amp;:to_i)&lt;br /&gt;
      signed_up_topics.each do |topic|&lt;br /&gt;
        ReviewBid.where(signuptopic_id: topic, participant_id: params[:id]).destroy_all&lt;br /&gt;
      end&lt;br /&gt;
      params[:topic].each_with_index do |topic_id, index|&lt;br /&gt;
        bid_existence = ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id])&lt;br /&gt;
        if bid_existence.empty?&lt;br /&gt;
          ReviewBid.create(priority: index + 1, signuptopic_id: topic_id, participant_id: params[:id], assignment_id: assignment_id)&lt;br /&gt;
        else&lt;br /&gt;
          ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id]).update_all(priority: index + 1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to action: 'show', assignment_id: params[:assignment_id], id: params[:id]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    participant_id = params[:participant_id].to_i&lt;br /&gt;
    selected_topic_ids = params[:topic]&amp;amp;.map(&amp;amp;:to_i) || []&lt;br /&gt;
    assignment_id = params[:assignment_id].to_i&lt;br /&gt;
&lt;br /&gt;
    unless current_user_can_modify_participant?(participant_id)&lt;br /&gt;
      flash[:error] = 'You are not authorized to modify these bids.'&lt;br /&gt;
      respond_to do |format|&lt;br /&gt;
        format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
        format.json { render json: { status: 'unauthorized' }, status: :unauthorized }&lt;br /&gt;
      end&lt;br /&gt;
      return&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    if selected_topic_ids.empty?&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      assignment = Assignment.find_by(id: assignment_id)&lt;br /&gt;
      if assignment.nil?&lt;br /&gt;
        flash[:error] = &amp;quot;Invalid assignment.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_assignment' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      unless SignUpTopic.where(id: selected_topic_ids, assignment_id: assignment_id).count == selected_topic_ids.size&lt;br /&gt;
        flash[:error] = &amp;quot;One or more selected topics are invalid.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_topics' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).where.not(signuptopic_id: selected_topic_ids).destroy_all&lt;br /&gt;
&lt;br /&gt;
      selected_topic_ids.each_with_index do |topic_id, index|&lt;br /&gt;
        bid = ReviewBid.find_or_initialize_by(signuptopic_id: topic_id, participant_id: participant_id)&lt;br /&gt;
        bid.priority = index + 1&lt;br /&gt;
        bid.assignment_id = assignment_id&lt;br /&gt;
        bid.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_to action: 'show', assignment_id: assignment_id, id: participant_id, notice: 'Review bids updated successfully.' }&lt;br /&gt;
      format.json { render json: { status: 'success' }, status: :ok }&lt;br /&gt;
    end&lt;br /&gt;
  rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
    flash[:error] = &amp;quot;Failed to update priorities: #{e.message}&amp;quot;&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
      format.json { render json: { status: 'error', message: e.message }, status: :unprocessable_entity }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Client Side Changes to Support &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Changes ====&lt;br /&gt;
After tidying up the server-side logic, we needed to refactor the client-side code to ensure it passed the correct information from the view to the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action. On the client side, we now grab the topicIDs and send them as &amp;lt;code&amp;gt;selected_topic_ids&amp;lt;/code&amp;gt;, allowing the controller to figure out which topics were selected and in what order.  &lt;br /&gt;
&lt;br /&gt;
We also extracted the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, making it a lot easier to send data with AJAX and keeping things flexible. When working with jQuery, AJAX is awesome because it lets us update the server without reloading the page, making the user experience smooth and responsive. When we pull the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, we can grab it along with other data, like the list of topics, and include it in the request. This way, we don’t have to rely on the URL to know which participant we’re working with, which makes the code cleaner and more adaptable.&lt;br /&gt;
&lt;br /&gt;
If we were to rely on the route (as opposed to using the DOM) for the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt;, every AJAX request would need to be tied to a specific URL structure. That’s limiting, especially if the route doesn’t naturally include the participant ID, as can be the case with collection routes versus member routes in Rails. &lt;br /&gt;
&lt;br /&gt;
Note: One interesting aspect of the application was the mix of JavaScript and CoffeeScript code. It wasn't clear why both types of code were used.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery -&amp;gt;&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: -&amp;gt;&lt;br /&gt;
      $.post($(this).data('update-url'), $(this).sortable('serialize'))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery(document).ready ($) -&amp;gt;&lt;br /&gt;
  sendUpdatedOrder = -&amp;gt;&lt;br /&gt;
    sortedIDs = $(this).sortable('toArray')&lt;br /&gt;
&lt;br /&gt;
    topicIDs = sortedIDs.map (id) -&amp;gt;&lt;br /&gt;
      id.replace 'topic_', ''&lt;br /&gt;
&lt;br /&gt;
    participant_id = $(this).data('participant-id')&lt;br /&gt;
&lt;br /&gt;
    assignment_id = new URL($(this).data('update-url'), window.location.origin).searchParams.get('assignment_id')&lt;br /&gt;
&lt;br /&gt;
    $.ajax&lt;br /&gt;
      type: 'POST'&lt;br /&gt;
      url: $(this).data('update-url')&lt;br /&gt;
      data:&lt;br /&gt;
        topic: topicIDs&lt;br /&gt;
        participant_id: participant_id&lt;br /&gt;
        assignment_id: assignment_id&lt;br /&gt;
      success: (response) -&amp;gt;&lt;br /&gt;
        console.log 'Selections updated successfully.'&lt;br /&gt;
      error: (xhr, status, error) -&amp;gt;&lt;br /&gt;
        alert 'An error occurred while saving your selections. Please try again.'&lt;br /&gt;
        console.error error&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: sendUpdatedOrder&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-1.-signupsheetcontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; Updates ===&lt;br /&gt;
&lt;br /&gt;
The ReviewBidsController's index method renders the review_bids_others_work action in &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt;. However, the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; does not include the controller action for this view. We need to find the old implementation of the controller action, add it back, and review and update it for any DRY violations.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # provides variables for reviewing page located at views/review_bids/others_work.html.erb&lt;br /&gt;
  def index&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
    return unless current_user_id?(@participant.user_id)&lt;br /&gt;
&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
    # Finding how many reviews have been completed&lt;br /&gt;
    @num_reviews_completed = 0&lt;br /&gt;
    @review_mappings.each do |map|&lt;br /&gt;
      @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # render view for completing reviews after review bidding has been completed&lt;br /&gt;
    render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
  end  &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-2.-reviewbidsotherswork-view-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;review_bids_others_work.html.erb&amp;lt;/code&amp;gt; View Updates ====&lt;br /&gt;
&lt;br /&gt;
The code only contains a view with this name and no associated controller action. Reviewing the code in the view, we noticed some SRP violations where there is much logic in the presentation layer. Set up helper methods in the controller to handle some of that logic or see if the model can handle some of that logic.&lt;br /&gt;
&lt;br /&gt;
Note: The view is technically part of the SignUpSheetController, but it's being rendered by the ReviewBidsController, which initially threw us off. After chatting with the professor, we decided not to change this view. He indicated that the plan is to reuse these views for both assignment bidding and review bidding.&lt;br /&gt;
&lt;br /&gt;
We would suggest building the assignment bidding and review bidding features separately first to make sure each one works on its own. Once that’s done, we can go back and clean things up by combining any shared functionality. This could include creating shared views or even introducing an abstract model to handle both types of bidding in a cleaner way. It could also mean replacing the ReviewBidsController with something more generic, like a BidsController, to keep things reusable and easier to maintain down the line.&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Rename &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; method ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; should be &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt;&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:''' Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The primary activity here is to refactor the method name &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; and fix the hardcoded URL. However, the match topic bidding algorithm no longer works since Heroku is no longer free, per the professor. The reference link below has detailed information about the web service that we can leverage to gain that understanding.&lt;br /&gt;
&lt;br /&gt;
Reference Link for Webservice Details: [https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Updated Implementation ====&lt;br /&gt;
The original requirement was to rename the run_bidding_algorithm method to assign_reviewers, but after digging into the code, we realized that renaming it didn’t make sense. The method runs the bidding algorithm to match topics, and there’s already a separate method to handle assigning review topics.&lt;br /&gt;
&lt;br /&gt;
However, we improved the logic in the assign_bidding method (which is called by the run_bidding_algorithm) by introducing a separate BiddingAlgorithmService object. This keeps the business logic out of the controller action, which makes things cleaner and more maintainable.&lt;br /&gt;
&lt;br /&gt;
==== review_bids_controller ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def run_bidding_algorithm&lt;br /&gt;
    review_bid = ReviewBid.new&lt;br /&gt;
    bidding_data = review_bid.bidding_data&lt;br /&gt;
    matched_topics = BiddingAlgorithmService.new(bidding_data).run&lt;br /&gt;
    raise ArgumentError, 'Failed to assign reviewers. Please try again later.' unless matched_topics&lt;br /&gt;
&lt;br /&gt;
    matched_topics&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== bidding_algorithm_service ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# frozen_string_literal: true&lt;br /&gt;
&lt;br /&gt;
# The `BiddingAlgorithmService` run the bid assignment algorithm&lt;br /&gt;
# Sends student IDs, topic IDs, student preferences, and timestamps to the web service&lt;br /&gt;
# The web service returns the matched assignments in the JSON response body&lt;br /&gt;
class BiddingAlgorithmService&lt;br /&gt;
  SERVICE_URL = 'http://app-csc517.herokuapp.com/match_topics'.freeze&lt;br /&gt;
&lt;br /&gt;
  # MOCK_DATA is based on the documentation detailing how the webservice behaves.&lt;br /&gt;
  # Reference: https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review#Webservice&lt;br /&gt;
  MOCK_DATA = {&lt;br /&gt;
    36239 =&amp;gt; [3970, 3972, 3975],&lt;br /&gt;
    36240 =&amp;gt; [3973, 3974, 3972],&lt;br /&gt;
    36241 =&amp;gt; [3969, 3971, 3972],&lt;br /&gt;
    36242 =&amp;gt; [3969, 3971, 3973],&lt;br /&gt;
    36243 =&amp;gt; [3969, 3970, 3971]&lt;br /&gt;
  }.freeze&lt;br /&gt;
&lt;br /&gt;
  def initialize(bidding_data)&lt;br /&gt;
    @bidding_data = bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def run&lt;br /&gt;
    return MOCK_DATA if Rails.application.config.use_mock_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
    perform_request&lt;br /&gt;
  rescue RestClient::ExceptionWithResponse, JSON::ParserError&lt;br /&gt;
    false&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def perform_request&lt;br /&gt;
    response = RestClient.post(&lt;br /&gt;
      SERVICE_URL,&lt;br /&gt;
      @bidding_data.to_json,&lt;br /&gt;
      content_type: :json,&lt;br /&gt;
      accept: :json&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    validate_response(response)&lt;br /&gt;
    JSON.parse(response.body)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def validate_response(response)&lt;br /&gt;
    raise 'Invalid response format' unless response.headers[:content_type] &amp;amp;&amp;amp; response.headers[:content_type].include?('application/json')&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Resolve Functionality Issues for Non Bidders ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Currently it doesn't work if some student does not bid. In this case, algorithm needs to be fixed to ignore anyone who didn’t bid, and let them choose from what’s left over.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to do several things within this controller. We will set a default state for non-bidders that we can point to to give them options to select topics to review from the leftovers. We will consolidate &amp;lt;code&amp;gt;@sign_up_topics&amp;lt;/code&amp;gt; to resolve the DRY violation. Lastly, we will remove business logic from the show method and push it to a new helper to conform to the SRP.&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # Additional method&lt;br /&gt;
&lt;br /&gt;
  # Provides variables for review bidding page&lt;br /&gt;
  def show&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id].to_i)&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @sign_up_topics = SignUpTopic.where(assignment_id: @assignment.id, private_to: nil)&lt;br /&gt;
    my_topic = SignedUpTeam.topic_id(@participant.parent_id, @participant.user_id)&lt;br /&gt;
    @sign_up_topics -= SignUpTopic.where(assignment_id: @assignment.id, id: my_topic)&lt;br /&gt;
    @num_participants = AssignmentParticipant.where(parent_id: @assignment.id).count&lt;br /&gt;
    @selected_topics = nil # This is used to list the topics assigned to review (i.e., select == assigned)&lt;br /&gt;
    @bids = ReviewBid.where(participant_id: @participant, assignment_id: @assignment.id)&lt;br /&gt;
    signed_up_topics = []&lt;br /&gt;
    &lt;br /&gt;
    @bids.each do |bid|&lt;br /&gt;
      sign_up_topic = SignUpTopic.find_by(id: bid.signuptopic_id)&lt;br /&gt;
      signed_up_topics &amp;lt;&amp;lt; sign_up_topic if sign_up_topic&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    signed_up_topics &amp;amp;= @sign_up_topics&lt;br /&gt;
    @sign_up_topics -= signed_up_topics&lt;br /&gt;
    @bids = signed_up_topics&lt;br /&gt;
    @num_of_topics = @sign_up_topics.size&lt;br /&gt;
    @assigned_review_maps = []&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: @assignment.id, reviewer_id: @participant.id).each do |review_map|&lt;br /&gt;
      @assigned_review_maps &amp;lt;&amp;lt; review_map&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Explicitly render view since it's in the sign up sheet views&lt;br /&gt;
    render 'sign_up_sheet/review_bids_show'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Additional methods&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-2.-reviewbidshelper-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
 # Assigns bidding topics to reviewers&lt;br /&gt;
  def assign_bidding&lt;br /&gt;
      assignment = validate_assignment(params[:assignment_id])&lt;br /&gt;
&lt;br /&gt;
      reviewers = validate_reviewers(assignment.id)&lt;br /&gt;
      reviewer_ids = reviewers.map(&amp;amp;:id)&lt;br /&gt;
&lt;br /&gt;
      matched_topics = run_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
      if matched_topics.blank?&lt;br /&gt;
        flash[:alert] = 'Topic or assignment is missing'&lt;br /&gt;
        redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      leftover_topics = find_leftover_topics(assignment.id, matched_topics)&lt;br /&gt;
      assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.new.assign_review_topics(matched_topics)&lt;br /&gt;
      assignment.update!(can_choose_topic_to_review: false)&lt;br /&gt;
&lt;br /&gt;
      flash[:notice] = 'Reviewers were successfully assigned to topics.'&lt;br /&gt;
      redirect_back fallback_location: root_path&lt;br /&gt;
    rescue ArgumentError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ArgumentError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: e.message&lt;br /&gt;
    rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::RecordInvalid: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue ActiveRecord::ActiveRecordError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::ActiveRecordError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue StandardError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;StandardError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers. Please try again later.'&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  #service methods here ...&lt;br /&gt;
&lt;br /&gt;
  def find_leftover_topics(assignment_id, matched_topics)&lt;br /&gt;
    all_topic_ids = SignUpTopic.where(assignment_id: assignment_id).pluck(:id)&lt;br /&gt;
    assigned_topic_ids = matched_topics.map { |match| match[:topic_id] }&lt;br /&gt;
&lt;br /&gt;
    # Calculate leftover topics&lt;br /&gt;
    all_topic_ids - assigned_topic_ids&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
    return if leftover_topics.blank?&lt;br /&gt;
&lt;br /&gt;
    # Find non-bidders by excluding already matched reviewers&lt;br /&gt;
    non_bidders = reviewer_ids - matched_topics.keys&lt;br /&gt;
&lt;br /&gt;
    # Assign leftover topics to non-bidders in a round-robin fashion&lt;br /&gt;
    non_bidders.each_with_index do |reviewer_id, index|&lt;br /&gt;
      topic_id = leftover_topics[index % leftover_topics.length]&lt;br /&gt;
      ReviewBid.create(priority: 1, signuptopic_id: topic_id, participant_id: reviewer_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirm Functionality for Single-person Teams ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Make sure your code works on individual assignments, i.e., assignments where participants sign up for topics instead of teams. In this case, a team is created for each participant when they sign up. So the code should work for assignments to which either individuals or teams submit.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to make an update to focus on topic rather than team or have it where teams are updated based on topic id.We will probably utilize sign up topic or a similar object to accomplish this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-5-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bid Status Messaging ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| To make the functionality more intuitive, include a message to say how many students are eligible to submit bids, how many have submitted their bids, when the deadline for submitting bids is.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
The logic for these messages will go into the helper file. It is effectively the same reasoning as our migration of other business/functionality code from the controller to the helper to conform to the SRP. It will go hand in hand with the SignUpTopic portion of the code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-6-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Why are the methods in review_bid.rb class methods? Can we change them to instance methods or move it to helpers?&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-1.-reviewbid-model-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model Updates ====&lt;br /&gt;
Since the current class methods are tied to &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model instances, we will change them to instance methods. There is no need to move them to helpers.&lt;br /&gt;
&lt;br /&gt;
===== Review Bid Model Flow Before Refactoring =====&lt;br /&gt;
This sequence diagram describes how the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; interacts with the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model in assigning review topics to reviewers according to their bids. Once the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; action is initiated by the user, the controller gathers the IDs of reviewers and calls &amp;lt;code&amp;gt;ReviewBid.bidding_data&amp;lt;/code&amp;gt; to gather bidding information. That is sent out to an external bidding algorithm, and the matched topics that come back are used by &amp;lt;code&amp;gt;ReviewBid.assign_review_topics&amp;lt;/code&amp;gt; to create &amp;lt;code&amp;gt;ReviewResponseMap&amp;lt;/code&amp;gt; entries, assigning topics to reviewers for the assignment.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
  belongs_to :assignment, class_name: 'Assignment'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  def self.bidding_data(assignment_id, reviewer_ids)&lt;br /&gt;
    # create basic hash and set basic hash data&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'users' =&amp;gt; {}, 'max_accepted_proposals' =&amp;gt; [] }&lt;br /&gt;
    bidding_data['tid'] = SignUpTopic.where(assignment_id: assignment_id).ids&lt;br /&gt;
    bidding_data['max_accepted_proposals'] = Assignment.where(id: assignment_id).pluck(:num_reviews_allowed).first&lt;br /&gt;
&lt;br /&gt;
    # loop through reviewer_ids to get reviewer specific bidding data&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      bidding_data['users'][reviewer_id] = reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # assigns topics to reviews as matched by the webservice algorithm&lt;br /&gt;
  def self.assign_review_topics(assignment_id, reviewer_ids, matched_topics, _min_num_reviews = 2)&lt;br /&gt;
    # if review response map already created, delete it&lt;br /&gt;
    if ReviewResponseMap.where(reviewed_object_id: assignment_id)&lt;br /&gt;
      ReviewResponseMap.where(reviewed_object_id: assignment_id).destroy_all&lt;br /&gt;
    end&lt;br /&gt;
    # loop through reviewer_ids to assign reviews to each reviewer&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      topics_to_assign = matched_topics[reviewer_id.to_s]&lt;br /&gt;
      topics_to_assign.each do |topic|&lt;br /&gt;
        assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method to assign a single topic to a reviewer&lt;br /&gt;
  def self.assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic).pluck(:team_id).first&lt;br /&gt;
    team_to_review.nil? ? [] : ReviewResponseMap.create(reviewed_object_id: assignment_id, reviewer_id: reviewer_id, reviewee_id: team_to_review, type: 'ReviewResponseMap')&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method for getting individual reviewer_ids bidding data&lt;br /&gt;
  # returns user's bidding data hash&lt;br /&gt;
  def self.reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    reviewer_user_id = AssignmentParticipant.find(reviewer_id).user_id&lt;br /&gt;
    self_topic = SignedUpTeam.topic_id(assignment_id, reviewer_user_id)&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'otid' =&amp;gt; self_topic, 'priority' =&amp;gt; [], 'time' =&amp;gt; [] }&lt;br /&gt;
    bids = ReviewBid.where(participant_id: reviewer_id)&lt;br /&gt;
&lt;br /&gt;
    # loop through each bid for a topic to get specific data&lt;br /&gt;
    bids.each do |bid|&lt;br /&gt;
      bidding_data['tid'] &amp;lt;&amp;lt; bid.signuptopic_id&lt;br /&gt;
      bidding_data['priority'] &amp;lt;&amp;lt; bid.priority&lt;br /&gt;
      bidding_data['time'] &amp;lt;&amp;lt; bid.updated_at&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  # Instance method to get bidding data for this review bid&lt;br /&gt;
  def bidding_data&lt;br /&gt;
    {&lt;br /&gt;
      'tid' =&amp;gt; topic_ids,&lt;br /&gt;
      'otid' =&amp;gt; self_topic,&lt;br /&gt;
      'priority' =&amp;gt; priorities,&lt;br /&gt;
      'time' =&amp;gt; updated_times&lt;br /&gt;
    }&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # bid object to limit db touches for similar data acquisitions&lt;br /&gt;
  def bids&lt;br /&gt;
    @bids ||= ReviewBid.where(participant_id: participant_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def topic_ids&lt;br /&gt;
    bids.pluck(:signuptopic_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def priorities&lt;br /&gt;
    bids.pluck(:priority)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def updated_times&lt;br /&gt;
    bids.pluck(:updated_at)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self_topic&lt;br /&gt;
    return nil unless topic &amp;amp;&amp;amp; topic.assignment_id&lt;br /&gt;
&lt;br /&gt;
    SignedUpTeam.topic_id(topic.assignment_id, participant.user_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign topics to reviews&lt;br /&gt;
  def assign_review_topics(matched_topics)&lt;br /&gt;
    validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: topic.assignment_id).destroy_all&lt;br /&gt;
&lt;br /&gt;
    matched_topics.each do |reviewer_id, topics|&lt;br /&gt;
      Array(topics).each { |topic_id| assign_topic_to_reviewer(reviewer_id, topic_id) }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign a single topic to a reviewer&lt;br /&gt;
  def assign_topic_to_reviewer(reviewer_id, topic_id)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic_id).pluck(:team_id).first&lt;br /&gt;
    return if team_to_review.nil?&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.create(&lt;br /&gt;
      reviewed_object_id: topic.assignment_id,&lt;br /&gt;
      reviewer_id: reviewer_id,&lt;br /&gt;
      reviewee_id: team_to_review,&lt;br /&gt;
      type: 'ReviewResponseMap'&lt;br /&gt;
    )&lt;br /&gt;
    # error handling&lt;br /&gt;
  rescue StandardError =&amp;gt; e&lt;br /&gt;
    Rails.logger.error(&amp;quot;Failed to assign topic #{topic_id} to reviewer #{reviewer_id}: #{e.message}&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
    raise ArgumentError, 'Topic or assignment is missing' if topic.nil? || topic.assignment_id.nil?&lt;br /&gt;
    raise ArgumentError, 'Matched topics must be a Hash' unless matched_topics.is_a?(Hash)&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
Once we change them to instance methods, we’ll need to modify their use in the code to instantiate the object first.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;Placeholder for implementation screenshots (before and after)&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-3.-schema.db-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;Schema.db&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We also need to remove the foreign key reference to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model since the &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model already has access to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;, and that is the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; we should be referencing. This will also involve modifying the database to remove the foreign key reference to the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Steps required:&lt;br /&gt;
&lt;br /&gt;
# Review the records in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table in the database to see if there are any records and if those records have values for &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;&lt;br /&gt;
# Create a migration file using the command: &amp;lt;code&amp;gt;rails generate migration RemoveAssignmentFromReviewBids&amp;lt;/code&amp;gt;&lt;br /&gt;
# In the generated file, indicate the change that needs to be made using the change method and migration columns &amp;lt;code&amp;gt;remove_foreign_key&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;remove_column&amp;lt;/code&amp;gt;. Migration should be straightforward since there is no cascading behavior (i.e., &amp;lt;code&amp;gt;dependent:destroy&amp;lt;/code&amp;gt;) to worry about.&lt;br /&gt;
# Once comfortable with the updates to the generated file, run the migration using the &amp;lt;code&amp;gt;rails db:migrate&amp;lt;/code&amp;gt; command.&lt;br /&gt;
# Run a query in the DB to ensure no orphaned records are in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table after the updates.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;Placeholder for implementation screenshots (before and after)&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-4.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implement Edge Case Testing ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| In the previous implementation wiki, there are edge cases which are not exhaustively tested. Should test those edge cases thoroughly and add more edge case testing - link&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span class=&amp;quot;mark&amp;quot;&amp;gt;TODO&amp;lt;/span&amp;gt;:We just got initial test results back. We still need to figure out why tests are failing and what may be slipping through the cracks.[[File:Test expertiza.png|411x459px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-8-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design Patterns and Principles ===&lt;br /&gt;
Below are the design patterns and principles we plan to use to address the problem statements outlined in the project requirements:&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
** Controller Responsibility&lt;br /&gt;
*** To address the issue of authentication logic residing in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, we will refactor the code to ensure the controller solely handles request and response flows.&lt;br /&gt;
*** Authentication logic will be moved to dedicated methods in &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt;, making the codebase easier to maintain and test.&lt;br /&gt;
** View Simplification&lt;br /&gt;
*** Currently, the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; view contains excessive business logic, violating SRP.&lt;br /&gt;
*** We will move this logic into helper methods or models, ensuring each component has a single responsibility and improving code readability.&lt;br /&gt;
&lt;br /&gt;
* Separation of Concerns (SoC)&lt;br /&gt;
** By relocating authorization logic out of the controller, we separate authentication from request handling.&lt;br /&gt;
** Adhering to SoC enhances readability and maintainability by allowing each part of the application to focus on its specific role.&lt;br /&gt;
* Don't Repeat Yourself (DRY)&lt;br /&gt;
** We will inspect the existing implementation for code duplication, particularly in the controller actions.&lt;br /&gt;
** Any repetitive code will be refactored or removed to reduce redundancy and improve clarity.&lt;br /&gt;
&lt;br /&gt;
* Model-View-Controller (MVC) Adherence&lt;br /&gt;
** Moving business logic from the view aligns with MVC best practices.&lt;br /&gt;
** By ensuring the view only presents data without processing it, we maintain clear boundaries between the model, view, and controller layers.&lt;br /&gt;
&lt;br /&gt;
* Dependency Inversion Principle (DIP)&lt;br /&gt;
** We plan to update class methods to instance methods, allowing the controller to depend on abstractions rather than concrete implementations.&lt;br /&gt;
** This change leverages Ruby's duck typing, providing flexibility and making the code easier to maintain and test.&lt;br /&gt;
&lt;br /&gt;
* Factory Pattern&lt;br /&gt;
** We will implement a factory pattern for the common processing of &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpBid&amp;lt;/code&amp;gt;, encapsulating object creation and promoting code reuse.&lt;br /&gt;
&lt;br /&gt;
* Law of Demeter (Principle of Least Knowledge)&lt;br /&gt;
** We'll delegate interactions appropriately to prevent tightly coupled code, especially when dealing with topics and teams.&lt;br /&gt;
** Clear and intentional naming during refactoring will enhance code clarity and maintainability.&lt;br /&gt;
&lt;br /&gt;
* Adapter Pattern&lt;br /&gt;
** To add messaging to bid states for users, we'll use the adapter pattern to allow different components to work together without modifying their existing code.&lt;br /&gt;
&lt;br /&gt;
== Files Modified/Added ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-1.-controllers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
review_bids_controller.rb&lt;br /&gt;
&lt;br /&gt;
signup_sheet_controller.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-2.-models&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
review_bid.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-3.-helpers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
&lt;br /&gt;
authorization_helper.rb&lt;br /&gt;
&lt;br /&gt;
Review_bids_helper.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;viii.-test-plan&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
The Excel spreadsheet below details our test plan for testing the code we changed or created to ensure code coverage. The process involved reviewing the code and determining all the possible scenarios that needed to be covered based on what the method was doing, then creating test cases accordingly.&lt;br /&gt;
&lt;br /&gt;
* [https://docs.google.com/spreadsheets/d/1iHzOdSwlTtxOtf5VsNdwOaCP8NzYMmiJ/edit?usp=drive_link Open Test Matrix Spreadsheet]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/review_bids_controller_spec.rb ReviewBidsController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/sign_up_sheet_controller_spec.rb SignUpSheetController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/models/review_bid_spec.rb ReviewBid model updated test cases]&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-1.-mentor&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Ed Gehringer&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-2.-members&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Members ===&lt;br /&gt;
&lt;br /&gt;
* Gavin Teague&lt;br /&gt;
* Janice Uwujaren&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xi.-links&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
Previous Wiki: [https://wiki.expertiza.ncsu.edu/index.php/CSC/ECE_517_Fall_2021_-_E2151._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;Link&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xii.-references&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/document/d/1LHVacylQmB15I6VcYUA5nuC7GRRU9szHzZhjlPcE-PA/edit?tab=t.0#heading=h.u0oycoj6e0ev &amp;lt;u&amp;gt;Project Instructions&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;appendix-a-uml-class-diagram-plantuml-code-for-review-bids&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Juwujar</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160975</id>
		<title>CSC/ECE 517 Fall 2024 - E2485. Allow reviewers to bid on what to review</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160975"/>
		<updated>2024-12-10T22:57:15Z</updated>

		<summary type="html">&lt;p&gt;Juwujar: /* Optimize ReviewBidsController#set_priority Action functionality */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
This project will fix the defects and improve the functionality in Expertiza of the Reviewer Bidding feature, a process where reviewers can bid for assignments they want to review. It will make the functionality smoother, more reliable, and align with Expertiza's collaborative learning objectives.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iii.-problem-statements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Problem Statements ==&lt;br /&gt;
&lt;br /&gt;
Assigning reviews to users on Expertiza involves a complex bidding algorithm that requires optimization before production deployment. Below are key areas we plan to address as per the project requirements:&lt;br /&gt;
* Code Refactoring and Best Practices&lt;br /&gt;
** '''Utilize Authentication Utilities''': Modify the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; function in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to use existing authentication utilities, enhancing code reusability and adhering to the Single Responsibility Principle (SRP) and Separation of Concerns (SoC).&lt;br /&gt;
** '''Eliminate Code Duplication''': Refactor &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; to comply with the Don't Repeat Yourself (DRY) principle.&lt;br /&gt;
** '''Rename Functions for Clarity''': Change &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; to better reflect its purpose.&lt;br /&gt;
** '''Refactor Methods in '''&amp;lt;code&amp;gt;review_bid.rb&amp;lt;/code&amp;gt;''':''': Convert class methods to instance methods or relocate them to helper classes for improved code organization.&lt;br /&gt;
* Functionality Enhancements&lt;br /&gt;
** '''Handle Missing Bids Gracefully''': Adjust the bidding algorithm to accommodate students who do not bid, allowing them to select from the remaining options.&lt;br /&gt;
** '''Support Both Individual and Team Assignments''': Ensure the code functions correctly for participant and team-based assignments when signing up for topics.&lt;br /&gt;
* User Interface Improvements&lt;br /&gt;
** '''Increase Transparency in the Bidding Process''': Display messages indicating how many students are eligible to bid, how many have submitted bids, and the deadline.&lt;br /&gt;
** '''Accurate Bid Status Indicators''': Ensure topic colors change correctly based on the number of outstanding bids, providing clear visual feedback to users.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Exhaustive Edge Case Testing''': Conduct thorough testing for edge cases not previously covered, adding additional test cases as needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iv.-design-goals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Design Goals ==&lt;br /&gt;
&lt;br /&gt;
While addressing the existing defects and enhancing the Reviewer Bidding feature, we plan to adhere to the following design guidelines:&lt;br /&gt;
&lt;br /&gt;
* Code Quality and Maintainability&lt;br /&gt;
** '''Improve Code Structure: '''Implement refactoring strategies to enhance code readability and maintainability, ensuring adherence to best practices like the DRY principle.&lt;br /&gt;
** '''Standardize Authentication Logic: '''Utilize a centralized authentication utility to promote consistency and reusability across the application.&lt;br /&gt;
* System Stability and Flexibility&lt;br /&gt;
** '''Enhance Algorithm Robustness: '''Modify the bidding algorithm to handle scenarios where students do not bid, ensuring the system remains stable under all conditions.&lt;br /&gt;
** '''Ensure Assignment Versatility: '''Design the system to seamlessly support both individual and team assignments without additional configuration.&lt;br /&gt;
* User Experience Enhancements&lt;br /&gt;
** '''Increase Bidding Transparency: '''Develop user interface elements that clearly display bidding status, deadlines, and participation metrics.&lt;br /&gt;
** '''Implement Dynamic Visual Feedback: '''Use real-time visual cues, such as color changes, to inform users about the bidding status instantly.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Develop Comprehensive Test Suites: '''Create extensive automated tests covering edge cases and critical functionalities to guarantee reliable system behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;v.-uml-diagram&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== UML Diagram ==&lt;br /&gt;
&lt;br /&gt;
Below is a visual representation of the models associated with the review bids functionality, depicting the data structures and their relationships.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml class diagram2.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi.-implementation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
'''ReviewBid: '''The &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; manages the bidding process, where participants indicate their preferences for reviewing different topics or assignments. This helps distribute review tasks fairly based on participants' interests.&lt;br /&gt;
&lt;br /&gt;
'''Assignment: '''The &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model represents team or individual assignments in Expertiza. It stores all the essential information required to keep the assignment process organized, like managing participants, setting due dates, forming teams, and overseeing the review process.&lt;br /&gt;
&lt;br /&gt;
'''SignUpTopic: '''The &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt; model identifies specific topics or areas within an assignment for which participants can sign up. It manages the availability of these topics, tracks bids, coordinates team sign-ups, and assigns topics based on both bids and participant availability. &lt;br /&gt;
&lt;br /&gt;
'''Participant: '''The &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model is about the individual users who participate in an assignment. This includes students, instructors, teaching assistants, or other relevant roles. This model is responsible for managing key user information, monitoring interactions with the assignment, keeping track of team memberships, and recording things like badges and review grades.&lt;br /&gt;
&lt;br /&gt;
== Implementation ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1.-decoupling-authorization-logic-from-reviewbidscontroller&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Decoupling Authorization Logic from &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| '''&amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; needs to use the authentication utilities'''&lt;br /&gt;
&lt;br /&gt;
The function &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; should use the available authentication utilities to support the Single Responsibility Principle and Separation of Concerns and to support code reusability.&lt;br /&gt;
&lt;br /&gt;
| '''Authentication Consistency:''' Remove authentication logic in the controller and call logic in the authentication utility class instead to ensure consistent use of the same logic across the application, foster reusability, and eliminate DRY.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
==== Authorization Flow Before Refactoring ====&lt;br /&gt;
The sequence diagram depicts the authorization process within the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;. When a user invokes an action, the controller checks if the action is allowed by verifying the user's role through the &amp;lt;code&amp;gt;current_role_name()&amp;lt;/code&amp;gt; method in the &amp;lt;code&amp;gt;ApplicationController&amp;lt;/code&amp;gt;. Depending on the action and the user's role, the controller either authorizes the user to proceed or denies access, ultimately executing the action logic if authorized and responding to the user.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 1.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We simplified the &amp;lt;code&amp;gt;action_allowed?&amp;lt;/code&amp;gt; method by decoupling authorization logic from the controller to use helper methods in the &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt; module. The &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; should not handle authentication, which is a violation of SRP. &lt;br /&gt;
&lt;br /&gt;
Note: We also made similar updates to the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; methods for &amp;lt;code&amp;gt;SignUpController&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;vertical-align:top;&amp;quot; | Original Code !! style=&amp;quot;vertical-align:top;&amp;quot; | Refactored Code&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index'&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator',&lt;br /&gt;
     'Student'].include? current_role_name and&lt;br /&gt;
        ((%w[list].include? action_name) ? are_needed_authorizations_present?(params[:id], &amp;quot;participant&amp;quot;, &amp;quot;reader&amp;quot;, &amp;quot;submitter&amp;quot;, &amp;quot;reviewer&amp;quot;) : true)&lt;br /&gt;
  else&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator'].include? current_role_name&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index', 'list'&lt;br /&gt;
    current_user_has_student_privileges? &amp;amp;&amp;amp; list_authorization_check&lt;br /&gt;
  else&lt;br /&gt;
    current_user_has_ta_privileges?&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; action ===&lt;br /&gt;
Previously, the &amp;lt;code&amp;gt;ReviewBidsController's index&amp;lt;/code&amp;gt; action was overloaded with responsibilities—it handled everything from locating and validating assignments to fetching review mappings and counting completed reviews. This all-in-one approach made the controller action messy. &lt;br /&gt;
&lt;br /&gt;
We have moved the business logic from the controller action and into dedicated service objects. With this change, we introduced two key service objects: &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; focuses on retrieving the participant along with their assignments, while the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; takes on the task of managing review mappings and calculating review-related stats, like how many reviews have been completed. &lt;br /&gt;
&lt;br /&gt;
This separation of responsibilities aligns with the Single Responsibility Principle. The logic for fetching assignments, retrieving review mappings, and counting reviews is now more flexible and reusable throughout the application. For example, we used methods in the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; in both the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; and the &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;, helping to avoid code duplication.The article https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial served as a guide on when and how to use service objects in Rails.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
  return unless current_user_id?(@participant.user_id)&lt;br /&gt;
  @assignment = @participant.assignment&lt;br /&gt;
  @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
  # Finding how many reviews have been completed&lt;br /&gt;
  @num_reviews_completed = 0&lt;br /&gt;
  @review_mappings.each do |map|&lt;br /&gt;
    @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # render view for completing reviews after review bidding has been completed&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @assignment = participant_service.assignment&lt;br /&gt;
  unless @assignment.is_a?(Assignment)&lt;br /&gt;
    flash[:error] = 'Assignment not found.'&lt;br /&gt;
    redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  @review_mappings = review_service.review_mappings&lt;br /&gt;
  @num_reviews_completed = review_service.review_counts[:completed]&lt;br /&gt;
&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Optimize &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Action functionality ===&lt;br /&gt;
In the earlier version of the set_priority action in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, the server only needed a couple of key pieces of information: an array of topic IDs (params[:topic]) and the participant ID (params[:id}). From there, it would figure out the assignment_id by using the first topic as a reference. &lt;br /&gt;
&lt;br /&gt;
With the new implementation, the action now expects clear and structured parameters, including participant_id, an array of topic IDs, and assignment_id. This change ensures that the server always receives complete and reliable information, preventing issues caused by missing or inconsistent data. It also removes the need for the server to perform unnecessary lookups, allowing the controller to focus on its main job: validating inputs (like making sure the topics in params[:topic] belong to the same assignment as the participant), updating the database, handling errors properly, and sending the correct response back to the view.&lt;br /&gt;
&lt;br /&gt;
While these updates have cleaned up the set_priority action, the action still contains a lot of business logic, like validations and priority updates. To make the code even more maintainable, there’s room for further refactoring—perhaps moving some of this logic into a service class or model method to streamline the controller even more.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | Old Code&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    if params[:topic].nil?&lt;br /&gt;
      ReviewBid.where(participant_id: params[:id]).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      participant = AssignmentParticipant.find_by(id: params[:id])&lt;br /&gt;
      assignment_id = SignUpTopic.find(params[:topic].first).assignment.id&lt;br /&gt;
      @bids = ReviewBid.where(participant_id: params[:id])&lt;br /&gt;
      signed_up_topics = ReviewBid.where(participant_id: params[:id]).map(&amp;amp;:signuptopic_id)&lt;br /&gt;
      signed_up_topics -= params[:topic].map(&amp;amp;:to_i)&lt;br /&gt;
      signed_up_topics.each do |topic|&lt;br /&gt;
        ReviewBid.where(signuptopic_id: topic, participant_id: params[:id]).destroy_all&lt;br /&gt;
      end&lt;br /&gt;
      params[:topic].each_with_index do |topic_id, index|&lt;br /&gt;
        bid_existence = ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id])&lt;br /&gt;
        if bid_existence.empty?&lt;br /&gt;
          ReviewBid.create(priority: index + 1, signuptopic_id: topic_id, participant_id: params[:id], assignment_id: assignment_id)&lt;br /&gt;
        else&lt;br /&gt;
          ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id]).update_all(priority: index + 1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to action: 'show', assignment_id: params[:assignment_id], id: params[:id]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    participant_id = params[:participant_id].to_i&lt;br /&gt;
    selected_topic_ids = params[:topic]&amp;amp;.map(&amp;amp;:to_i) || []&lt;br /&gt;
    assignment_id = params[:assignment_id].to_i&lt;br /&gt;
&lt;br /&gt;
    unless current_user_can_modify_participant?(participant_id)&lt;br /&gt;
      flash[:error] = 'You are not authorized to modify these bids.'&lt;br /&gt;
      respond_to do |format|&lt;br /&gt;
        format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
        format.json { render json: { status: 'unauthorized' }, status: :unauthorized }&lt;br /&gt;
      end&lt;br /&gt;
      return&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    if selected_topic_ids.empty?&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      assignment = Assignment.find_by(id: assignment_id)&lt;br /&gt;
      if assignment.nil?&lt;br /&gt;
        flash[:error] = &amp;quot;Invalid assignment.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_assignment' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      unless SignUpTopic.where(id: selected_topic_ids, assignment_id: assignment_id).count == selected_topic_ids.size&lt;br /&gt;
        flash[:error] = &amp;quot;One or more selected topics are invalid.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_topics' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).where.not(signuptopic_id: selected_topic_ids).destroy_all&lt;br /&gt;
&lt;br /&gt;
      selected_topic_ids.each_with_index do |topic_id, index|&lt;br /&gt;
        bid = ReviewBid.find_or_initialize_by(signuptopic_id: topic_id, participant_id: participant_id)&lt;br /&gt;
        bid.priority = index + 1&lt;br /&gt;
        bid.assignment_id = assignment_id&lt;br /&gt;
        bid.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_to action: 'show', assignment_id: assignment_id, id: participant_id, notice: 'Review bids updated successfully.' }&lt;br /&gt;
      format.json { render json: { status: 'success' }, status: :ok }&lt;br /&gt;
    end&lt;br /&gt;
  rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
    flash[:error] = &amp;quot;Failed to update priorities: #{e.message}&amp;quot;&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
      format.json { render json: { status: 'error', message: e.message }, status: :unprocessable_entity }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Client Side Changes to Support &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Changes ====&lt;br /&gt;
After tidying up the server-side logic, we needed to refactor the client-side code to ensure it passed the correct information from the view to the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action. On the client side, we now grab the topicIDs and send them as &amp;lt;code&amp;gt;selected_topic_ids&amp;lt;/code&amp;gt;, allowing the controller to figure out which topics were selected and in what order.  &lt;br /&gt;
&lt;br /&gt;
We also extracted the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, making it a lot easier to send data with AJAX and keeping things flexible. When working with jQuery, AJAX is awesome because it lets us update the server without reloading the page, making the user experience smooth and responsive. When we pull the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, we can grab it along with other data, like the list of topics, and include it in the request. This way, we don’t have to rely on the URL to know which participant we’re working with, which makes the code cleaner and more adaptable.&lt;br /&gt;
&lt;br /&gt;
If we were to rely on the route (as opposed to using the DOM) for the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt;, every AJAX request would need to be tied to a specific URL structure. That’s limiting, especially if the route doesn’t naturally include the participant ID, as can be the case with collection routes versus member routes in Rails. &lt;br /&gt;
&lt;br /&gt;
Note: One thing that was interesting is the mix of JavaScript and CoffeeScript code. It wasn't clear if we are keeping both and refactoring to simple JavaScript (or possibly TypeScript in the future).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery -&amp;gt;&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: -&amp;gt;&lt;br /&gt;
      $.post($(this).data('update-url'), $(this).sortable('serialize'))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery(document).ready ($) -&amp;gt;&lt;br /&gt;
  sendUpdatedOrder = -&amp;gt;&lt;br /&gt;
    sortedIDs = $(this).sortable('toArray')&lt;br /&gt;
&lt;br /&gt;
    topicIDs = sortedIDs.map (id) -&amp;gt;&lt;br /&gt;
      id.replace 'topic_', ''&lt;br /&gt;
&lt;br /&gt;
    participant_id = $(this).data('participant-id')&lt;br /&gt;
&lt;br /&gt;
    assignment_id = new URL($(this).data('update-url'), window.location.origin).searchParams.get('assignment_id')&lt;br /&gt;
&lt;br /&gt;
    $.ajax&lt;br /&gt;
      type: 'POST'&lt;br /&gt;
      url: $(this).data('update-url')&lt;br /&gt;
      data:&lt;br /&gt;
        topic: topicIDs&lt;br /&gt;
        participant_id: participant_id&lt;br /&gt;
        assignment_id: assignment_id&lt;br /&gt;
      success: (response) -&amp;gt;&lt;br /&gt;
        console.log 'Selections updated successfully.'&lt;br /&gt;
      error: (xhr, status, error) -&amp;gt;&lt;br /&gt;
        alert 'An error occurred while saving your selections. Please try again.'&lt;br /&gt;
        console.error error&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: sendUpdatedOrder&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-1.-signupsheetcontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; Updates ===&lt;br /&gt;
&lt;br /&gt;
The ReviewBidsController's index method renders the review_bids_others_work action in &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt;. However, the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; does not include the controller action for this view. We need to find the old implementation of the controller action, add it back, and review and update it for any DRY violations.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # provides variables for reviewing page located at views/review_bids/others_work.html.erb&lt;br /&gt;
  def index&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
    return unless current_user_id?(@participant.user_id)&lt;br /&gt;
&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
    # Finding how many reviews have been completed&lt;br /&gt;
    @num_reviews_completed = 0&lt;br /&gt;
    @review_mappings.each do |map|&lt;br /&gt;
      @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # render view for completing reviews after review bidding has been completed&lt;br /&gt;
    render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
  end  &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-2.-reviewbidsotherswork-view-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;review_bids_others_work.html.erb&amp;lt;/code&amp;gt; View Updates ====&lt;br /&gt;
&lt;br /&gt;
The code only contains a view with this name and no associated controller action. Reviewing the code in the view, we noticed some SRP violations where there is much logic in the presentation layer. Set up helper methods in the controller to handle some of that logic or see if the model can handle some of that logic.&lt;br /&gt;
&lt;br /&gt;
Note: The view is technically part of the SignUpSheetController, but it's being rendered by the ReviewBidsController, which initially threw us off. After chatting with the professor, we decided not to change this view. He indicated that the plan is to reuse these views for both assignment bidding and review bidding.&lt;br /&gt;
&lt;br /&gt;
We would suggest building the assignment bidding and review bidding features separately first to make sure each one works on its own. Once that’s done, we can go back and clean things up by combining any shared functionality. This could include creating shared views or even introducing an abstract model to handle both types of bidding in a cleaner way. It could also mean replacing the ReviewBidsController with something more generic, like a BidsController, to keep things reusable and easier to maintain down the line.&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Rename &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; method ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; should be &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt;&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:''' Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The primary activity here is to refactor the method name &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; and fix the hardcoded URL. However, the match topic bidding algorithm no longer works since Heroku is no longer free, per the professor. The reference link below has detailed information about the web service that we can leverage to gain that understanding.&lt;br /&gt;
&lt;br /&gt;
Reference Link for Webservice Details: [https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Updated Implementation ====&lt;br /&gt;
The original requirement was to rename the run_bidding_algorithm method to assign_reviewers, but after digging into the code, we realized that renaming it didn’t make sense. The method runs the bidding algorithm to match topics, and there’s already a separate method to handle assigning review topics.&lt;br /&gt;
&lt;br /&gt;
However, we improved the logic in the assign_bidding method (which is called by the run_bidding_algorithm) by introducing a separate BiddingAlgorithmService object. This keeps the business logic out of the controller action, which makes things cleaner and more maintainable.&lt;br /&gt;
&lt;br /&gt;
==== review_bids_controller ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def run_bidding_algorithm&lt;br /&gt;
    review_bid = ReviewBid.new&lt;br /&gt;
    bidding_data = review_bid.bidding_data&lt;br /&gt;
    matched_topics = BiddingAlgorithmService.new(bidding_data).run&lt;br /&gt;
    raise ArgumentError, 'Failed to assign reviewers. Please try again later.' unless matched_topics&lt;br /&gt;
&lt;br /&gt;
    matched_topics&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== bidding_algorithm_service ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# frozen_string_literal: true&lt;br /&gt;
&lt;br /&gt;
# The `BiddingAlgorithmService` run the bid assignment algorithm&lt;br /&gt;
# Sends student IDs, topic IDs, student preferences, and timestamps to the web service&lt;br /&gt;
# The web service returns the matched assignments in the JSON response body&lt;br /&gt;
class BiddingAlgorithmService&lt;br /&gt;
  SERVICE_URL = 'http://app-csc517.herokuapp.com/match_topics'.freeze&lt;br /&gt;
&lt;br /&gt;
  # MOCK_DATA is based on the documentation detailing how the webservice behaves.&lt;br /&gt;
  # Reference: https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review#Webservice&lt;br /&gt;
  MOCK_DATA = {&lt;br /&gt;
    36239 =&amp;gt; [3970, 3972, 3975],&lt;br /&gt;
    36240 =&amp;gt; [3973, 3974, 3972],&lt;br /&gt;
    36241 =&amp;gt; [3969, 3971, 3972],&lt;br /&gt;
    36242 =&amp;gt; [3969, 3971, 3973],&lt;br /&gt;
    36243 =&amp;gt; [3969, 3970, 3971]&lt;br /&gt;
  }.freeze&lt;br /&gt;
&lt;br /&gt;
  def initialize(bidding_data)&lt;br /&gt;
    @bidding_data = bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def run&lt;br /&gt;
    return MOCK_DATA if Rails.application.config.use_mock_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
    perform_request&lt;br /&gt;
  rescue RestClient::ExceptionWithResponse, JSON::ParserError&lt;br /&gt;
    false&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def perform_request&lt;br /&gt;
    response = RestClient.post(&lt;br /&gt;
      SERVICE_URL,&lt;br /&gt;
      @bidding_data.to_json,&lt;br /&gt;
      content_type: :json,&lt;br /&gt;
      accept: :json&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    validate_response(response)&lt;br /&gt;
    JSON.parse(response.body)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def validate_response(response)&lt;br /&gt;
    raise 'Invalid response format' unless response.headers[:content_type] &amp;amp;&amp;amp; response.headers[:content_type].include?('application/json')&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Resolve Functionality Issues for Non Bidders ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Currently it doesn't work if some student does not bid. In this case, algorithm needs to be fixed to ignore anyone who didn’t bid, and let them choose from what’s left over.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to do several things within this controller. We will set a default state for non-bidders that we can point to to give them options to select topics to review from the leftovers. We will consolidate &amp;lt;code&amp;gt;@sign_up_topics&amp;lt;/code&amp;gt; to resolve the DRY violation. Lastly, we will remove business logic from the show method and push it to a new helper to conform to the SRP.&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # Additional method&lt;br /&gt;
&lt;br /&gt;
  # Provides variables for review bidding page&lt;br /&gt;
  def show&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id].to_i)&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @sign_up_topics = SignUpTopic.where(assignment_id: @assignment.id, private_to: nil)&lt;br /&gt;
    my_topic = SignedUpTeam.topic_id(@participant.parent_id, @participant.user_id)&lt;br /&gt;
    @sign_up_topics -= SignUpTopic.where(assignment_id: @assignment.id, id: my_topic)&lt;br /&gt;
    @num_participants = AssignmentParticipant.where(parent_id: @assignment.id).count&lt;br /&gt;
    @selected_topics = nil # This is used to list the topics assigned to review (i.e., select == assigned)&lt;br /&gt;
    @bids = ReviewBid.where(participant_id: @participant, assignment_id: @assignment.id)&lt;br /&gt;
    signed_up_topics = []&lt;br /&gt;
    &lt;br /&gt;
    @bids.each do |bid|&lt;br /&gt;
      sign_up_topic = SignUpTopic.find_by(id: bid.signuptopic_id)&lt;br /&gt;
      signed_up_topics &amp;lt;&amp;lt; sign_up_topic if sign_up_topic&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    signed_up_topics &amp;amp;= @sign_up_topics&lt;br /&gt;
    @sign_up_topics -= signed_up_topics&lt;br /&gt;
    @bids = signed_up_topics&lt;br /&gt;
    @num_of_topics = @sign_up_topics.size&lt;br /&gt;
    @assigned_review_maps = []&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: @assignment.id, reviewer_id: @participant.id).each do |review_map|&lt;br /&gt;
      @assigned_review_maps &amp;lt;&amp;lt; review_map&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Explicitly render view since it's in the sign up sheet views&lt;br /&gt;
    render 'sign_up_sheet/review_bids_show'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Additional methods&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-2.-reviewbidshelper-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
 # Assigns bidding topics to reviewers&lt;br /&gt;
  def assign_bidding&lt;br /&gt;
      assignment = validate_assignment(params[:assignment_id])&lt;br /&gt;
&lt;br /&gt;
      reviewers = validate_reviewers(assignment.id)&lt;br /&gt;
      reviewer_ids = reviewers.map(&amp;amp;:id)&lt;br /&gt;
&lt;br /&gt;
      matched_topics = run_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
      if matched_topics.blank?&lt;br /&gt;
        flash[:alert] = 'Topic or assignment is missing'&lt;br /&gt;
        redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      leftover_topics = find_leftover_topics(assignment.id, matched_topics)&lt;br /&gt;
      assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.new.assign_review_topics(matched_topics)&lt;br /&gt;
      assignment.update!(can_choose_topic_to_review: false)&lt;br /&gt;
&lt;br /&gt;
      flash[:notice] = 'Reviewers were successfully assigned to topics.'&lt;br /&gt;
      redirect_back fallback_location: root_path&lt;br /&gt;
    rescue ArgumentError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ArgumentError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: e.message&lt;br /&gt;
    rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::RecordInvalid: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue ActiveRecord::ActiveRecordError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::ActiveRecordError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue StandardError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;StandardError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers. Please try again later.'&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  #service methods here ...&lt;br /&gt;
&lt;br /&gt;
  def find_leftover_topics(assignment_id, matched_topics)&lt;br /&gt;
    all_topic_ids = SignUpTopic.where(assignment_id: assignment_id).pluck(:id)&lt;br /&gt;
    assigned_topic_ids = matched_topics.map { |match| match[:topic_id] }&lt;br /&gt;
&lt;br /&gt;
    # Calculate leftover topics&lt;br /&gt;
    all_topic_ids - assigned_topic_ids&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
    return if leftover_topics.blank?&lt;br /&gt;
&lt;br /&gt;
    # Find non-bidders by excluding already matched reviewers&lt;br /&gt;
    non_bidders = reviewer_ids - matched_topics.keys&lt;br /&gt;
&lt;br /&gt;
    # Assign leftover topics to non-bidders in a round-robin fashion&lt;br /&gt;
    non_bidders.each_with_index do |reviewer_id, index|&lt;br /&gt;
      topic_id = leftover_topics[index % leftover_topics.length]&lt;br /&gt;
      ReviewBid.create(priority: 1, signuptopic_id: topic_id, participant_id: reviewer_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirm Functionality for Single-person Teams ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Make sure your code works on individual assignments, i.e., assignments where participants sign up for topics instead of teams. In this case, a team is created for each participant when they sign up. So the code should work for assignments to which either individuals or teams submit.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to make an update to focus on topic rather than team or have it where teams are updated based on topic id.We will probably utilize sign up topic or a similar object to accomplish this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-5-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bid Status Messaging ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| To make the functionality more intuitive, include a message to say how many students are eligible to submit bids, how many have submitted their bids, when the deadline for submitting bids is.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
The logic for these messages will go into the helper file. It is effectively the same reasoning as our migration of other business/functionality code from the controller to the helper to conform to the SRP. It will go hand in hand with the SignUpTopic portion of the code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-6-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Why are the methods in review_bid.rb class methods? Can we change them to instance methods or move it to helpers?&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-1.-reviewbid-model-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model Updates ====&lt;br /&gt;
Since the current class methods are tied to &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model instances, we will change them to instance methods. There is no need to move them to helpers.&lt;br /&gt;
&lt;br /&gt;
===== Review Bid Model Flow Before Refactoring =====&lt;br /&gt;
This sequence diagram describes how the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; interacts with the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model in assigning review topics to reviewers according to their bids. Once the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; action is initiated by the user, the controller gathers the IDs of reviewers and calls &amp;lt;code&amp;gt;ReviewBid.bidding_data&amp;lt;/code&amp;gt; to gather bidding information. That is sent out to an external bidding algorithm, and the matched topics that come back are used by &amp;lt;code&amp;gt;ReviewBid.assign_review_topics&amp;lt;/code&amp;gt; to create &amp;lt;code&amp;gt;ReviewResponseMap&amp;lt;/code&amp;gt; entries, assigning topics to reviewers for the assignment.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
  belongs_to :assignment, class_name: 'Assignment'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  def self.bidding_data(assignment_id, reviewer_ids)&lt;br /&gt;
    # create basic hash and set basic hash data&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'users' =&amp;gt; {}, 'max_accepted_proposals' =&amp;gt; [] }&lt;br /&gt;
    bidding_data['tid'] = SignUpTopic.where(assignment_id: assignment_id).ids&lt;br /&gt;
    bidding_data['max_accepted_proposals'] = Assignment.where(id: assignment_id).pluck(:num_reviews_allowed).first&lt;br /&gt;
&lt;br /&gt;
    # loop through reviewer_ids to get reviewer specific bidding data&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      bidding_data['users'][reviewer_id] = reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # assigns topics to reviews as matched by the webservice algorithm&lt;br /&gt;
  def self.assign_review_topics(assignment_id, reviewer_ids, matched_topics, _min_num_reviews = 2)&lt;br /&gt;
    # if review response map already created, delete it&lt;br /&gt;
    if ReviewResponseMap.where(reviewed_object_id: assignment_id)&lt;br /&gt;
      ReviewResponseMap.where(reviewed_object_id: assignment_id).destroy_all&lt;br /&gt;
    end&lt;br /&gt;
    # loop through reviewer_ids to assign reviews to each reviewer&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      topics_to_assign = matched_topics[reviewer_id.to_s]&lt;br /&gt;
      topics_to_assign.each do |topic|&lt;br /&gt;
        assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method to assign a single topic to a reviewer&lt;br /&gt;
  def self.assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic).pluck(:team_id).first&lt;br /&gt;
    team_to_review.nil? ? [] : ReviewResponseMap.create(reviewed_object_id: assignment_id, reviewer_id: reviewer_id, reviewee_id: team_to_review, type: 'ReviewResponseMap')&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method for getting individual reviewer_ids bidding data&lt;br /&gt;
  # returns user's bidding data hash&lt;br /&gt;
  def self.reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    reviewer_user_id = AssignmentParticipant.find(reviewer_id).user_id&lt;br /&gt;
    self_topic = SignedUpTeam.topic_id(assignment_id, reviewer_user_id)&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'otid' =&amp;gt; self_topic, 'priority' =&amp;gt; [], 'time' =&amp;gt; [] }&lt;br /&gt;
    bids = ReviewBid.where(participant_id: reviewer_id)&lt;br /&gt;
&lt;br /&gt;
    # loop through each bid for a topic to get specific data&lt;br /&gt;
    bids.each do |bid|&lt;br /&gt;
      bidding_data['tid'] &amp;lt;&amp;lt; bid.signuptopic_id&lt;br /&gt;
      bidding_data['priority'] &amp;lt;&amp;lt; bid.priority&lt;br /&gt;
      bidding_data['time'] &amp;lt;&amp;lt; bid.updated_at&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  # Instance method to get bidding data for this review bid&lt;br /&gt;
  def bidding_data&lt;br /&gt;
    {&lt;br /&gt;
      'tid' =&amp;gt; topic_ids,&lt;br /&gt;
      'otid' =&amp;gt; self_topic,&lt;br /&gt;
      'priority' =&amp;gt; priorities,&lt;br /&gt;
      'time' =&amp;gt; updated_times&lt;br /&gt;
    }&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # bid object to limit db touches for similar data acquisitions&lt;br /&gt;
  def bids&lt;br /&gt;
    @bids ||= ReviewBid.where(participant_id: participant_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def topic_ids&lt;br /&gt;
    bids.pluck(:signuptopic_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def priorities&lt;br /&gt;
    bids.pluck(:priority)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def updated_times&lt;br /&gt;
    bids.pluck(:updated_at)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self_topic&lt;br /&gt;
    return nil unless topic &amp;amp;&amp;amp; topic.assignment_id&lt;br /&gt;
&lt;br /&gt;
    SignedUpTeam.topic_id(topic.assignment_id, participant.user_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign topics to reviews&lt;br /&gt;
  def assign_review_topics(matched_topics)&lt;br /&gt;
    validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: topic.assignment_id).destroy_all&lt;br /&gt;
&lt;br /&gt;
    matched_topics.each do |reviewer_id, topics|&lt;br /&gt;
      Array(topics).each { |topic_id| assign_topic_to_reviewer(reviewer_id, topic_id) }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign a single topic to a reviewer&lt;br /&gt;
  def assign_topic_to_reviewer(reviewer_id, topic_id)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic_id).pluck(:team_id).first&lt;br /&gt;
    return if team_to_review.nil?&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.create(&lt;br /&gt;
      reviewed_object_id: topic.assignment_id,&lt;br /&gt;
      reviewer_id: reviewer_id,&lt;br /&gt;
      reviewee_id: team_to_review,&lt;br /&gt;
      type: 'ReviewResponseMap'&lt;br /&gt;
    )&lt;br /&gt;
    # error handling&lt;br /&gt;
  rescue StandardError =&amp;gt; e&lt;br /&gt;
    Rails.logger.error(&amp;quot;Failed to assign topic #{topic_id} to reviewer #{reviewer_id}: #{e.message}&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
    raise ArgumentError, 'Topic or assignment is missing' if topic.nil? || topic.assignment_id.nil?&lt;br /&gt;
    raise ArgumentError, 'Matched topics must be a Hash' unless matched_topics.is_a?(Hash)&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
Once we change them to instance methods, we’ll need to modify their use in the code to instantiate the object first.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;Placeholder for implementation screenshots (before and after)&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-3.-schema.db-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;Schema.db&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We also need to remove the foreign key reference to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model since the &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model already has access to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;, and that is the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; we should be referencing. This will also involve modifying the database to remove the foreign key reference to the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Steps required:&lt;br /&gt;
&lt;br /&gt;
# Review the records in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table in the database to see if there are any records and if those records have values for &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;&lt;br /&gt;
# Create a migration file using the command: &amp;lt;code&amp;gt;rails generate migration RemoveAssignmentFromReviewBids&amp;lt;/code&amp;gt;&lt;br /&gt;
# In the generated file, indicate the change that needs to be made using the change method and migration columns &amp;lt;code&amp;gt;remove_foreign_key&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;remove_column&amp;lt;/code&amp;gt;. Migration should be straightforward since there is no cascading behavior (i.e., &amp;lt;code&amp;gt;dependent:destroy&amp;lt;/code&amp;gt;) to worry about.&lt;br /&gt;
# Once comfortable with the updates to the generated file, run the migration using the &amp;lt;code&amp;gt;rails db:migrate&amp;lt;/code&amp;gt; command.&lt;br /&gt;
# Run a query in the DB to ensure no orphaned records are in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table after the updates.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;Placeholder for implementation screenshots (before and after)&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-4.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implement Edge Case Testing ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| In the previous implementation wiki, there are edge cases which are not exhaustively tested. Should test those edge cases thoroughly and add more edge case testing - link&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span class=&amp;quot;mark&amp;quot;&amp;gt;TODO&amp;lt;/span&amp;gt;:We just got initial test results back. We still need to figure out why tests are failing and what may be slipping through the cracks.[[File:Test expertiza.png|411x459px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-8-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design Patterns and Principles ===&lt;br /&gt;
Below are the design patterns and principles we plan to use to address the problem statements outlined in the project requirements:&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
** Controller Responsibility&lt;br /&gt;
*** To address the issue of authentication logic residing in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, we will refactor the code to ensure the controller solely handles request and response flows.&lt;br /&gt;
*** Authentication logic will be moved to dedicated methods in &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt;, making the codebase easier to maintain and test.&lt;br /&gt;
** View Simplification&lt;br /&gt;
*** Currently, the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; view contains excessive business logic, violating SRP.&lt;br /&gt;
*** We will move this logic into helper methods or models, ensuring each component has a single responsibility and improving code readability.&lt;br /&gt;
&lt;br /&gt;
* Separation of Concerns (SoC)&lt;br /&gt;
** By relocating authorization logic out of the controller, we separate authentication from request handling.&lt;br /&gt;
** Adhering to SoC enhances readability and maintainability by allowing each part of the application to focus on its specific role.&lt;br /&gt;
* Don't Repeat Yourself (DRY)&lt;br /&gt;
** We will inspect the existing implementation for code duplication, particularly in the controller actions.&lt;br /&gt;
** Any repetitive code will be refactored or removed to reduce redundancy and improve clarity.&lt;br /&gt;
&lt;br /&gt;
* Model-View-Controller (MVC) Adherence&lt;br /&gt;
** Moving business logic from the view aligns with MVC best practices.&lt;br /&gt;
** By ensuring the view only presents data without processing it, we maintain clear boundaries between the model, view, and controller layers.&lt;br /&gt;
&lt;br /&gt;
* Dependency Inversion Principle (DIP)&lt;br /&gt;
** We plan to update class methods to instance methods, allowing the controller to depend on abstractions rather than concrete implementations.&lt;br /&gt;
** This change leverages Ruby's duck typing, providing flexibility and making the code easier to maintain and test.&lt;br /&gt;
&lt;br /&gt;
* Factory Pattern&lt;br /&gt;
** We will implement a factory pattern for the common processing of &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpBid&amp;lt;/code&amp;gt;, encapsulating object creation and promoting code reuse.&lt;br /&gt;
&lt;br /&gt;
* Law of Demeter (Principle of Least Knowledge)&lt;br /&gt;
** We'll delegate interactions appropriately to prevent tightly coupled code, especially when dealing with topics and teams.&lt;br /&gt;
** Clear and intentional naming during refactoring will enhance code clarity and maintainability.&lt;br /&gt;
&lt;br /&gt;
* Adapter Pattern&lt;br /&gt;
** To add messaging to bid states for users, we'll use the adapter pattern to allow different components to work together without modifying their existing code.&lt;br /&gt;
&lt;br /&gt;
== Files Modified/Added ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-1.-controllers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
review_bids_controller.rb&lt;br /&gt;
&lt;br /&gt;
signup_sheet_controller.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-2.-models&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
review_bid.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-3.-helpers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
&lt;br /&gt;
authorization_helper.rb&lt;br /&gt;
&lt;br /&gt;
Review_bids_helper.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;viii.-test-plan&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
The Excel spreadsheet below details our test plan for testing the code we changed or created to ensure code coverage. The process involved reviewing the code and determining all the possible scenarios that needed to be covered based on what the method was doing, then creating test cases accordingly.&lt;br /&gt;
&lt;br /&gt;
* [https://docs.google.com/spreadsheets/d/1iHzOdSwlTtxOtf5VsNdwOaCP8NzYMmiJ/edit?usp=drive_link Open Test Matrix Spreadsheet]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/review_bids_controller_spec.rb ReviewBidsController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/sign_up_sheet_controller_spec.rb SignUpSheetController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/models/review_bid_spec.rb ReviewBid model updated test cases]&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-1.-mentor&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Ed Gehringer&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-2.-members&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Members ===&lt;br /&gt;
&lt;br /&gt;
* Gavin Teague&lt;br /&gt;
* Janice Uwujaren&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xi.-links&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
Previous Wiki: [https://wiki.expertiza.ncsu.edu/index.php/CSC/ECE_517_Fall_2021_-_E2151._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;Link&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xii.-references&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/document/d/1LHVacylQmB15I6VcYUA5nuC7GRRU9szHzZhjlPcE-PA/edit?tab=t.0#heading=h.u0oycoj6e0ev &amp;lt;u&amp;gt;Project Instructions&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;appendix-a-uml-class-diagram-plantuml-code-for-review-bids&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Juwujar</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160974</id>
		<title>CSC/ECE 517 Fall 2024 - E2485. Allow reviewers to bid on what to review</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160974"/>
		<updated>2024-12-10T22:55:30Z</updated>

		<summary type="html">&lt;p&gt;Juwujar: /* ReviewBidsController Updates */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
This project will fix the defects and improve the functionality in Expertiza of the Reviewer Bidding feature, a process where reviewers can bid for assignments they want to review. It will make the functionality smoother, more reliable, and align with Expertiza's collaborative learning objectives.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iii.-problem-statements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Problem Statements ==&lt;br /&gt;
&lt;br /&gt;
Assigning reviews to users on Expertiza involves a complex bidding algorithm that requires optimization before production deployment. Below are key areas we plan to address as per the project requirements:&lt;br /&gt;
* Code Refactoring and Best Practices&lt;br /&gt;
** '''Utilize Authentication Utilities''': Modify the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; function in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to use existing authentication utilities, enhancing code reusability and adhering to the Single Responsibility Principle (SRP) and Separation of Concerns (SoC).&lt;br /&gt;
** '''Eliminate Code Duplication''': Refactor &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; to comply with the Don't Repeat Yourself (DRY) principle.&lt;br /&gt;
** '''Rename Functions for Clarity''': Change &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; to better reflect its purpose.&lt;br /&gt;
** '''Refactor Methods in '''&amp;lt;code&amp;gt;review_bid.rb&amp;lt;/code&amp;gt;''':''': Convert class methods to instance methods or relocate them to helper classes for improved code organization.&lt;br /&gt;
* Functionality Enhancements&lt;br /&gt;
** '''Handle Missing Bids Gracefully''': Adjust the bidding algorithm to accommodate students who do not bid, allowing them to select from the remaining options.&lt;br /&gt;
** '''Support Both Individual and Team Assignments''': Ensure the code functions correctly for participant and team-based assignments when signing up for topics.&lt;br /&gt;
* User Interface Improvements&lt;br /&gt;
** '''Increase Transparency in the Bidding Process''': Display messages indicating how many students are eligible to bid, how many have submitted bids, and the deadline.&lt;br /&gt;
** '''Accurate Bid Status Indicators''': Ensure topic colors change correctly based on the number of outstanding bids, providing clear visual feedback to users.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Exhaustive Edge Case Testing''': Conduct thorough testing for edge cases not previously covered, adding additional test cases as needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iv.-design-goals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Design Goals ==&lt;br /&gt;
&lt;br /&gt;
While addressing the existing defects and enhancing the Reviewer Bidding feature, we plan to adhere to the following design guidelines:&lt;br /&gt;
&lt;br /&gt;
* Code Quality and Maintainability&lt;br /&gt;
** '''Improve Code Structure: '''Implement refactoring strategies to enhance code readability and maintainability, ensuring adherence to best practices like the DRY principle.&lt;br /&gt;
** '''Standardize Authentication Logic: '''Utilize a centralized authentication utility to promote consistency and reusability across the application.&lt;br /&gt;
* System Stability and Flexibility&lt;br /&gt;
** '''Enhance Algorithm Robustness: '''Modify the bidding algorithm to handle scenarios where students do not bid, ensuring the system remains stable under all conditions.&lt;br /&gt;
** '''Ensure Assignment Versatility: '''Design the system to seamlessly support both individual and team assignments without additional configuration.&lt;br /&gt;
* User Experience Enhancements&lt;br /&gt;
** '''Increase Bidding Transparency: '''Develop user interface elements that clearly display bidding status, deadlines, and participation metrics.&lt;br /&gt;
** '''Implement Dynamic Visual Feedback: '''Use real-time visual cues, such as color changes, to inform users about the bidding status instantly.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Develop Comprehensive Test Suites: '''Create extensive automated tests covering edge cases and critical functionalities to guarantee reliable system behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;v.-uml-diagram&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== UML Diagram ==&lt;br /&gt;
&lt;br /&gt;
Below is a visual representation of the models associated with the review bids functionality, depicting the data structures and their relationships.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml class diagram2.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi.-implementation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
'''ReviewBid: '''The &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; manages the bidding process, where participants indicate their preferences for reviewing different topics or assignments. This helps distribute review tasks fairly based on participants' interests.&lt;br /&gt;
&lt;br /&gt;
'''Assignment: '''The &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model represents team or individual assignments in Expertiza. It stores all the essential information required to keep the assignment process organized, like managing participants, setting due dates, forming teams, and overseeing the review process.&lt;br /&gt;
&lt;br /&gt;
'''SignUpTopic: '''The &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt; model identifies specific topics or areas within an assignment for which participants can sign up. It manages the availability of these topics, tracks bids, coordinates team sign-ups, and assigns topics based on both bids and participant availability. &lt;br /&gt;
&lt;br /&gt;
'''Participant: '''The &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model is about the individual users who participate in an assignment. This includes students, instructors, teaching assistants, or other relevant roles. This model is responsible for managing key user information, monitoring interactions with the assignment, keeping track of team memberships, and recording things like badges and review grades.&lt;br /&gt;
&lt;br /&gt;
== Implementation ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1.-decoupling-authorization-logic-from-reviewbidscontroller&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Decoupling Authorization Logic from &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| '''&amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; needs to use the authentication utilities'''&lt;br /&gt;
&lt;br /&gt;
The function &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; should use the available authentication utilities to support the Single Responsibility Principle and Separation of Concerns and to support code reusability.&lt;br /&gt;
&lt;br /&gt;
| '''Authentication Consistency:''' Remove authentication logic in the controller and call logic in the authentication utility class instead to ensure consistent use of the same logic across the application, foster reusability, and eliminate DRY.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
==== Authorization Flow Before Refactoring ====&lt;br /&gt;
The sequence diagram depicts the authorization process within the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;. When a user invokes an action, the controller checks if the action is allowed by verifying the user's role through the &amp;lt;code&amp;gt;current_role_name()&amp;lt;/code&amp;gt; method in the &amp;lt;code&amp;gt;ApplicationController&amp;lt;/code&amp;gt;. Depending on the action and the user's role, the controller either authorizes the user to proceed or denies access, ultimately executing the action logic if authorized and responding to the user.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 1.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We simplified the &amp;lt;code&amp;gt;action_allowed?&amp;lt;/code&amp;gt; method by decoupling authorization logic from the controller to use helper methods in the &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt; module. The &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; should not handle authentication, which is a violation of SRP. &lt;br /&gt;
&lt;br /&gt;
Note: We also made similar updates to the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; methods for &amp;lt;code&amp;gt;SignUpController&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;vertical-align:top;&amp;quot; | Original Code !! style=&amp;quot;vertical-align:top;&amp;quot; | Refactored Code&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index'&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator',&lt;br /&gt;
     'Student'].include? current_role_name and&lt;br /&gt;
        ((%w[list].include? action_name) ? are_needed_authorizations_present?(params[:id], &amp;quot;participant&amp;quot;, &amp;quot;reader&amp;quot;, &amp;quot;submitter&amp;quot;, &amp;quot;reviewer&amp;quot;) : true)&lt;br /&gt;
  else&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator'].include? current_role_name&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index', 'list'&lt;br /&gt;
    current_user_has_student_privileges? &amp;amp;&amp;amp; list_authorization_check&lt;br /&gt;
  else&lt;br /&gt;
    current_user_has_ta_privileges?&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; action ===&lt;br /&gt;
Previously, the &amp;lt;code&amp;gt;ReviewBidsController's index&amp;lt;/code&amp;gt; action was overloaded with responsibilities—it handled everything from locating and validating assignments to fetching review mappings and counting completed reviews. This all-in-one approach made the controller action messy. &lt;br /&gt;
&lt;br /&gt;
We have moved the business logic from the controller action and into dedicated service objects. With this change, we introduced two key service objects: &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; focuses on retrieving the participant along with their assignments, while the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; takes on the task of managing review mappings and calculating review-related stats, like how many reviews have been completed. &lt;br /&gt;
&lt;br /&gt;
This separation of responsibilities aligns with the Single Responsibility Principle. The logic for fetching assignments, retrieving review mappings, and counting reviews is now more flexible and reusable throughout the application. For example, we used methods in the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; in both the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; and the &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;, helping to avoid code duplication.The article https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial served as a guide on when and how to use service objects in Rails.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
  return unless current_user_id?(@participant.user_id)&lt;br /&gt;
  @assignment = @participant.assignment&lt;br /&gt;
  @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
  # Finding how many reviews have been completed&lt;br /&gt;
  @num_reviews_completed = 0&lt;br /&gt;
  @review_mappings.each do |map|&lt;br /&gt;
    @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # render view for completing reviews after review bidding has been completed&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @assignment = participant_service.assignment&lt;br /&gt;
  unless @assignment.is_a?(Assignment)&lt;br /&gt;
    flash[:error] = 'Assignment not found.'&lt;br /&gt;
    redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  @review_mappings = review_service.review_mappings&lt;br /&gt;
  @num_reviews_completed = review_service.review_counts[:completed]&lt;br /&gt;
&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Optimize &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Action functionality ===&lt;br /&gt;
In the earlier version of the set_priority action in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, the server only needed a couple of key pieces of information: an array of topic IDs (params[:topic]) and the participant ID (params[:id}). From there, it would figure out the assignment_id by using the first topic as a reference. While this worked in simple cases, it could lead to problems if any data was missing or didn’t line up correctly.&lt;br /&gt;
&lt;br /&gt;
With the new implementation, the action now expects clear and structured parameters, including participant_id, an array of topic IDs, and assignment_id. This change ensures that the server always receives complete and reliable information, preventing issues caused by missing or inconsistent data. It also removes the need for the server to perform unnecessary lookups, allowing the controller to focus on its main job: validating inputs (like making sure the topics in params[:topic] belong to the same assignment as the participant), updating the database, handling errors properly, and sending the correct response back to the view.&lt;br /&gt;
&lt;br /&gt;
While these updates have cleaned up the set_priority action, they still contain a lot of business logic, like validations and priority updates. To make the code even more maintainable, there’s room for further refactoring—perhaps moving some of this logic into a service class or model method to streamline the controller even more.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | Old Code&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    if params[:topic].nil?&lt;br /&gt;
      ReviewBid.where(participant_id: params[:id]).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      participant = AssignmentParticipant.find_by(id: params[:id])&lt;br /&gt;
      assignment_id = SignUpTopic.find(params[:topic].first).assignment.id&lt;br /&gt;
      @bids = ReviewBid.where(participant_id: params[:id])&lt;br /&gt;
      signed_up_topics = ReviewBid.where(participant_id: params[:id]).map(&amp;amp;:signuptopic_id)&lt;br /&gt;
      signed_up_topics -= params[:topic].map(&amp;amp;:to_i)&lt;br /&gt;
      signed_up_topics.each do |topic|&lt;br /&gt;
        ReviewBid.where(signuptopic_id: topic, participant_id: params[:id]).destroy_all&lt;br /&gt;
      end&lt;br /&gt;
      params[:topic].each_with_index do |topic_id, index|&lt;br /&gt;
        bid_existence = ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id])&lt;br /&gt;
        if bid_existence.empty?&lt;br /&gt;
          ReviewBid.create(priority: index + 1, signuptopic_id: topic_id, participant_id: params[:id], assignment_id: assignment_id)&lt;br /&gt;
        else&lt;br /&gt;
          ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id]).update_all(priority: index + 1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to action: 'show', assignment_id: params[:assignment_id], id: params[:id]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    participant_id = params[:participant_id].to_i&lt;br /&gt;
    selected_topic_ids = params[:topic]&amp;amp;.map(&amp;amp;:to_i) || []&lt;br /&gt;
    assignment_id = params[:assignment_id].to_i&lt;br /&gt;
&lt;br /&gt;
    unless current_user_can_modify_participant?(participant_id)&lt;br /&gt;
      flash[:error] = 'You are not authorized to modify these bids.'&lt;br /&gt;
      respond_to do |format|&lt;br /&gt;
        format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
        format.json { render json: { status: 'unauthorized' }, status: :unauthorized }&lt;br /&gt;
      end&lt;br /&gt;
      return&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    if selected_topic_ids.empty?&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      assignment = Assignment.find_by(id: assignment_id)&lt;br /&gt;
      if assignment.nil?&lt;br /&gt;
        flash[:error] = &amp;quot;Invalid assignment.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_assignment' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      unless SignUpTopic.where(id: selected_topic_ids, assignment_id: assignment_id).count == selected_topic_ids.size&lt;br /&gt;
        flash[:error] = &amp;quot;One or more selected topics are invalid.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_topics' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).where.not(signuptopic_id: selected_topic_ids).destroy_all&lt;br /&gt;
&lt;br /&gt;
      selected_topic_ids.each_with_index do |topic_id, index|&lt;br /&gt;
        bid = ReviewBid.find_or_initialize_by(signuptopic_id: topic_id, participant_id: participant_id)&lt;br /&gt;
        bid.priority = index + 1&lt;br /&gt;
        bid.assignment_id = assignment_id&lt;br /&gt;
        bid.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_to action: 'show', assignment_id: assignment_id, id: participant_id, notice: 'Review bids updated successfully.' }&lt;br /&gt;
      format.json { render json: { status: 'success' }, status: :ok }&lt;br /&gt;
    end&lt;br /&gt;
  rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
    flash[:error] = &amp;quot;Failed to update priorities: #{e.message}&amp;quot;&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
      format.json { render json: { status: 'error', message: e.message }, status: :unprocessable_entity }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Client Side Changes to Support &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Changes ====&lt;br /&gt;
After tidying up the server-side logic, we needed to refactor the client-side code to ensure it passed the correct information from the view to the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action. On the client side, we now grab the topicIDs and send them as &amp;lt;code&amp;gt;selected_topic_ids&amp;lt;/code&amp;gt;, allowing the controller to figure out which topics were selected and in what order.  &lt;br /&gt;
&lt;br /&gt;
We also extracted the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, making it a lot easier to send data with AJAX and keeping things flexible. When working with jQuery, AJAX is awesome because it lets us update the server without reloading the page, making the user experience smooth and responsive. When we pull the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, we can grab it along with other data, like the list of topics, and include it in the request. This way, we don’t have to rely on the URL to know which participant we’re working with, which makes the code cleaner and more adaptable.&lt;br /&gt;
&lt;br /&gt;
If we were to rely on the route (as opposed to using the DOM) for the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt;, every AJAX request would need to be tied to a specific URL structure. That’s limiting, especially if the route doesn’t naturally include the participant ID, as can be the case with collection routes versus member routes in Rails. &lt;br /&gt;
&lt;br /&gt;
Note: One thing that was interesting is the mix of JavaScript and CoffeeScript code. It wasn't clear if we are keeping both and refactoring to simple JavaScript (or possibly TypeScript in the future).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery -&amp;gt;&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: -&amp;gt;&lt;br /&gt;
      $.post($(this).data('update-url'), $(this).sortable('serialize'))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery(document).ready ($) -&amp;gt;&lt;br /&gt;
  sendUpdatedOrder = -&amp;gt;&lt;br /&gt;
    sortedIDs = $(this).sortable('toArray')&lt;br /&gt;
&lt;br /&gt;
    topicIDs = sortedIDs.map (id) -&amp;gt;&lt;br /&gt;
      id.replace 'topic_', ''&lt;br /&gt;
&lt;br /&gt;
    participant_id = $(this).data('participant-id')&lt;br /&gt;
&lt;br /&gt;
    assignment_id = new URL($(this).data('update-url'), window.location.origin).searchParams.get('assignment_id')&lt;br /&gt;
&lt;br /&gt;
    $.ajax&lt;br /&gt;
      type: 'POST'&lt;br /&gt;
      url: $(this).data('update-url')&lt;br /&gt;
      data:&lt;br /&gt;
        topic: topicIDs&lt;br /&gt;
        participant_id: participant_id&lt;br /&gt;
        assignment_id: assignment_id&lt;br /&gt;
      success: (response) -&amp;gt;&lt;br /&gt;
        console.log 'Selections updated successfully.'&lt;br /&gt;
      error: (xhr, status, error) -&amp;gt;&lt;br /&gt;
        alert 'An error occurred while saving your selections. Please try again.'&lt;br /&gt;
        console.error error&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: sendUpdatedOrder&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-1.-signupsheetcontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; Updates ===&lt;br /&gt;
&lt;br /&gt;
The ReviewBidsController's index method renders the review_bids_others_work action in &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt;. However, the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; does not include the controller action for this view. We need to find the old implementation of the controller action, add it back, and review and update it for any DRY violations.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # provides variables for reviewing page located at views/review_bids/others_work.html.erb&lt;br /&gt;
  def index&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
    return unless current_user_id?(@participant.user_id)&lt;br /&gt;
&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
    # Finding how many reviews have been completed&lt;br /&gt;
    @num_reviews_completed = 0&lt;br /&gt;
    @review_mappings.each do |map|&lt;br /&gt;
      @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # render view for completing reviews after review bidding has been completed&lt;br /&gt;
    render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
  end  &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-2.-reviewbidsotherswork-view-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;review_bids_others_work.html.erb&amp;lt;/code&amp;gt; View Updates ====&lt;br /&gt;
&lt;br /&gt;
The code only contains a view with this name and no associated controller action. Reviewing the code in the view, we noticed some SRP violations where there is much logic in the presentation layer. Set up helper methods in the controller to handle some of that logic or see if the model can handle some of that logic.&lt;br /&gt;
&lt;br /&gt;
Note: The view is technically part of the SignUpSheetController, but it's being rendered by the ReviewBidsController, which initially threw us off. After chatting with the professor, we decided not to change this view. He indicated that the plan is to reuse these views for both assignment bidding and review bidding.&lt;br /&gt;
&lt;br /&gt;
We would suggest building the assignment bidding and review bidding features separately first to make sure each one works on its own. Once that’s done, we can go back and clean things up by combining any shared functionality. This could include creating shared views or even introducing an abstract model to handle both types of bidding in a cleaner way. It could also mean replacing the ReviewBidsController with something more generic, like a BidsController, to keep things reusable and easier to maintain down the line.&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Rename &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; method ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; should be &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt;&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:''' Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The primary activity here is to refactor the method name &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; and fix the hardcoded URL. However, the match topic bidding algorithm no longer works since Heroku is no longer free, per the professor. The reference link below has detailed information about the web service that we can leverage to gain that understanding.&lt;br /&gt;
&lt;br /&gt;
Reference Link for Webservice Details: [https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Updated Implementation ====&lt;br /&gt;
The original requirement was to rename the run_bidding_algorithm method to assign_reviewers, but after digging into the code, we realized that renaming it didn’t make sense. The method runs the bidding algorithm to match topics, and there’s already a separate method to handle assigning review topics.&lt;br /&gt;
&lt;br /&gt;
However, we improved the logic in the assign_bidding method (which is called by the run_bidding_algorithm) by introducing a separate BiddingAlgorithmService object. This keeps the business logic out of the controller action, which makes things cleaner and more maintainable.&lt;br /&gt;
&lt;br /&gt;
==== review_bids_controller ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def run_bidding_algorithm&lt;br /&gt;
    review_bid = ReviewBid.new&lt;br /&gt;
    bidding_data = review_bid.bidding_data&lt;br /&gt;
    matched_topics = BiddingAlgorithmService.new(bidding_data).run&lt;br /&gt;
    raise ArgumentError, 'Failed to assign reviewers. Please try again later.' unless matched_topics&lt;br /&gt;
&lt;br /&gt;
    matched_topics&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== bidding_algorithm_service ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# frozen_string_literal: true&lt;br /&gt;
&lt;br /&gt;
# The `BiddingAlgorithmService` run the bid assignment algorithm&lt;br /&gt;
# Sends student IDs, topic IDs, student preferences, and timestamps to the web service&lt;br /&gt;
# The web service returns the matched assignments in the JSON response body&lt;br /&gt;
class BiddingAlgorithmService&lt;br /&gt;
  SERVICE_URL = 'http://app-csc517.herokuapp.com/match_topics'.freeze&lt;br /&gt;
&lt;br /&gt;
  # MOCK_DATA is based on the documentation detailing how the webservice behaves.&lt;br /&gt;
  # Reference: https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review#Webservice&lt;br /&gt;
  MOCK_DATA = {&lt;br /&gt;
    36239 =&amp;gt; [3970, 3972, 3975],&lt;br /&gt;
    36240 =&amp;gt; [3973, 3974, 3972],&lt;br /&gt;
    36241 =&amp;gt; [3969, 3971, 3972],&lt;br /&gt;
    36242 =&amp;gt; [3969, 3971, 3973],&lt;br /&gt;
    36243 =&amp;gt; [3969, 3970, 3971]&lt;br /&gt;
  }.freeze&lt;br /&gt;
&lt;br /&gt;
  def initialize(bidding_data)&lt;br /&gt;
    @bidding_data = bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def run&lt;br /&gt;
    return MOCK_DATA if Rails.application.config.use_mock_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
    perform_request&lt;br /&gt;
  rescue RestClient::ExceptionWithResponse, JSON::ParserError&lt;br /&gt;
    false&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def perform_request&lt;br /&gt;
    response = RestClient.post(&lt;br /&gt;
      SERVICE_URL,&lt;br /&gt;
      @bidding_data.to_json,&lt;br /&gt;
      content_type: :json,&lt;br /&gt;
      accept: :json&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    validate_response(response)&lt;br /&gt;
    JSON.parse(response.body)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def validate_response(response)&lt;br /&gt;
    raise 'Invalid response format' unless response.headers[:content_type] &amp;amp;&amp;amp; response.headers[:content_type].include?('application/json')&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Resolve Functionality Issues for Non Bidders ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Currently it doesn't work if some student does not bid. In this case, algorithm needs to be fixed to ignore anyone who didn’t bid, and let them choose from what’s left over.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to do several things within this controller. We will set a default state for non-bidders that we can point to to give them options to select topics to review from the leftovers. We will consolidate &amp;lt;code&amp;gt;@sign_up_topics&amp;lt;/code&amp;gt; to resolve the DRY violation. Lastly, we will remove business logic from the show method and push it to a new helper to conform to the SRP.&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # Additional method&lt;br /&gt;
&lt;br /&gt;
  # Provides variables for review bidding page&lt;br /&gt;
  def show&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id].to_i)&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @sign_up_topics = SignUpTopic.where(assignment_id: @assignment.id, private_to: nil)&lt;br /&gt;
    my_topic = SignedUpTeam.topic_id(@participant.parent_id, @participant.user_id)&lt;br /&gt;
    @sign_up_topics -= SignUpTopic.where(assignment_id: @assignment.id, id: my_topic)&lt;br /&gt;
    @num_participants = AssignmentParticipant.where(parent_id: @assignment.id).count&lt;br /&gt;
    @selected_topics = nil # This is used to list the topics assigned to review (i.e., select == assigned)&lt;br /&gt;
    @bids = ReviewBid.where(participant_id: @participant, assignment_id: @assignment.id)&lt;br /&gt;
    signed_up_topics = []&lt;br /&gt;
    &lt;br /&gt;
    @bids.each do |bid|&lt;br /&gt;
      sign_up_topic = SignUpTopic.find_by(id: bid.signuptopic_id)&lt;br /&gt;
      signed_up_topics &amp;lt;&amp;lt; sign_up_topic if sign_up_topic&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    signed_up_topics &amp;amp;= @sign_up_topics&lt;br /&gt;
    @sign_up_topics -= signed_up_topics&lt;br /&gt;
    @bids = signed_up_topics&lt;br /&gt;
    @num_of_topics = @sign_up_topics.size&lt;br /&gt;
    @assigned_review_maps = []&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: @assignment.id, reviewer_id: @participant.id).each do |review_map|&lt;br /&gt;
      @assigned_review_maps &amp;lt;&amp;lt; review_map&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Explicitly render view since it's in the sign up sheet views&lt;br /&gt;
    render 'sign_up_sheet/review_bids_show'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Additional methods&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-2.-reviewbidshelper-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
 # Assigns bidding topics to reviewers&lt;br /&gt;
  def assign_bidding&lt;br /&gt;
      assignment = validate_assignment(params[:assignment_id])&lt;br /&gt;
&lt;br /&gt;
      reviewers = validate_reviewers(assignment.id)&lt;br /&gt;
      reviewer_ids = reviewers.map(&amp;amp;:id)&lt;br /&gt;
&lt;br /&gt;
      matched_topics = run_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
      if matched_topics.blank?&lt;br /&gt;
        flash[:alert] = 'Topic or assignment is missing'&lt;br /&gt;
        redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      leftover_topics = find_leftover_topics(assignment.id, matched_topics)&lt;br /&gt;
      assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.new.assign_review_topics(matched_topics)&lt;br /&gt;
      assignment.update!(can_choose_topic_to_review: false)&lt;br /&gt;
&lt;br /&gt;
      flash[:notice] = 'Reviewers were successfully assigned to topics.'&lt;br /&gt;
      redirect_back fallback_location: root_path&lt;br /&gt;
    rescue ArgumentError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ArgumentError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: e.message&lt;br /&gt;
    rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::RecordInvalid: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue ActiveRecord::ActiveRecordError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::ActiveRecordError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue StandardError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;StandardError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers. Please try again later.'&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  #service methods here ...&lt;br /&gt;
&lt;br /&gt;
  def find_leftover_topics(assignment_id, matched_topics)&lt;br /&gt;
    all_topic_ids = SignUpTopic.where(assignment_id: assignment_id).pluck(:id)&lt;br /&gt;
    assigned_topic_ids = matched_topics.map { |match| match[:topic_id] }&lt;br /&gt;
&lt;br /&gt;
    # Calculate leftover topics&lt;br /&gt;
    all_topic_ids - assigned_topic_ids&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
    return if leftover_topics.blank?&lt;br /&gt;
&lt;br /&gt;
    # Find non-bidders by excluding already matched reviewers&lt;br /&gt;
    non_bidders = reviewer_ids - matched_topics.keys&lt;br /&gt;
&lt;br /&gt;
    # Assign leftover topics to non-bidders in a round-robin fashion&lt;br /&gt;
    non_bidders.each_with_index do |reviewer_id, index|&lt;br /&gt;
      topic_id = leftover_topics[index % leftover_topics.length]&lt;br /&gt;
      ReviewBid.create(priority: 1, signuptopic_id: topic_id, participant_id: reviewer_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirm Functionality for Single-person Teams ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Make sure your code works on individual assignments, i.e., assignments where participants sign up for topics instead of teams. In this case, a team is created for each participant when they sign up. So the code should work for assignments to which either individuals or teams submit.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to make an update to focus on topic rather than team or have it where teams are updated based on topic id.We will probably utilize sign up topic or a similar object to accomplish this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-5-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bid Status Messaging ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| To make the functionality more intuitive, include a message to say how many students are eligible to submit bids, how many have submitted their bids, when the deadline for submitting bids is.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
The logic for these messages will go into the helper file. It is effectively the same reasoning as our migration of other business/functionality code from the controller to the helper to conform to the SRP. It will go hand in hand with the SignUpTopic portion of the code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-6-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Why are the methods in review_bid.rb class methods? Can we change them to instance methods or move it to helpers?&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-1.-reviewbid-model-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model Updates ====&lt;br /&gt;
Since the current class methods are tied to &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model instances, we will change them to instance methods. There is no need to move them to helpers.&lt;br /&gt;
&lt;br /&gt;
===== Review Bid Model Flow Before Refactoring =====&lt;br /&gt;
This sequence diagram describes how the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; interacts with the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model in assigning review topics to reviewers according to their bids. Once the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; action is initiated by the user, the controller gathers the IDs of reviewers and calls &amp;lt;code&amp;gt;ReviewBid.bidding_data&amp;lt;/code&amp;gt; to gather bidding information. That is sent out to an external bidding algorithm, and the matched topics that come back are used by &amp;lt;code&amp;gt;ReviewBid.assign_review_topics&amp;lt;/code&amp;gt; to create &amp;lt;code&amp;gt;ReviewResponseMap&amp;lt;/code&amp;gt; entries, assigning topics to reviewers for the assignment.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
  belongs_to :assignment, class_name: 'Assignment'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  def self.bidding_data(assignment_id, reviewer_ids)&lt;br /&gt;
    # create basic hash and set basic hash data&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'users' =&amp;gt; {}, 'max_accepted_proposals' =&amp;gt; [] }&lt;br /&gt;
    bidding_data['tid'] = SignUpTopic.where(assignment_id: assignment_id).ids&lt;br /&gt;
    bidding_data['max_accepted_proposals'] = Assignment.where(id: assignment_id).pluck(:num_reviews_allowed).first&lt;br /&gt;
&lt;br /&gt;
    # loop through reviewer_ids to get reviewer specific bidding data&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      bidding_data['users'][reviewer_id] = reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # assigns topics to reviews as matched by the webservice algorithm&lt;br /&gt;
  def self.assign_review_topics(assignment_id, reviewer_ids, matched_topics, _min_num_reviews = 2)&lt;br /&gt;
    # if review response map already created, delete it&lt;br /&gt;
    if ReviewResponseMap.where(reviewed_object_id: assignment_id)&lt;br /&gt;
      ReviewResponseMap.where(reviewed_object_id: assignment_id).destroy_all&lt;br /&gt;
    end&lt;br /&gt;
    # loop through reviewer_ids to assign reviews to each reviewer&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      topics_to_assign = matched_topics[reviewer_id.to_s]&lt;br /&gt;
      topics_to_assign.each do |topic|&lt;br /&gt;
        assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method to assign a single topic to a reviewer&lt;br /&gt;
  def self.assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic).pluck(:team_id).first&lt;br /&gt;
    team_to_review.nil? ? [] : ReviewResponseMap.create(reviewed_object_id: assignment_id, reviewer_id: reviewer_id, reviewee_id: team_to_review, type: 'ReviewResponseMap')&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method for getting individual reviewer_ids bidding data&lt;br /&gt;
  # returns user's bidding data hash&lt;br /&gt;
  def self.reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    reviewer_user_id = AssignmentParticipant.find(reviewer_id).user_id&lt;br /&gt;
    self_topic = SignedUpTeam.topic_id(assignment_id, reviewer_user_id)&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'otid' =&amp;gt; self_topic, 'priority' =&amp;gt; [], 'time' =&amp;gt; [] }&lt;br /&gt;
    bids = ReviewBid.where(participant_id: reviewer_id)&lt;br /&gt;
&lt;br /&gt;
    # loop through each bid for a topic to get specific data&lt;br /&gt;
    bids.each do |bid|&lt;br /&gt;
      bidding_data['tid'] &amp;lt;&amp;lt; bid.signuptopic_id&lt;br /&gt;
      bidding_data['priority'] &amp;lt;&amp;lt; bid.priority&lt;br /&gt;
      bidding_data['time'] &amp;lt;&amp;lt; bid.updated_at&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  # Instance method to get bidding data for this review bid&lt;br /&gt;
  def bidding_data&lt;br /&gt;
    {&lt;br /&gt;
      'tid' =&amp;gt; topic_ids,&lt;br /&gt;
      'otid' =&amp;gt; self_topic,&lt;br /&gt;
      'priority' =&amp;gt; priorities,&lt;br /&gt;
      'time' =&amp;gt; updated_times&lt;br /&gt;
    }&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # bid object to limit db touches for similar data acquisitions&lt;br /&gt;
  def bids&lt;br /&gt;
    @bids ||= ReviewBid.where(participant_id: participant_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def topic_ids&lt;br /&gt;
    bids.pluck(:signuptopic_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def priorities&lt;br /&gt;
    bids.pluck(:priority)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def updated_times&lt;br /&gt;
    bids.pluck(:updated_at)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self_topic&lt;br /&gt;
    return nil unless topic &amp;amp;&amp;amp; topic.assignment_id&lt;br /&gt;
&lt;br /&gt;
    SignedUpTeam.topic_id(topic.assignment_id, participant.user_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign topics to reviews&lt;br /&gt;
  def assign_review_topics(matched_topics)&lt;br /&gt;
    validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: topic.assignment_id).destroy_all&lt;br /&gt;
&lt;br /&gt;
    matched_topics.each do |reviewer_id, topics|&lt;br /&gt;
      Array(topics).each { |topic_id| assign_topic_to_reviewer(reviewer_id, topic_id) }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign a single topic to a reviewer&lt;br /&gt;
  def assign_topic_to_reviewer(reviewer_id, topic_id)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic_id).pluck(:team_id).first&lt;br /&gt;
    return if team_to_review.nil?&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.create(&lt;br /&gt;
      reviewed_object_id: topic.assignment_id,&lt;br /&gt;
      reviewer_id: reviewer_id,&lt;br /&gt;
      reviewee_id: team_to_review,&lt;br /&gt;
      type: 'ReviewResponseMap'&lt;br /&gt;
    )&lt;br /&gt;
    # error handling&lt;br /&gt;
  rescue StandardError =&amp;gt; e&lt;br /&gt;
    Rails.logger.error(&amp;quot;Failed to assign topic #{topic_id} to reviewer #{reviewer_id}: #{e.message}&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
    raise ArgumentError, 'Topic or assignment is missing' if topic.nil? || topic.assignment_id.nil?&lt;br /&gt;
    raise ArgumentError, 'Matched topics must be a Hash' unless matched_topics.is_a?(Hash)&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
Once we change them to instance methods, we’ll need to modify their use in the code to instantiate the object first.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;Placeholder for implementation screenshots (before and after)&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-3.-schema.db-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;Schema.db&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We also need to remove the foreign key reference to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model since the &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model already has access to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;, and that is the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; we should be referencing. This will also involve modifying the database to remove the foreign key reference to the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Steps required:&lt;br /&gt;
&lt;br /&gt;
# Review the records in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table in the database to see if there are any records and if those records have values for &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;&lt;br /&gt;
# Create a migration file using the command: &amp;lt;code&amp;gt;rails generate migration RemoveAssignmentFromReviewBids&amp;lt;/code&amp;gt;&lt;br /&gt;
# In the generated file, indicate the change that needs to be made using the change method and migration columns &amp;lt;code&amp;gt;remove_foreign_key&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;remove_column&amp;lt;/code&amp;gt;. Migration should be straightforward since there is no cascading behavior (i.e., &amp;lt;code&amp;gt;dependent:destroy&amp;lt;/code&amp;gt;) to worry about.&lt;br /&gt;
# Once comfortable with the updates to the generated file, run the migration using the &amp;lt;code&amp;gt;rails db:migrate&amp;lt;/code&amp;gt; command.&lt;br /&gt;
# Run a query in the DB to ensure no orphaned records are in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table after the updates.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;Placeholder for implementation screenshots (before and after)&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-4.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implement Edge Case Testing ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| In the previous implementation wiki, there are edge cases which are not exhaustively tested. Should test those edge cases thoroughly and add more edge case testing - link&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span class=&amp;quot;mark&amp;quot;&amp;gt;TODO&amp;lt;/span&amp;gt;:We just got initial test results back. We still need to figure out why tests are failing and what may be slipping through the cracks.[[File:Test expertiza.png|411x459px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-8-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design Patterns and Principles ===&lt;br /&gt;
Below are the design patterns and principles we plan to use to address the problem statements outlined in the project requirements:&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
** Controller Responsibility&lt;br /&gt;
*** To address the issue of authentication logic residing in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, we will refactor the code to ensure the controller solely handles request and response flows.&lt;br /&gt;
*** Authentication logic will be moved to dedicated methods in &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt;, making the codebase easier to maintain and test.&lt;br /&gt;
** View Simplification&lt;br /&gt;
*** Currently, the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; view contains excessive business logic, violating SRP.&lt;br /&gt;
*** We will move this logic into helper methods or models, ensuring each component has a single responsibility and improving code readability.&lt;br /&gt;
&lt;br /&gt;
* Separation of Concerns (SoC)&lt;br /&gt;
** By relocating authorization logic out of the controller, we separate authentication from request handling.&lt;br /&gt;
** Adhering to SoC enhances readability and maintainability by allowing each part of the application to focus on its specific role.&lt;br /&gt;
* Don't Repeat Yourself (DRY)&lt;br /&gt;
** We will inspect the existing implementation for code duplication, particularly in the controller actions.&lt;br /&gt;
** Any repetitive code will be refactored or removed to reduce redundancy and improve clarity.&lt;br /&gt;
&lt;br /&gt;
* Model-View-Controller (MVC) Adherence&lt;br /&gt;
** Moving business logic from the view aligns with MVC best practices.&lt;br /&gt;
** By ensuring the view only presents data without processing it, we maintain clear boundaries between the model, view, and controller layers.&lt;br /&gt;
&lt;br /&gt;
* Dependency Inversion Principle (DIP)&lt;br /&gt;
** We plan to update class methods to instance methods, allowing the controller to depend on abstractions rather than concrete implementations.&lt;br /&gt;
** This change leverages Ruby's duck typing, providing flexibility and making the code easier to maintain and test.&lt;br /&gt;
&lt;br /&gt;
* Factory Pattern&lt;br /&gt;
** We will implement a factory pattern for the common processing of &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpBid&amp;lt;/code&amp;gt;, encapsulating object creation and promoting code reuse.&lt;br /&gt;
&lt;br /&gt;
* Law of Demeter (Principle of Least Knowledge)&lt;br /&gt;
** We'll delegate interactions appropriately to prevent tightly coupled code, especially when dealing with topics and teams.&lt;br /&gt;
** Clear and intentional naming during refactoring will enhance code clarity and maintainability.&lt;br /&gt;
&lt;br /&gt;
* Adapter Pattern&lt;br /&gt;
** To add messaging to bid states for users, we'll use the adapter pattern to allow different components to work together without modifying their existing code.&lt;br /&gt;
&lt;br /&gt;
== Files Modified/Added ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-1.-controllers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
review_bids_controller.rb&lt;br /&gt;
&lt;br /&gt;
signup_sheet_controller.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-2.-models&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
review_bid.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-3.-helpers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
&lt;br /&gt;
authorization_helper.rb&lt;br /&gt;
&lt;br /&gt;
Review_bids_helper.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;viii.-test-plan&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
The Excel spreadsheet below details our test plan for testing the code we changed or created to ensure code coverage. The process involved reviewing the code and determining all the possible scenarios that needed to be covered based on what the method was doing, then creating test cases accordingly.&lt;br /&gt;
&lt;br /&gt;
* [https://docs.google.com/spreadsheets/d/1iHzOdSwlTtxOtf5VsNdwOaCP8NzYMmiJ/edit?usp=drive_link Open Test Matrix Spreadsheet]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/review_bids_controller_spec.rb ReviewBidsController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/sign_up_sheet_controller_spec.rb SignUpSheetController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/models/review_bid_spec.rb ReviewBid model updated test cases]&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-1.-mentor&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Ed Gehringer&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-2.-members&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Members ===&lt;br /&gt;
&lt;br /&gt;
* Gavin Teague&lt;br /&gt;
* Janice Uwujaren&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xi.-links&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
Previous Wiki: [https://wiki.expertiza.ncsu.edu/index.php/CSC/ECE_517_Fall_2021_-_E2151._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;Link&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xii.-references&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/document/d/1LHVacylQmB15I6VcYUA5nuC7GRRU9szHzZhjlPcE-PA/edit?tab=t.0#heading=h.u0oycoj6e0ev &amp;lt;u&amp;gt;Project Instructions&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;appendix-a-uml-class-diagram-plantuml-code-for-review-bids&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Juwujar</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160973</id>
		<title>CSC/ECE 517 Fall 2024 - E2485. Allow reviewers to bid on what to review</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160973"/>
		<updated>2024-12-10T22:55:05Z</updated>

		<summary type="html">&lt;p&gt;Juwujar: /* ReviewBidsController Updates */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
This project will fix the defects and improve the functionality in Expertiza of the Reviewer Bidding feature, a process where reviewers can bid for assignments they want to review. It will make the functionality smoother, more reliable, and align with Expertiza's collaborative learning objectives.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iii.-problem-statements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Problem Statements ==&lt;br /&gt;
&lt;br /&gt;
Assigning reviews to users on Expertiza involves a complex bidding algorithm that requires optimization before production deployment. Below are key areas we plan to address as per the project requirements:&lt;br /&gt;
* Code Refactoring and Best Practices&lt;br /&gt;
** '''Utilize Authentication Utilities''': Modify the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; function in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to use existing authentication utilities, enhancing code reusability and adhering to the Single Responsibility Principle (SRP) and Separation of Concerns (SoC).&lt;br /&gt;
** '''Eliminate Code Duplication''': Refactor &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; to comply with the Don't Repeat Yourself (DRY) principle.&lt;br /&gt;
** '''Rename Functions for Clarity''': Change &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; to better reflect its purpose.&lt;br /&gt;
** '''Refactor Methods in '''&amp;lt;code&amp;gt;review_bid.rb&amp;lt;/code&amp;gt;''':''': Convert class methods to instance methods or relocate them to helper classes for improved code organization.&lt;br /&gt;
* Functionality Enhancements&lt;br /&gt;
** '''Handle Missing Bids Gracefully''': Adjust the bidding algorithm to accommodate students who do not bid, allowing them to select from the remaining options.&lt;br /&gt;
** '''Support Both Individual and Team Assignments''': Ensure the code functions correctly for participant and team-based assignments when signing up for topics.&lt;br /&gt;
* User Interface Improvements&lt;br /&gt;
** '''Increase Transparency in the Bidding Process''': Display messages indicating how many students are eligible to bid, how many have submitted bids, and the deadline.&lt;br /&gt;
** '''Accurate Bid Status Indicators''': Ensure topic colors change correctly based on the number of outstanding bids, providing clear visual feedback to users.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Exhaustive Edge Case Testing''': Conduct thorough testing for edge cases not previously covered, adding additional test cases as needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iv.-design-goals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Design Goals ==&lt;br /&gt;
&lt;br /&gt;
While addressing the existing defects and enhancing the Reviewer Bidding feature, we plan to adhere to the following design guidelines:&lt;br /&gt;
&lt;br /&gt;
* Code Quality and Maintainability&lt;br /&gt;
** '''Improve Code Structure: '''Implement refactoring strategies to enhance code readability and maintainability, ensuring adherence to best practices like the DRY principle.&lt;br /&gt;
** '''Standardize Authentication Logic: '''Utilize a centralized authentication utility to promote consistency and reusability across the application.&lt;br /&gt;
* System Stability and Flexibility&lt;br /&gt;
** '''Enhance Algorithm Robustness: '''Modify the bidding algorithm to handle scenarios where students do not bid, ensuring the system remains stable under all conditions.&lt;br /&gt;
** '''Ensure Assignment Versatility: '''Design the system to seamlessly support both individual and team assignments without additional configuration.&lt;br /&gt;
* User Experience Enhancements&lt;br /&gt;
** '''Increase Bidding Transparency: '''Develop user interface elements that clearly display bidding status, deadlines, and participation metrics.&lt;br /&gt;
** '''Implement Dynamic Visual Feedback: '''Use real-time visual cues, such as color changes, to inform users about the bidding status instantly.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Develop Comprehensive Test Suites: '''Create extensive automated tests covering edge cases and critical functionalities to guarantee reliable system behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;v.-uml-diagram&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== UML Diagram ==&lt;br /&gt;
&lt;br /&gt;
Below is a visual representation of the models associated with the review bids functionality, depicting the data structures and their relationships.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml class diagram2.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi.-implementation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
'''ReviewBid: '''The &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; manages the bidding process, where participants indicate their preferences for reviewing different topics or assignments. This helps distribute review tasks fairly based on participants' interests.&lt;br /&gt;
&lt;br /&gt;
'''Assignment: '''The &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model represents team or individual assignments in Expertiza. It stores all the essential information required to keep the assignment process organized, like managing participants, setting due dates, forming teams, and overseeing the review process.&lt;br /&gt;
&lt;br /&gt;
'''SignUpTopic: '''The &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt; model identifies specific topics or areas within an assignment for which participants can sign up. It manages the availability of these topics, tracks bids, coordinates team sign-ups, and assigns topics based on both bids and participant availability. &lt;br /&gt;
&lt;br /&gt;
'''Participant: '''The &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model is about the individual users who participate in an assignment. This includes students, instructors, teaching assistants, or other relevant roles. This model is responsible for managing key user information, monitoring interactions with the assignment, keeping track of team memberships, and recording things like badges and review grades.&lt;br /&gt;
&lt;br /&gt;
== Implementation ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1.-decoupling-authorization-logic-from-reviewbidscontroller&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Decoupling Authorization Logic from &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| '''&amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; needs to use the authentication utilities'''&lt;br /&gt;
&lt;br /&gt;
The function &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; should use the available authentication utilities to support the Single Responsibility Principle and Separation of Concerns and to support code reusability.&lt;br /&gt;
&lt;br /&gt;
| '''Authentication Consistency:''' Remove authentication logic in the controller and call logic in the authentication utility class instead to ensure consistent use of the same logic across the application, foster reusability, and eliminate DRY.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
==== Authorization Flow Before Refactoring ====&lt;br /&gt;
The sequence diagram depicts the authorization process within the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;. When a user invokes an action, the controller checks if the action is allowed by verifying the user's role through the &amp;lt;code&amp;gt;current_role_name()&amp;lt;/code&amp;gt; method in the &amp;lt;code&amp;gt;ApplicationController&amp;lt;/code&amp;gt;. Depending on the action and the user's role, the controller either authorizes the user to proceed or denies access, ultimately executing the action logic if authorized and responding to the user.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 1.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We simplified the &amp;lt;code&amp;gt;action_allowed?&amp;lt;/code&amp;gt; method by decoupling authorization logic from the controller to use helper methods in the &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt; module. The &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; should not handle authentication, which is a violation of SRP. &lt;br /&gt;
&lt;br /&gt;
*Note:* We also made similar updates to the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; methods for &amp;lt;code&amp;gt;SignUpController&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;vertical-align:top;&amp;quot; | Original Code !! style=&amp;quot;vertical-align:top;&amp;quot; | Refactored Code&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index'&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator',&lt;br /&gt;
     'Student'].include? current_role_name and&lt;br /&gt;
        ((%w[list].include? action_name) ? are_needed_authorizations_present?(params[:id], &amp;quot;participant&amp;quot;, &amp;quot;reader&amp;quot;, &amp;quot;submitter&amp;quot;, &amp;quot;reviewer&amp;quot;) : true)&lt;br /&gt;
  else&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator'].include? current_role_name&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index', 'list'&lt;br /&gt;
    current_user_has_student_privileges? &amp;amp;&amp;amp; list_authorization_check&lt;br /&gt;
  else&lt;br /&gt;
    current_user_has_ta_privileges?&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; action ===&lt;br /&gt;
Previously, the &amp;lt;code&amp;gt;ReviewBidsController's index&amp;lt;/code&amp;gt; action was overloaded with responsibilities—it handled everything from locating and validating assignments to fetching review mappings and counting completed reviews. This all-in-one approach made the controller action messy. &lt;br /&gt;
&lt;br /&gt;
We have moved the business logic from the controller action and into dedicated service objects. With this change, we introduced two key service objects: &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; focuses on retrieving the participant along with their assignments, while the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; takes on the task of managing review mappings and calculating review-related stats, like how many reviews have been completed. &lt;br /&gt;
&lt;br /&gt;
This separation of responsibilities aligns with the Single Responsibility Principle. The logic for fetching assignments, retrieving review mappings, and counting reviews is now more flexible and reusable throughout the application. For example, we used methods in the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; in both the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; and the &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;, helping to avoid code duplication.The article https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial served as a guide on when and how to use service objects in Rails.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
  return unless current_user_id?(@participant.user_id)&lt;br /&gt;
  @assignment = @participant.assignment&lt;br /&gt;
  @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
  # Finding how many reviews have been completed&lt;br /&gt;
  @num_reviews_completed = 0&lt;br /&gt;
  @review_mappings.each do |map|&lt;br /&gt;
    @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # render view for completing reviews after review bidding has been completed&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @assignment = participant_service.assignment&lt;br /&gt;
  unless @assignment.is_a?(Assignment)&lt;br /&gt;
    flash[:error] = 'Assignment not found.'&lt;br /&gt;
    redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  @review_mappings = review_service.review_mappings&lt;br /&gt;
  @num_reviews_completed = review_service.review_counts[:completed]&lt;br /&gt;
&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Optimize &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Action functionality ===&lt;br /&gt;
In the earlier version of the set_priority action in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, the server only needed a couple of key pieces of information: an array of topic IDs (params[:topic]) and the participant ID (params[:id}). From there, it would figure out the assignment_id by using the first topic as a reference. While this worked in simple cases, it could lead to problems if any data was missing or didn’t line up correctly.&lt;br /&gt;
&lt;br /&gt;
With the new implementation, the action now expects clear and structured parameters, including participant_id, an array of topic IDs, and assignment_id. This change ensures that the server always receives complete and reliable information, preventing issues caused by missing or inconsistent data. It also removes the need for the server to perform unnecessary lookups, allowing the controller to focus on its main job: validating inputs (like making sure the topics in params[:topic] belong to the same assignment as the participant), updating the database, handling errors properly, and sending the correct response back to the view.&lt;br /&gt;
&lt;br /&gt;
While these updates have cleaned up the set_priority action, they still contain a lot of business logic, like validations and priority updates. To make the code even more maintainable, there’s room for further refactoring—perhaps moving some of this logic into a service class or model method to streamline the controller even more.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | Old Code&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    if params[:topic].nil?&lt;br /&gt;
      ReviewBid.where(participant_id: params[:id]).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      participant = AssignmentParticipant.find_by(id: params[:id])&lt;br /&gt;
      assignment_id = SignUpTopic.find(params[:topic].first).assignment.id&lt;br /&gt;
      @bids = ReviewBid.where(participant_id: params[:id])&lt;br /&gt;
      signed_up_topics = ReviewBid.where(participant_id: params[:id]).map(&amp;amp;:signuptopic_id)&lt;br /&gt;
      signed_up_topics -= params[:topic].map(&amp;amp;:to_i)&lt;br /&gt;
      signed_up_topics.each do |topic|&lt;br /&gt;
        ReviewBid.where(signuptopic_id: topic, participant_id: params[:id]).destroy_all&lt;br /&gt;
      end&lt;br /&gt;
      params[:topic].each_with_index do |topic_id, index|&lt;br /&gt;
        bid_existence = ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id])&lt;br /&gt;
        if bid_existence.empty?&lt;br /&gt;
          ReviewBid.create(priority: index + 1, signuptopic_id: topic_id, participant_id: params[:id], assignment_id: assignment_id)&lt;br /&gt;
        else&lt;br /&gt;
          ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id]).update_all(priority: index + 1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to action: 'show', assignment_id: params[:assignment_id], id: params[:id]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    participant_id = params[:participant_id].to_i&lt;br /&gt;
    selected_topic_ids = params[:topic]&amp;amp;.map(&amp;amp;:to_i) || []&lt;br /&gt;
    assignment_id = params[:assignment_id].to_i&lt;br /&gt;
&lt;br /&gt;
    unless current_user_can_modify_participant?(participant_id)&lt;br /&gt;
      flash[:error] = 'You are not authorized to modify these bids.'&lt;br /&gt;
      respond_to do |format|&lt;br /&gt;
        format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
        format.json { render json: { status: 'unauthorized' }, status: :unauthorized }&lt;br /&gt;
      end&lt;br /&gt;
      return&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    if selected_topic_ids.empty?&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      assignment = Assignment.find_by(id: assignment_id)&lt;br /&gt;
      if assignment.nil?&lt;br /&gt;
        flash[:error] = &amp;quot;Invalid assignment.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_assignment' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      unless SignUpTopic.where(id: selected_topic_ids, assignment_id: assignment_id).count == selected_topic_ids.size&lt;br /&gt;
        flash[:error] = &amp;quot;One or more selected topics are invalid.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_topics' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).where.not(signuptopic_id: selected_topic_ids).destroy_all&lt;br /&gt;
&lt;br /&gt;
      selected_topic_ids.each_with_index do |topic_id, index|&lt;br /&gt;
        bid = ReviewBid.find_or_initialize_by(signuptopic_id: topic_id, participant_id: participant_id)&lt;br /&gt;
        bid.priority = index + 1&lt;br /&gt;
        bid.assignment_id = assignment_id&lt;br /&gt;
        bid.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_to action: 'show', assignment_id: assignment_id, id: participant_id, notice: 'Review bids updated successfully.' }&lt;br /&gt;
      format.json { render json: { status: 'success' }, status: :ok }&lt;br /&gt;
    end&lt;br /&gt;
  rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
    flash[:error] = &amp;quot;Failed to update priorities: #{e.message}&amp;quot;&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
      format.json { render json: { status: 'error', message: e.message }, status: :unprocessable_entity }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Client Side Changes to Support &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Changes ====&lt;br /&gt;
After tidying up the server-side logic, we needed to refactor the client-side code to ensure it passed the correct information from the view to the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action. On the client side, we now grab the topicIDs and send them as &amp;lt;code&amp;gt;selected_topic_ids&amp;lt;/code&amp;gt;, allowing the controller to figure out which topics were selected and in what order.  &lt;br /&gt;
&lt;br /&gt;
We also extracted the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, making it a lot easier to send data with AJAX and keeping things flexible. When working with jQuery, AJAX is awesome because it lets us update the server without reloading the page, making the user experience smooth and responsive. When we pull the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, we can grab it along with other data, like the list of topics, and include it in the request. This way, we don’t have to rely on the URL to know which participant we’re working with, which makes the code cleaner and more adaptable.&lt;br /&gt;
&lt;br /&gt;
If we were to rely on the route (as opposed to using the DOM) for the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt;, every AJAX request would need to be tied to a specific URL structure. That’s limiting, especially if the route doesn’t naturally include the participant ID, as can be the case with collection routes versus member routes in Rails. &lt;br /&gt;
&lt;br /&gt;
Note: One thing that was interesting is the mix of JavaScript and CoffeeScript code. It wasn't clear if we are keeping both and refactoring to simple JavaScript (or possibly TypeScript in the future).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery -&amp;gt;&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: -&amp;gt;&lt;br /&gt;
      $.post($(this).data('update-url'), $(this).sortable('serialize'))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery(document).ready ($) -&amp;gt;&lt;br /&gt;
  sendUpdatedOrder = -&amp;gt;&lt;br /&gt;
    sortedIDs = $(this).sortable('toArray')&lt;br /&gt;
&lt;br /&gt;
    topicIDs = sortedIDs.map (id) -&amp;gt;&lt;br /&gt;
      id.replace 'topic_', ''&lt;br /&gt;
&lt;br /&gt;
    participant_id = $(this).data('participant-id')&lt;br /&gt;
&lt;br /&gt;
    assignment_id = new URL($(this).data('update-url'), window.location.origin).searchParams.get('assignment_id')&lt;br /&gt;
&lt;br /&gt;
    $.ajax&lt;br /&gt;
      type: 'POST'&lt;br /&gt;
      url: $(this).data('update-url')&lt;br /&gt;
      data:&lt;br /&gt;
        topic: topicIDs&lt;br /&gt;
        participant_id: participant_id&lt;br /&gt;
        assignment_id: assignment_id&lt;br /&gt;
      success: (response) -&amp;gt;&lt;br /&gt;
        console.log 'Selections updated successfully.'&lt;br /&gt;
      error: (xhr, status, error) -&amp;gt;&lt;br /&gt;
        alert 'An error occurred while saving your selections. Please try again.'&lt;br /&gt;
        console.error error&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: sendUpdatedOrder&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-1.-signupsheetcontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; Updates ===&lt;br /&gt;
&lt;br /&gt;
The ReviewBidsController's index method renders the review_bids_others_work action in &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt;. However, the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; does not include the controller action for this view. We need to find the old implementation of the controller action, add it back, and review and update it for any DRY violations.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # provides variables for reviewing page located at views/review_bids/others_work.html.erb&lt;br /&gt;
  def index&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
    return unless current_user_id?(@participant.user_id)&lt;br /&gt;
&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
    # Finding how many reviews have been completed&lt;br /&gt;
    @num_reviews_completed = 0&lt;br /&gt;
    @review_mappings.each do |map|&lt;br /&gt;
      @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # render view for completing reviews after review bidding has been completed&lt;br /&gt;
    render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
  end  &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-2.-reviewbidsotherswork-view-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;review_bids_others_work.html.erb&amp;lt;/code&amp;gt; View Updates ====&lt;br /&gt;
&lt;br /&gt;
The code only contains a view with this name and no associated controller action. Reviewing the code in the view, we noticed some SRP violations where there is much logic in the presentation layer. Set up helper methods in the controller to handle some of that logic or see if the model can handle some of that logic.&lt;br /&gt;
&lt;br /&gt;
Note: The view is technically part of the SignUpSheetController, but it's being rendered by the ReviewBidsController, which initially threw us off. After chatting with the professor, we decided not to change this view. He indicated that the plan is to reuse these views for both assignment bidding and review bidding.&lt;br /&gt;
&lt;br /&gt;
We would suggest building the assignment bidding and review bidding features separately first to make sure each one works on its own. Once that’s done, we can go back and clean things up by combining any shared functionality. This could include creating shared views or even introducing an abstract model to handle both types of bidding in a cleaner way. It could also mean replacing the ReviewBidsController with something more generic, like a BidsController, to keep things reusable and easier to maintain down the line.&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Rename &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; method ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; should be &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt;&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:''' Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The primary activity here is to refactor the method name &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; and fix the hardcoded URL. However, the match topic bidding algorithm no longer works since Heroku is no longer free, per the professor. The reference link below has detailed information about the web service that we can leverage to gain that understanding.&lt;br /&gt;
&lt;br /&gt;
Reference Link for Webservice Details: [https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Updated Implementation ====&lt;br /&gt;
The original requirement was to rename the run_bidding_algorithm method to assign_reviewers, but after digging into the code, we realized that renaming it didn’t make sense. The method runs the bidding algorithm to match topics, and there’s already a separate method to handle assigning review topics.&lt;br /&gt;
&lt;br /&gt;
However, we improved the logic in the assign_bidding method (which is called by the run_bidding_algorithm) by introducing a separate BiddingAlgorithmService object. This keeps the business logic out of the controller action, which makes things cleaner and more maintainable.&lt;br /&gt;
&lt;br /&gt;
==== review_bids_controller ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def run_bidding_algorithm&lt;br /&gt;
    review_bid = ReviewBid.new&lt;br /&gt;
    bidding_data = review_bid.bidding_data&lt;br /&gt;
    matched_topics = BiddingAlgorithmService.new(bidding_data).run&lt;br /&gt;
    raise ArgumentError, 'Failed to assign reviewers. Please try again later.' unless matched_topics&lt;br /&gt;
&lt;br /&gt;
    matched_topics&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== bidding_algorithm_service ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# frozen_string_literal: true&lt;br /&gt;
&lt;br /&gt;
# The `BiddingAlgorithmService` run the bid assignment algorithm&lt;br /&gt;
# Sends student IDs, topic IDs, student preferences, and timestamps to the web service&lt;br /&gt;
# The web service returns the matched assignments in the JSON response body&lt;br /&gt;
class BiddingAlgorithmService&lt;br /&gt;
  SERVICE_URL = 'http://app-csc517.herokuapp.com/match_topics'.freeze&lt;br /&gt;
&lt;br /&gt;
  # MOCK_DATA is based on the documentation detailing how the webservice behaves.&lt;br /&gt;
  # Reference: https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review#Webservice&lt;br /&gt;
  MOCK_DATA = {&lt;br /&gt;
    36239 =&amp;gt; [3970, 3972, 3975],&lt;br /&gt;
    36240 =&amp;gt; [3973, 3974, 3972],&lt;br /&gt;
    36241 =&amp;gt; [3969, 3971, 3972],&lt;br /&gt;
    36242 =&amp;gt; [3969, 3971, 3973],&lt;br /&gt;
    36243 =&amp;gt; [3969, 3970, 3971]&lt;br /&gt;
  }.freeze&lt;br /&gt;
&lt;br /&gt;
  def initialize(bidding_data)&lt;br /&gt;
    @bidding_data = bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def run&lt;br /&gt;
    return MOCK_DATA if Rails.application.config.use_mock_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
    perform_request&lt;br /&gt;
  rescue RestClient::ExceptionWithResponse, JSON::ParserError&lt;br /&gt;
    false&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def perform_request&lt;br /&gt;
    response = RestClient.post(&lt;br /&gt;
      SERVICE_URL,&lt;br /&gt;
      @bidding_data.to_json,&lt;br /&gt;
      content_type: :json,&lt;br /&gt;
      accept: :json&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    validate_response(response)&lt;br /&gt;
    JSON.parse(response.body)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def validate_response(response)&lt;br /&gt;
    raise 'Invalid response format' unless response.headers[:content_type] &amp;amp;&amp;amp; response.headers[:content_type].include?('application/json')&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Resolve Functionality Issues for Non Bidders ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Currently it doesn't work if some student does not bid. In this case, algorithm needs to be fixed to ignore anyone who didn’t bid, and let them choose from what’s left over.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to do several things within this controller. We will set a default state for non-bidders that we can point to to give them options to select topics to review from the leftovers. We will consolidate &amp;lt;code&amp;gt;@sign_up_topics&amp;lt;/code&amp;gt; to resolve the DRY violation. Lastly, we will remove business logic from the show method and push it to a new helper to conform to the SRP.&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # Additional method&lt;br /&gt;
&lt;br /&gt;
  # Provides variables for review bidding page&lt;br /&gt;
  def show&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id].to_i)&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @sign_up_topics = SignUpTopic.where(assignment_id: @assignment.id, private_to: nil)&lt;br /&gt;
    my_topic = SignedUpTeam.topic_id(@participant.parent_id, @participant.user_id)&lt;br /&gt;
    @sign_up_topics -= SignUpTopic.where(assignment_id: @assignment.id, id: my_topic)&lt;br /&gt;
    @num_participants = AssignmentParticipant.where(parent_id: @assignment.id).count&lt;br /&gt;
    @selected_topics = nil # This is used to list the topics assigned to review (i.e., select == assigned)&lt;br /&gt;
    @bids = ReviewBid.where(participant_id: @participant, assignment_id: @assignment.id)&lt;br /&gt;
    signed_up_topics = []&lt;br /&gt;
    &lt;br /&gt;
    @bids.each do |bid|&lt;br /&gt;
      sign_up_topic = SignUpTopic.find_by(id: bid.signuptopic_id)&lt;br /&gt;
      signed_up_topics &amp;lt;&amp;lt; sign_up_topic if sign_up_topic&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    signed_up_topics &amp;amp;= @sign_up_topics&lt;br /&gt;
    @sign_up_topics -= signed_up_topics&lt;br /&gt;
    @bids = signed_up_topics&lt;br /&gt;
    @num_of_topics = @sign_up_topics.size&lt;br /&gt;
    @assigned_review_maps = []&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: @assignment.id, reviewer_id: @participant.id).each do |review_map|&lt;br /&gt;
      @assigned_review_maps &amp;lt;&amp;lt; review_map&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Explicitly render view since it's in the sign up sheet views&lt;br /&gt;
    render 'sign_up_sheet/review_bids_show'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Additional methods&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-2.-reviewbidshelper-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
 # Assigns bidding topics to reviewers&lt;br /&gt;
  def assign_bidding&lt;br /&gt;
      assignment = validate_assignment(params[:assignment_id])&lt;br /&gt;
&lt;br /&gt;
      reviewers = validate_reviewers(assignment.id)&lt;br /&gt;
      reviewer_ids = reviewers.map(&amp;amp;:id)&lt;br /&gt;
&lt;br /&gt;
      matched_topics = run_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
      if matched_topics.blank?&lt;br /&gt;
        flash[:alert] = 'Topic or assignment is missing'&lt;br /&gt;
        redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      leftover_topics = find_leftover_topics(assignment.id, matched_topics)&lt;br /&gt;
      assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.new.assign_review_topics(matched_topics)&lt;br /&gt;
      assignment.update!(can_choose_topic_to_review: false)&lt;br /&gt;
&lt;br /&gt;
      flash[:notice] = 'Reviewers were successfully assigned to topics.'&lt;br /&gt;
      redirect_back fallback_location: root_path&lt;br /&gt;
    rescue ArgumentError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ArgumentError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: e.message&lt;br /&gt;
    rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::RecordInvalid: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue ActiveRecord::ActiveRecordError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::ActiveRecordError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue StandardError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;StandardError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers. Please try again later.'&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  #service methods here ...&lt;br /&gt;
&lt;br /&gt;
  def find_leftover_topics(assignment_id, matched_topics)&lt;br /&gt;
    all_topic_ids = SignUpTopic.where(assignment_id: assignment_id).pluck(:id)&lt;br /&gt;
    assigned_topic_ids = matched_topics.map { |match| match[:topic_id] }&lt;br /&gt;
&lt;br /&gt;
    # Calculate leftover topics&lt;br /&gt;
    all_topic_ids - assigned_topic_ids&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
    return if leftover_topics.blank?&lt;br /&gt;
&lt;br /&gt;
    # Find non-bidders by excluding already matched reviewers&lt;br /&gt;
    non_bidders = reviewer_ids - matched_topics.keys&lt;br /&gt;
&lt;br /&gt;
    # Assign leftover topics to non-bidders in a round-robin fashion&lt;br /&gt;
    non_bidders.each_with_index do |reviewer_id, index|&lt;br /&gt;
      topic_id = leftover_topics[index % leftover_topics.length]&lt;br /&gt;
      ReviewBid.create(priority: 1, signuptopic_id: topic_id, participant_id: reviewer_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirm Functionality for Single-person Teams ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Make sure your code works on individual assignments, i.e., assignments where participants sign up for topics instead of teams. In this case, a team is created for each participant when they sign up. So the code should work for assignments to which either individuals or teams submit.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to make an update to focus on topic rather than team or have it where teams are updated based on topic id.We will probably utilize sign up topic or a similar object to accomplish this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-5-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bid Status Messaging ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| To make the functionality more intuitive, include a message to say how many students are eligible to submit bids, how many have submitted their bids, when the deadline for submitting bids is.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
The logic for these messages will go into the helper file. It is effectively the same reasoning as our migration of other business/functionality code from the controller to the helper to conform to the SRP. It will go hand in hand with the SignUpTopic portion of the code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-6-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Why are the methods in review_bid.rb class methods? Can we change them to instance methods or move it to helpers?&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-1.-reviewbid-model-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model Updates ====&lt;br /&gt;
Since the current class methods are tied to &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model instances, we will change them to instance methods. There is no need to move them to helpers.&lt;br /&gt;
&lt;br /&gt;
===== Review Bid Model Flow Before Refactoring =====&lt;br /&gt;
This sequence diagram describes how the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; interacts with the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model in assigning review topics to reviewers according to their bids. Once the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; action is initiated by the user, the controller gathers the IDs of reviewers and calls &amp;lt;code&amp;gt;ReviewBid.bidding_data&amp;lt;/code&amp;gt; to gather bidding information. That is sent out to an external bidding algorithm, and the matched topics that come back are used by &amp;lt;code&amp;gt;ReviewBid.assign_review_topics&amp;lt;/code&amp;gt; to create &amp;lt;code&amp;gt;ReviewResponseMap&amp;lt;/code&amp;gt; entries, assigning topics to reviewers for the assignment.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
  belongs_to :assignment, class_name: 'Assignment'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  def self.bidding_data(assignment_id, reviewer_ids)&lt;br /&gt;
    # create basic hash and set basic hash data&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'users' =&amp;gt; {}, 'max_accepted_proposals' =&amp;gt; [] }&lt;br /&gt;
    bidding_data['tid'] = SignUpTopic.where(assignment_id: assignment_id).ids&lt;br /&gt;
    bidding_data['max_accepted_proposals'] = Assignment.where(id: assignment_id).pluck(:num_reviews_allowed).first&lt;br /&gt;
&lt;br /&gt;
    # loop through reviewer_ids to get reviewer specific bidding data&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      bidding_data['users'][reviewer_id] = reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # assigns topics to reviews as matched by the webservice algorithm&lt;br /&gt;
  def self.assign_review_topics(assignment_id, reviewer_ids, matched_topics, _min_num_reviews = 2)&lt;br /&gt;
    # if review response map already created, delete it&lt;br /&gt;
    if ReviewResponseMap.where(reviewed_object_id: assignment_id)&lt;br /&gt;
      ReviewResponseMap.where(reviewed_object_id: assignment_id).destroy_all&lt;br /&gt;
    end&lt;br /&gt;
    # loop through reviewer_ids to assign reviews to each reviewer&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      topics_to_assign = matched_topics[reviewer_id.to_s]&lt;br /&gt;
      topics_to_assign.each do |topic|&lt;br /&gt;
        assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method to assign a single topic to a reviewer&lt;br /&gt;
  def self.assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic).pluck(:team_id).first&lt;br /&gt;
    team_to_review.nil? ? [] : ReviewResponseMap.create(reviewed_object_id: assignment_id, reviewer_id: reviewer_id, reviewee_id: team_to_review, type: 'ReviewResponseMap')&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method for getting individual reviewer_ids bidding data&lt;br /&gt;
  # returns user's bidding data hash&lt;br /&gt;
  def self.reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    reviewer_user_id = AssignmentParticipant.find(reviewer_id).user_id&lt;br /&gt;
    self_topic = SignedUpTeam.topic_id(assignment_id, reviewer_user_id)&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'otid' =&amp;gt; self_topic, 'priority' =&amp;gt; [], 'time' =&amp;gt; [] }&lt;br /&gt;
    bids = ReviewBid.where(participant_id: reviewer_id)&lt;br /&gt;
&lt;br /&gt;
    # loop through each bid for a topic to get specific data&lt;br /&gt;
    bids.each do |bid|&lt;br /&gt;
      bidding_data['tid'] &amp;lt;&amp;lt; bid.signuptopic_id&lt;br /&gt;
      bidding_data['priority'] &amp;lt;&amp;lt; bid.priority&lt;br /&gt;
      bidding_data['time'] &amp;lt;&amp;lt; bid.updated_at&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  # Instance method to get bidding data for this review bid&lt;br /&gt;
  def bidding_data&lt;br /&gt;
    {&lt;br /&gt;
      'tid' =&amp;gt; topic_ids,&lt;br /&gt;
      'otid' =&amp;gt; self_topic,&lt;br /&gt;
      'priority' =&amp;gt; priorities,&lt;br /&gt;
      'time' =&amp;gt; updated_times&lt;br /&gt;
    }&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # bid object to limit db touches for similar data acquisitions&lt;br /&gt;
  def bids&lt;br /&gt;
    @bids ||= ReviewBid.where(participant_id: participant_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def topic_ids&lt;br /&gt;
    bids.pluck(:signuptopic_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def priorities&lt;br /&gt;
    bids.pluck(:priority)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def updated_times&lt;br /&gt;
    bids.pluck(:updated_at)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self_topic&lt;br /&gt;
    return nil unless topic &amp;amp;&amp;amp; topic.assignment_id&lt;br /&gt;
&lt;br /&gt;
    SignedUpTeam.topic_id(topic.assignment_id, participant.user_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign topics to reviews&lt;br /&gt;
  def assign_review_topics(matched_topics)&lt;br /&gt;
    validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: topic.assignment_id).destroy_all&lt;br /&gt;
&lt;br /&gt;
    matched_topics.each do |reviewer_id, topics|&lt;br /&gt;
      Array(topics).each { |topic_id| assign_topic_to_reviewer(reviewer_id, topic_id) }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign a single topic to a reviewer&lt;br /&gt;
  def assign_topic_to_reviewer(reviewer_id, topic_id)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic_id).pluck(:team_id).first&lt;br /&gt;
    return if team_to_review.nil?&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.create(&lt;br /&gt;
      reviewed_object_id: topic.assignment_id,&lt;br /&gt;
      reviewer_id: reviewer_id,&lt;br /&gt;
      reviewee_id: team_to_review,&lt;br /&gt;
      type: 'ReviewResponseMap'&lt;br /&gt;
    )&lt;br /&gt;
    # error handling&lt;br /&gt;
  rescue StandardError =&amp;gt; e&lt;br /&gt;
    Rails.logger.error(&amp;quot;Failed to assign topic #{topic_id} to reviewer #{reviewer_id}: #{e.message}&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
    raise ArgumentError, 'Topic or assignment is missing' if topic.nil? || topic.assignment_id.nil?&lt;br /&gt;
    raise ArgumentError, 'Matched topics must be a Hash' unless matched_topics.is_a?(Hash)&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
Once we change them to instance methods, we’ll need to modify their use in the code to instantiate the object first.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;Placeholder for implementation screenshots (before and after)&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-3.-schema.db-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;Schema.db&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We also need to remove the foreign key reference to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model since the &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model already has access to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;, and that is the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; we should be referencing. This will also involve modifying the database to remove the foreign key reference to the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Steps required:&lt;br /&gt;
&lt;br /&gt;
# Review the records in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table in the database to see if there are any records and if those records have values for &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;&lt;br /&gt;
# Create a migration file using the command: &amp;lt;code&amp;gt;rails generate migration RemoveAssignmentFromReviewBids&amp;lt;/code&amp;gt;&lt;br /&gt;
# In the generated file, indicate the change that needs to be made using the change method and migration columns &amp;lt;code&amp;gt;remove_foreign_key&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;remove_column&amp;lt;/code&amp;gt;. Migration should be straightforward since there is no cascading behavior (i.e., &amp;lt;code&amp;gt;dependent:destroy&amp;lt;/code&amp;gt;) to worry about.&lt;br /&gt;
# Once comfortable with the updates to the generated file, run the migration using the &amp;lt;code&amp;gt;rails db:migrate&amp;lt;/code&amp;gt; command.&lt;br /&gt;
# Run a query in the DB to ensure no orphaned records are in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table after the updates.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;Placeholder for implementation screenshots (before and after)&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-4.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implement Edge Case Testing ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| In the previous implementation wiki, there are edge cases which are not exhaustively tested. Should test those edge cases thoroughly and add more edge case testing - link&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span class=&amp;quot;mark&amp;quot;&amp;gt;TODO&amp;lt;/span&amp;gt;:We just got initial test results back. We still need to figure out why tests are failing and what may be slipping through the cracks.[[File:Test expertiza.png|411x459px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-8-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design Patterns and Principles ===&lt;br /&gt;
Below are the design patterns and principles we plan to use to address the problem statements outlined in the project requirements:&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
** Controller Responsibility&lt;br /&gt;
*** To address the issue of authentication logic residing in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, we will refactor the code to ensure the controller solely handles request and response flows.&lt;br /&gt;
*** Authentication logic will be moved to dedicated methods in &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt;, making the codebase easier to maintain and test.&lt;br /&gt;
** View Simplification&lt;br /&gt;
*** Currently, the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; view contains excessive business logic, violating SRP.&lt;br /&gt;
*** We will move this logic into helper methods or models, ensuring each component has a single responsibility and improving code readability.&lt;br /&gt;
&lt;br /&gt;
* Separation of Concerns (SoC)&lt;br /&gt;
** By relocating authorization logic out of the controller, we separate authentication from request handling.&lt;br /&gt;
** Adhering to SoC enhances readability and maintainability by allowing each part of the application to focus on its specific role.&lt;br /&gt;
* Don't Repeat Yourself (DRY)&lt;br /&gt;
** We will inspect the existing implementation for code duplication, particularly in the controller actions.&lt;br /&gt;
** Any repetitive code will be refactored or removed to reduce redundancy and improve clarity.&lt;br /&gt;
&lt;br /&gt;
* Model-View-Controller (MVC) Adherence&lt;br /&gt;
** Moving business logic from the view aligns with MVC best practices.&lt;br /&gt;
** By ensuring the view only presents data without processing it, we maintain clear boundaries between the model, view, and controller layers.&lt;br /&gt;
&lt;br /&gt;
* Dependency Inversion Principle (DIP)&lt;br /&gt;
** We plan to update class methods to instance methods, allowing the controller to depend on abstractions rather than concrete implementations.&lt;br /&gt;
** This change leverages Ruby's duck typing, providing flexibility and making the code easier to maintain and test.&lt;br /&gt;
&lt;br /&gt;
* Factory Pattern&lt;br /&gt;
** We will implement a factory pattern for the common processing of &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpBid&amp;lt;/code&amp;gt;, encapsulating object creation and promoting code reuse.&lt;br /&gt;
&lt;br /&gt;
* Law of Demeter (Principle of Least Knowledge)&lt;br /&gt;
** We'll delegate interactions appropriately to prevent tightly coupled code, especially when dealing with topics and teams.&lt;br /&gt;
** Clear and intentional naming during refactoring will enhance code clarity and maintainability.&lt;br /&gt;
&lt;br /&gt;
* Adapter Pattern&lt;br /&gt;
** To add messaging to bid states for users, we'll use the adapter pattern to allow different components to work together without modifying their existing code.&lt;br /&gt;
&lt;br /&gt;
== Files Modified/Added ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-1.-controllers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
review_bids_controller.rb&lt;br /&gt;
&lt;br /&gt;
signup_sheet_controller.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-2.-models&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
review_bid.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-3.-helpers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
&lt;br /&gt;
authorization_helper.rb&lt;br /&gt;
&lt;br /&gt;
Review_bids_helper.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;viii.-test-plan&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
The Excel spreadsheet below details our test plan for testing the code we changed or created to ensure code coverage. The process involved reviewing the code and determining all the possible scenarios that needed to be covered based on what the method was doing, then creating test cases accordingly.&lt;br /&gt;
&lt;br /&gt;
* [https://docs.google.com/spreadsheets/d/1iHzOdSwlTtxOtf5VsNdwOaCP8NzYMmiJ/edit?usp=drive_link Open Test Matrix Spreadsheet]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/review_bids_controller_spec.rb ReviewBidsController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/sign_up_sheet_controller_spec.rb SignUpSheetController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/models/review_bid_spec.rb ReviewBid model updated test cases]&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-1.-mentor&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Ed Gehringer&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-2.-members&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Members ===&lt;br /&gt;
&lt;br /&gt;
* Gavin Teague&lt;br /&gt;
* Janice Uwujaren&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xi.-links&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
Previous Wiki: [https://wiki.expertiza.ncsu.edu/index.php/CSC/ECE_517_Fall_2021_-_E2151._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;Link&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xii.-references&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/document/d/1LHVacylQmB15I6VcYUA5nuC7GRRU9szHzZhjlPcE-PA/edit?tab=t.0#heading=h.u0oycoj6e0ev &amp;lt;u&amp;gt;Project Instructions&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;appendix-a-uml-class-diagram-plantuml-code-for-review-bids&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Juwujar</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160972</id>
		<title>CSC/ECE 517 Fall 2024 - E2485. Allow reviewers to bid on what to review</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160972"/>
		<updated>2024-12-10T22:54:29Z</updated>

		<summary type="html">&lt;p&gt;Juwujar: /* ReviewBidsController Updates */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
This project will fix the defects and improve the functionality in Expertiza of the Reviewer Bidding feature, a process where reviewers can bid for assignments they want to review. It will make the functionality smoother, more reliable, and align with Expertiza's collaborative learning objectives.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iii.-problem-statements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Problem Statements ==&lt;br /&gt;
&lt;br /&gt;
Assigning reviews to users on Expertiza involves a complex bidding algorithm that requires optimization before production deployment. Below are key areas we plan to address as per the project requirements:&lt;br /&gt;
* Code Refactoring and Best Practices&lt;br /&gt;
** '''Utilize Authentication Utilities''': Modify the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; function in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to use existing authentication utilities, enhancing code reusability and adhering to the Single Responsibility Principle (SRP) and Separation of Concerns (SoC).&lt;br /&gt;
** '''Eliminate Code Duplication''': Refactor &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; to comply with the Don't Repeat Yourself (DRY) principle.&lt;br /&gt;
** '''Rename Functions for Clarity''': Change &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; to better reflect its purpose.&lt;br /&gt;
** '''Refactor Methods in '''&amp;lt;code&amp;gt;review_bid.rb&amp;lt;/code&amp;gt;''':''': Convert class methods to instance methods or relocate them to helper classes for improved code organization.&lt;br /&gt;
* Functionality Enhancements&lt;br /&gt;
** '''Handle Missing Bids Gracefully''': Adjust the bidding algorithm to accommodate students who do not bid, allowing them to select from the remaining options.&lt;br /&gt;
** '''Support Both Individual and Team Assignments''': Ensure the code functions correctly for participant and team-based assignments when signing up for topics.&lt;br /&gt;
* User Interface Improvements&lt;br /&gt;
** '''Increase Transparency in the Bidding Process''': Display messages indicating how many students are eligible to bid, how many have submitted bids, and the deadline.&lt;br /&gt;
** '''Accurate Bid Status Indicators''': Ensure topic colors change correctly based on the number of outstanding bids, providing clear visual feedback to users.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Exhaustive Edge Case Testing''': Conduct thorough testing for edge cases not previously covered, adding additional test cases as needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iv.-design-goals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Design Goals ==&lt;br /&gt;
&lt;br /&gt;
While addressing the existing defects and enhancing the Reviewer Bidding feature, we plan to adhere to the following design guidelines:&lt;br /&gt;
&lt;br /&gt;
* Code Quality and Maintainability&lt;br /&gt;
** '''Improve Code Structure: '''Implement refactoring strategies to enhance code readability and maintainability, ensuring adherence to best practices like the DRY principle.&lt;br /&gt;
** '''Standardize Authentication Logic: '''Utilize a centralized authentication utility to promote consistency and reusability across the application.&lt;br /&gt;
* System Stability and Flexibility&lt;br /&gt;
** '''Enhance Algorithm Robustness: '''Modify the bidding algorithm to handle scenarios where students do not bid, ensuring the system remains stable under all conditions.&lt;br /&gt;
** '''Ensure Assignment Versatility: '''Design the system to seamlessly support both individual and team assignments without additional configuration.&lt;br /&gt;
* User Experience Enhancements&lt;br /&gt;
** '''Increase Bidding Transparency: '''Develop user interface elements that clearly display bidding status, deadlines, and participation metrics.&lt;br /&gt;
** '''Implement Dynamic Visual Feedback: '''Use real-time visual cues, such as color changes, to inform users about the bidding status instantly.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Develop Comprehensive Test Suites: '''Create extensive automated tests covering edge cases and critical functionalities to guarantee reliable system behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;v.-uml-diagram&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== UML Diagram ==&lt;br /&gt;
&lt;br /&gt;
Below is a visual representation of the models associated with the review bids functionality, depicting the data structures and their relationships.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml class diagram2.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi.-implementation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
'''ReviewBid: '''The &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; manages the bidding process, where participants indicate their preferences for reviewing different topics or assignments. This helps distribute review tasks fairly based on participants' interests.&lt;br /&gt;
&lt;br /&gt;
'''Assignment: '''The &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model represents team or individual assignments in Expertiza. It stores all the essential information required to keep the assignment process organized, like managing participants, setting due dates, forming teams, and overseeing the review process.&lt;br /&gt;
&lt;br /&gt;
'''SignUpTopic: '''The &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt; model identifies specific topics or areas within an assignment for which participants can sign up. It manages the availability of these topics, tracks bids, coordinates team sign-ups, and assigns topics based on both bids and participant availability. &lt;br /&gt;
&lt;br /&gt;
'''Participant: '''The &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model is about the individual users who participate in an assignment. This includes students, instructors, teaching assistants, or other relevant roles. This model is responsible for managing key user information, monitoring interactions with the assignment, keeping track of team memberships, and recording things like badges and review grades.&lt;br /&gt;
&lt;br /&gt;
== Implementation ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1.-decoupling-authorization-logic-from-reviewbidscontroller&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Decoupling Authorization Logic from &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| '''&amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; needs to use the authentication utilities'''&lt;br /&gt;
&lt;br /&gt;
The function &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; should use the available authentication utilities to support the Single Responsibility Principle and Separation of Concerns and to support code reusability.&lt;br /&gt;
&lt;br /&gt;
| '''Authentication Consistency:''' Remove authentication logic in the controller and call logic in the authentication utility class instead to ensure consistent use of the same logic across the application, foster reusability, and eliminate DRY.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
==== Authorization Flow Before Refactoring ====&lt;br /&gt;
The sequence diagram depicts the authorization process within the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;. When a user invokes an action, the controller checks if the action is allowed by verifying the user's role through the &amp;lt;code&amp;gt;current_role_name()&amp;lt;/code&amp;gt; method in the &amp;lt;code&amp;gt;ApplicationController&amp;lt;/code&amp;gt;. Depending on the action and the user's role, the controller either authorizes the user to proceed or denies access, ultimately executing the action logic if authorized and responding to the user.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 1.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We simplified the &amp;lt;code&amp;gt;action_allowed?&amp;lt;/code&amp;gt; method by decoupling authorization logic from the controller to use helper methods in the &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt; module. The &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; should not handle authentication, which is a violation of SRP. &lt;br /&gt;
&lt;br /&gt;
**Note:** We also made similar updates to the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; methods for &amp;lt;code&amp;gt;SignUpController&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;vertical-align:top;&amp;quot; | Original Code !! style=&amp;quot;vertical-align:top;&amp;quot; | Refactored Code&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index'&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator',&lt;br /&gt;
     'Student'].include? current_role_name and&lt;br /&gt;
        ((%w[list].include? action_name) ? are_needed_authorizations_present?(params[:id], &amp;quot;participant&amp;quot;, &amp;quot;reader&amp;quot;, &amp;quot;submitter&amp;quot;, &amp;quot;reviewer&amp;quot;) : true)&lt;br /&gt;
  else&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator'].include? current_role_name&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index', 'list'&lt;br /&gt;
    current_user_has_student_privileges? &amp;amp;&amp;amp; list_authorization_check&lt;br /&gt;
  else&lt;br /&gt;
    current_user_has_ta_privileges?&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; action ===&lt;br /&gt;
Previously, the &amp;lt;code&amp;gt;ReviewBidsController's index&amp;lt;/code&amp;gt; action was overloaded with responsibilities—it handled everything from locating and validating assignments to fetching review mappings and counting completed reviews. This all-in-one approach made the controller action messy. &lt;br /&gt;
&lt;br /&gt;
We have moved the business logic from the controller action and into dedicated service objects. With this change, we introduced two key service objects: &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; focuses on retrieving the participant along with their assignments, while the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; takes on the task of managing review mappings and calculating review-related stats, like how many reviews have been completed. &lt;br /&gt;
&lt;br /&gt;
This separation of responsibilities aligns with the Single Responsibility Principle. The logic for fetching assignments, retrieving review mappings, and counting reviews is now more flexible and reusable throughout the application. For example, we used methods in the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; in both the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; and the &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;, helping to avoid code duplication.The article https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial served as a guide on when and how to use service objects in Rails.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
  return unless current_user_id?(@participant.user_id)&lt;br /&gt;
  @assignment = @participant.assignment&lt;br /&gt;
  @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
  # Finding how many reviews have been completed&lt;br /&gt;
  @num_reviews_completed = 0&lt;br /&gt;
  @review_mappings.each do |map|&lt;br /&gt;
    @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # render view for completing reviews after review bidding has been completed&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @assignment = participant_service.assignment&lt;br /&gt;
  unless @assignment.is_a?(Assignment)&lt;br /&gt;
    flash[:error] = 'Assignment not found.'&lt;br /&gt;
    redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  @review_mappings = review_service.review_mappings&lt;br /&gt;
  @num_reviews_completed = review_service.review_counts[:completed]&lt;br /&gt;
&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Optimize &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Action functionality ===&lt;br /&gt;
In the earlier version of the set_priority action in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, the server only needed a couple of key pieces of information: an array of topic IDs (params[:topic]) and the participant ID (params[:id}). From there, it would figure out the assignment_id by using the first topic as a reference. While this worked in simple cases, it could lead to problems if any data was missing or didn’t line up correctly.&lt;br /&gt;
&lt;br /&gt;
With the new implementation, the action now expects clear and structured parameters, including participant_id, an array of topic IDs, and assignment_id. This change ensures that the server always receives complete and reliable information, preventing issues caused by missing or inconsistent data. It also removes the need for the server to perform unnecessary lookups, allowing the controller to focus on its main job: validating inputs (like making sure the topics in params[:topic] belong to the same assignment as the participant), updating the database, handling errors properly, and sending the correct response back to the view.&lt;br /&gt;
&lt;br /&gt;
While these updates have cleaned up the set_priority action, they still contain a lot of business logic, like validations and priority updates. To make the code even more maintainable, there’s room for further refactoring—perhaps moving some of this logic into a service class or model method to streamline the controller even more.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | Old Code&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    if params[:topic].nil?&lt;br /&gt;
      ReviewBid.where(participant_id: params[:id]).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      participant = AssignmentParticipant.find_by(id: params[:id])&lt;br /&gt;
      assignment_id = SignUpTopic.find(params[:topic].first).assignment.id&lt;br /&gt;
      @bids = ReviewBid.where(participant_id: params[:id])&lt;br /&gt;
      signed_up_topics = ReviewBid.where(participant_id: params[:id]).map(&amp;amp;:signuptopic_id)&lt;br /&gt;
      signed_up_topics -= params[:topic].map(&amp;amp;:to_i)&lt;br /&gt;
      signed_up_topics.each do |topic|&lt;br /&gt;
        ReviewBid.where(signuptopic_id: topic, participant_id: params[:id]).destroy_all&lt;br /&gt;
      end&lt;br /&gt;
      params[:topic].each_with_index do |topic_id, index|&lt;br /&gt;
        bid_existence = ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id])&lt;br /&gt;
        if bid_existence.empty?&lt;br /&gt;
          ReviewBid.create(priority: index + 1, signuptopic_id: topic_id, participant_id: params[:id], assignment_id: assignment_id)&lt;br /&gt;
        else&lt;br /&gt;
          ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id]).update_all(priority: index + 1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to action: 'show', assignment_id: params[:assignment_id], id: params[:id]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    participant_id = params[:participant_id].to_i&lt;br /&gt;
    selected_topic_ids = params[:topic]&amp;amp;.map(&amp;amp;:to_i) || []&lt;br /&gt;
    assignment_id = params[:assignment_id].to_i&lt;br /&gt;
&lt;br /&gt;
    unless current_user_can_modify_participant?(participant_id)&lt;br /&gt;
      flash[:error] = 'You are not authorized to modify these bids.'&lt;br /&gt;
      respond_to do |format|&lt;br /&gt;
        format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
        format.json { render json: { status: 'unauthorized' }, status: :unauthorized }&lt;br /&gt;
      end&lt;br /&gt;
      return&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    if selected_topic_ids.empty?&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      assignment = Assignment.find_by(id: assignment_id)&lt;br /&gt;
      if assignment.nil?&lt;br /&gt;
        flash[:error] = &amp;quot;Invalid assignment.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_assignment' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      unless SignUpTopic.where(id: selected_topic_ids, assignment_id: assignment_id).count == selected_topic_ids.size&lt;br /&gt;
        flash[:error] = &amp;quot;One or more selected topics are invalid.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_topics' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).where.not(signuptopic_id: selected_topic_ids).destroy_all&lt;br /&gt;
&lt;br /&gt;
      selected_topic_ids.each_with_index do |topic_id, index|&lt;br /&gt;
        bid = ReviewBid.find_or_initialize_by(signuptopic_id: topic_id, participant_id: participant_id)&lt;br /&gt;
        bid.priority = index + 1&lt;br /&gt;
        bid.assignment_id = assignment_id&lt;br /&gt;
        bid.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_to action: 'show', assignment_id: assignment_id, id: participant_id, notice: 'Review bids updated successfully.' }&lt;br /&gt;
      format.json { render json: { status: 'success' }, status: :ok }&lt;br /&gt;
    end&lt;br /&gt;
  rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
    flash[:error] = &amp;quot;Failed to update priorities: #{e.message}&amp;quot;&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
      format.json { render json: { status: 'error', message: e.message }, status: :unprocessable_entity }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Client Side Changes to Support &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Changes ====&lt;br /&gt;
After tidying up the server-side logic, we needed to refactor the client-side code to ensure it passed the correct information from the view to the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action. On the client side, we now grab the topicIDs and send them as &amp;lt;code&amp;gt;selected_topic_ids&amp;lt;/code&amp;gt;, allowing the controller to figure out which topics were selected and in what order.  &lt;br /&gt;
&lt;br /&gt;
We also extracted the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, making it a lot easier to send data with AJAX and keeping things flexible. When working with jQuery, AJAX is awesome because it lets us update the server without reloading the page, making the user experience smooth and responsive. When we pull the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, we can grab it along with other data, like the list of topics, and include it in the request. This way, we don’t have to rely on the URL to know which participant we’re working with, which makes the code cleaner and more adaptable.&lt;br /&gt;
&lt;br /&gt;
If we were to rely on the route (as opposed to using the DOM) for the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt;, every AJAX request would need to be tied to a specific URL structure. That’s limiting, especially if the route doesn’t naturally include the participant ID, as can be the case with collection routes versus member routes in Rails. &lt;br /&gt;
&lt;br /&gt;
Note: One thing that was interesting is the mix of JavaScript and CoffeeScript code. It wasn't clear if we are keeping both and refactoring to simple JavaScript (or possibly TypeScript in the future).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery -&amp;gt;&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: -&amp;gt;&lt;br /&gt;
      $.post($(this).data('update-url'), $(this).sortable('serialize'))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery(document).ready ($) -&amp;gt;&lt;br /&gt;
  sendUpdatedOrder = -&amp;gt;&lt;br /&gt;
    sortedIDs = $(this).sortable('toArray')&lt;br /&gt;
&lt;br /&gt;
    topicIDs = sortedIDs.map (id) -&amp;gt;&lt;br /&gt;
      id.replace 'topic_', ''&lt;br /&gt;
&lt;br /&gt;
    participant_id = $(this).data('participant-id')&lt;br /&gt;
&lt;br /&gt;
    assignment_id = new URL($(this).data('update-url'), window.location.origin).searchParams.get('assignment_id')&lt;br /&gt;
&lt;br /&gt;
    $.ajax&lt;br /&gt;
      type: 'POST'&lt;br /&gt;
      url: $(this).data('update-url')&lt;br /&gt;
      data:&lt;br /&gt;
        topic: topicIDs&lt;br /&gt;
        participant_id: participant_id&lt;br /&gt;
        assignment_id: assignment_id&lt;br /&gt;
      success: (response) -&amp;gt;&lt;br /&gt;
        console.log 'Selections updated successfully.'&lt;br /&gt;
      error: (xhr, status, error) -&amp;gt;&lt;br /&gt;
        alert 'An error occurred while saving your selections. Please try again.'&lt;br /&gt;
        console.error error&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: sendUpdatedOrder&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-1.-signupsheetcontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; Updates ===&lt;br /&gt;
&lt;br /&gt;
The ReviewBidsController's index method renders the review_bids_others_work action in &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt;. However, the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; does not include the controller action for this view. We need to find the old implementation of the controller action, add it back, and review and update it for any DRY violations.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # provides variables for reviewing page located at views/review_bids/others_work.html.erb&lt;br /&gt;
  def index&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
    return unless current_user_id?(@participant.user_id)&lt;br /&gt;
&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
    # Finding how many reviews have been completed&lt;br /&gt;
    @num_reviews_completed = 0&lt;br /&gt;
    @review_mappings.each do |map|&lt;br /&gt;
      @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # render view for completing reviews after review bidding has been completed&lt;br /&gt;
    render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
  end  &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-2.-reviewbidsotherswork-view-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;review_bids_others_work.html.erb&amp;lt;/code&amp;gt; View Updates ====&lt;br /&gt;
&lt;br /&gt;
The code only contains a view with this name and no associated controller action. Reviewing the code in the view, we noticed some SRP violations where there is much logic in the presentation layer. Set up helper methods in the controller to handle some of that logic or see if the model can handle some of that logic.&lt;br /&gt;
&lt;br /&gt;
Note: The view is technically part of the SignUpSheetController, but it's being rendered by the ReviewBidsController, which initially threw us off. After chatting with the professor, we decided not to change this view. He indicated that the plan is to reuse these views for both assignment bidding and review bidding.&lt;br /&gt;
&lt;br /&gt;
We would suggest building the assignment bidding and review bidding features separately first to make sure each one works on its own. Once that’s done, we can go back and clean things up by combining any shared functionality. This could include creating shared views or even introducing an abstract model to handle both types of bidding in a cleaner way. It could also mean replacing the ReviewBidsController with something more generic, like a BidsController, to keep things reusable and easier to maintain down the line.&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Rename &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; method ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; should be &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt;&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:''' Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The primary activity here is to refactor the method name &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; and fix the hardcoded URL. However, the match topic bidding algorithm no longer works since Heroku is no longer free, per the professor. The reference link below has detailed information about the web service that we can leverage to gain that understanding.&lt;br /&gt;
&lt;br /&gt;
Reference Link for Webservice Details: [https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Updated Implementation ====&lt;br /&gt;
The original requirement was to rename the run_bidding_algorithm method to assign_reviewers, but after digging into the code, we realized that renaming it didn’t make sense. The method runs the bidding algorithm to match topics, and there’s already a separate method to handle assigning review topics.&lt;br /&gt;
&lt;br /&gt;
However, we improved the logic in the assign_bidding method (which is called by the run_bidding_algorithm) by introducing a separate BiddingAlgorithmService object. This keeps the business logic out of the controller action, which makes things cleaner and more maintainable.&lt;br /&gt;
&lt;br /&gt;
==== review_bids_controller ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def run_bidding_algorithm&lt;br /&gt;
    review_bid = ReviewBid.new&lt;br /&gt;
    bidding_data = review_bid.bidding_data&lt;br /&gt;
    matched_topics = BiddingAlgorithmService.new(bidding_data).run&lt;br /&gt;
    raise ArgumentError, 'Failed to assign reviewers. Please try again later.' unless matched_topics&lt;br /&gt;
&lt;br /&gt;
    matched_topics&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== bidding_algorithm_service ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# frozen_string_literal: true&lt;br /&gt;
&lt;br /&gt;
# The `BiddingAlgorithmService` run the bid assignment algorithm&lt;br /&gt;
# Sends student IDs, topic IDs, student preferences, and timestamps to the web service&lt;br /&gt;
# The web service returns the matched assignments in the JSON response body&lt;br /&gt;
class BiddingAlgorithmService&lt;br /&gt;
  SERVICE_URL = 'http://app-csc517.herokuapp.com/match_topics'.freeze&lt;br /&gt;
&lt;br /&gt;
  # MOCK_DATA is based on the documentation detailing how the webservice behaves.&lt;br /&gt;
  # Reference: https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review#Webservice&lt;br /&gt;
  MOCK_DATA = {&lt;br /&gt;
    36239 =&amp;gt; [3970, 3972, 3975],&lt;br /&gt;
    36240 =&amp;gt; [3973, 3974, 3972],&lt;br /&gt;
    36241 =&amp;gt; [3969, 3971, 3972],&lt;br /&gt;
    36242 =&amp;gt; [3969, 3971, 3973],&lt;br /&gt;
    36243 =&amp;gt; [3969, 3970, 3971]&lt;br /&gt;
  }.freeze&lt;br /&gt;
&lt;br /&gt;
  def initialize(bidding_data)&lt;br /&gt;
    @bidding_data = bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def run&lt;br /&gt;
    return MOCK_DATA if Rails.application.config.use_mock_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
    perform_request&lt;br /&gt;
  rescue RestClient::ExceptionWithResponse, JSON::ParserError&lt;br /&gt;
    false&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def perform_request&lt;br /&gt;
    response = RestClient.post(&lt;br /&gt;
      SERVICE_URL,&lt;br /&gt;
      @bidding_data.to_json,&lt;br /&gt;
      content_type: :json,&lt;br /&gt;
      accept: :json&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    validate_response(response)&lt;br /&gt;
    JSON.parse(response.body)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def validate_response(response)&lt;br /&gt;
    raise 'Invalid response format' unless response.headers[:content_type] &amp;amp;&amp;amp; response.headers[:content_type].include?('application/json')&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Resolve Functionality Issues for Non Bidders ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Currently it doesn't work if some student does not bid. In this case, algorithm needs to be fixed to ignore anyone who didn’t bid, and let them choose from what’s left over.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to do several things within this controller. We will set a default state for non-bidders that we can point to to give them options to select topics to review from the leftovers. We will consolidate &amp;lt;code&amp;gt;@sign_up_topics&amp;lt;/code&amp;gt; to resolve the DRY violation. Lastly, we will remove business logic from the show method and push it to a new helper to conform to the SRP.&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # Additional method&lt;br /&gt;
&lt;br /&gt;
  # Provides variables for review bidding page&lt;br /&gt;
  def show&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id].to_i)&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @sign_up_topics = SignUpTopic.where(assignment_id: @assignment.id, private_to: nil)&lt;br /&gt;
    my_topic = SignedUpTeam.topic_id(@participant.parent_id, @participant.user_id)&lt;br /&gt;
    @sign_up_topics -= SignUpTopic.where(assignment_id: @assignment.id, id: my_topic)&lt;br /&gt;
    @num_participants = AssignmentParticipant.where(parent_id: @assignment.id).count&lt;br /&gt;
    @selected_topics = nil # This is used to list the topics assigned to review (i.e., select == assigned)&lt;br /&gt;
    @bids = ReviewBid.where(participant_id: @participant, assignment_id: @assignment.id)&lt;br /&gt;
    signed_up_topics = []&lt;br /&gt;
    &lt;br /&gt;
    @bids.each do |bid|&lt;br /&gt;
      sign_up_topic = SignUpTopic.find_by(id: bid.signuptopic_id)&lt;br /&gt;
      signed_up_topics &amp;lt;&amp;lt; sign_up_topic if sign_up_topic&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    signed_up_topics &amp;amp;= @sign_up_topics&lt;br /&gt;
    @sign_up_topics -= signed_up_topics&lt;br /&gt;
    @bids = signed_up_topics&lt;br /&gt;
    @num_of_topics = @sign_up_topics.size&lt;br /&gt;
    @assigned_review_maps = []&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: @assignment.id, reviewer_id: @participant.id).each do |review_map|&lt;br /&gt;
      @assigned_review_maps &amp;lt;&amp;lt; review_map&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Explicitly render view since it's in the sign up sheet views&lt;br /&gt;
    render 'sign_up_sheet/review_bids_show'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Additional methods&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-2.-reviewbidshelper-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
 # Assigns bidding topics to reviewers&lt;br /&gt;
  def assign_bidding&lt;br /&gt;
      assignment = validate_assignment(params[:assignment_id])&lt;br /&gt;
&lt;br /&gt;
      reviewers = validate_reviewers(assignment.id)&lt;br /&gt;
      reviewer_ids = reviewers.map(&amp;amp;:id)&lt;br /&gt;
&lt;br /&gt;
      matched_topics = run_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
      if matched_topics.blank?&lt;br /&gt;
        flash[:alert] = 'Topic or assignment is missing'&lt;br /&gt;
        redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      leftover_topics = find_leftover_topics(assignment.id, matched_topics)&lt;br /&gt;
      assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.new.assign_review_topics(matched_topics)&lt;br /&gt;
      assignment.update!(can_choose_topic_to_review: false)&lt;br /&gt;
&lt;br /&gt;
      flash[:notice] = 'Reviewers were successfully assigned to topics.'&lt;br /&gt;
      redirect_back fallback_location: root_path&lt;br /&gt;
    rescue ArgumentError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ArgumentError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: e.message&lt;br /&gt;
    rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::RecordInvalid: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue ActiveRecord::ActiveRecordError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::ActiveRecordError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue StandardError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;StandardError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers. Please try again later.'&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  #service methods here ...&lt;br /&gt;
&lt;br /&gt;
  def find_leftover_topics(assignment_id, matched_topics)&lt;br /&gt;
    all_topic_ids = SignUpTopic.where(assignment_id: assignment_id).pluck(:id)&lt;br /&gt;
    assigned_topic_ids = matched_topics.map { |match| match[:topic_id] }&lt;br /&gt;
&lt;br /&gt;
    # Calculate leftover topics&lt;br /&gt;
    all_topic_ids - assigned_topic_ids&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
    return if leftover_topics.blank?&lt;br /&gt;
&lt;br /&gt;
    # Find non-bidders by excluding already matched reviewers&lt;br /&gt;
    non_bidders = reviewer_ids - matched_topics.keys&lt;br /&gt;
&lt;br /&gt;
    # Assign leftover topics to non-bidders in a round-robin fashion&lt;br /&gt;
    non_bidders.each_with_index do |reviewer_id, index|&lt;br /&gt;
      topic_id = leftover_topics[index % leftover_topics.length]&lt;br /&gt;
      ReviewBid.create(priority: 1, signuptopic_id: topic_id, participant_id: reviewer_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirm Functionality for Single-person Teams ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Make sure your code works on individual assignments, i.e., assignments where participants sign up for topics instead of teams. In this case, a team is created for each participant when they sign up. So the code should work for assignments to which either individuals or teams submit.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to make an update to focus on topic rather than team or have it where teams are updated based on topic id.We will probably utilize sign up topic or a similar object to accomplish this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-5-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bid Status Messaging ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| To make the functionality more intuitive, include a message to say how many students are eligible to submit bids, how many have submitted their bids, when the deadline for submitting bids is.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
The logic for these messages will go into the helper file. It is effectively the same reasoning as our migration of other business/functionality code from the controller to the helper to conform to the SRP. It will go hand in hand with the SignUpTopic portion of the code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-6-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Why are the methods in review_bid.rb class methods? Can we change them to instance methods or move it to helpers?&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-1.-reviewbid-model-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model Updates ====&lt;br /&gt;
Since the current class methods are tied to &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model instances, we will change them to instance methods. There is no need to move them to helpers.&lt;br /&gt;
&lt;br /&gt;
===== Review Bid Model Flow Before Refactoring =====&lt;br /&gt;
This sequence diagram describes how the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; interacts with the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model in assigning review topics to reviewers according to their bids. Once the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; action is initiated by the user, the controller gathers the IDs of reviewers and calls &amp;lt;code&amp;gt;ReviewBid.bidding_data&amp;lt;/code&amp;gt; to gather bidding information. That is sent out to an external bidding algorithm, and the matched topics that come back are used by &amp;lt;code&amp;gt;ReviewBid.assign_review_topics&amp;lt;/code&amp;gt; to create &amp;lt;code&amp;gt;ReviewResponseMap&amp;lt;/code&amp;gt; entries, assigning topics to reviewers for the assignment.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
  belongs_to :assignment, class_name: 'Assignment'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  def self.bidding_data(assignment_id, reviewer_ids)&lt;br /&gt;
    # create basic hash and set basic hash data&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'users' =&amp;gt; {}, 'max_accepted_proposals' =&amp;gt; [] }&lt;br /&gt;
    bidding_data['tid'] = SignUpTopic.where(assignment_id: assignment_id).ids&lt;br /&gt;
    bidding_data['max_accepted_proposals'] = Assignment.where(id: assignment_id).pluck(:num_reviews_allowed).first&lt;br /&gt;
&lt;br /&gt;
    # loop through reviewer_ids to get reviewer specific bidding data&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      bidding_data['users'][reviewer_id] = reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # assigns topics to reviews as matched by the webservice algorithm&lt;br /&gt;
  def self.assign_review_topics(assignment_id, reviewer_ids, matched_topics, _min_num_reviews = 2)&lt;br /&gt;
    # if review response map already created, delete it&lt;br /&gt;
    if ReviewResponseMap.where(reviewed_object_id: assignment_id)&lt;br /&gt;
      ReviewResponseMap.where(reviewed_object_id: assignment_id).destroy_all&lt;br /&gt;
    end&lt;br /&gt;
    # loop through reviewer_ids to assign reviews to each reviewer&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      topics_to_assign = matched_topics[reviewer_id.to_s]&lt;br /&gt;
      topics_to_assign.each do |topic|&lt;br /&gt;
        assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method to assign a single topic to a reviewer&lt;br /&gt;
  def self.assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic).pluck(:team_id).first&lt;br /&gt;
    team_to_review.nil? ? [] : ReviewResponseMap.create(reviewed_object_id: assignment_id, reviewer_id: reviewer_id, reviewee_id: team_to_review, type: 'ReviewResponseMap')&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method for getting individual reviewer_ids bidding data&lt;br /&gt;
  # returns user's bidding data hash&lt;br /&gt;
  def self.reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    reviewer_user_id = AssignmentParticipant.find(reviewer_id).user_id&lt;br /&gt;
    self_topic = SignedUpTeam.topic_id(assignment_id, reviewer_user_id)&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'otid' =&amp;gt; self_topic, 'priority' =&amp;gt; [], 'time' =&amp;gt; [] }&lt;br /&gt;
    bids = ReviewBid.where(participant_id: reviewer_id)&lt;br /&gt;
&lt;br /&gt;
    # loop through each bid for a topic to get specific data&lt;br /&gt;
    bids.each do |bid|&lt;br /&gt;
      bidding_data['tid'] &amp;lt;&amp;lt; bid.signuptopic_id&lt;br /&gt;
      bidding_data['priority'] &amp;lt;&amp;lt; bid.priority&lt;br /&gt;
      bidding_data['time'] &amp;lt;&amp;lt; bid.updated_at&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  # Instance method to get bidding data for this review bid&lt;br /&gt;
  def bidding_data&lt;br /&gt;
    {&lt;br /&gt;
      'tid' =&amp;gt; topic_ids,&lt;br /&gt;
      'otid' =&amp;gt; self_topic,&lt;br /&gt;
      'priority' =&amp;gt; priorities,&lt;br /&gt;
      'time' =&amp;gt; updated_times&lt;br /&gt;
    }&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # bid object to limit db touches for similar data acquisitions&lt;br /&gt;
  def bids&lt;br /&gt;
    @bids ||= ReviewBid.where(participant_id: participant_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def topic_ids&lt;br /&gt;
    bids.pluck(:signuptopic_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def priorities&lt;br /&gt;
    bids.pluck(:priority)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def updated_times&lt;br /&gt;
    bids.pluck(:updated_at)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self_topic&lt;br /&gt;
    return nil unless topic &amp;amp;&amp;amp; topic.assignment_id&lt;br /&gt;
&lt;br /&gt;
    SignedUpTeam.topic_id(topic.assignment_id, participant.user_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign topics to reviews&lt;br /&gt;
  def assign_review_topics(matched_topics)&lt;br /&gt;
    validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: topic.assignment_id).destroy_all&lt;br /&gt;
&lt;br /&gt;
    matched_topics.each do |reviewer_id, topics|&lt;br /&gt;
      Array(topics).each { |topic_id| assign_topic_to_reviewer(reviewer_id, topic_id) }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign a single topic to a reviewer&lt;br /&gt;
  def assign_topic_to_reviewer(reviewer_id, topic_id)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic_id).pluck(:team_id).first&lt;br /&gt;
    return if team_to_review.nil?&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.create(&lt;br /&gt;
      reviewed_object_id: topic.assignment_id,&lt;br /&gt;
      reviewer_id: reviewer_id,&lt;br /&gt;
      reviewee_id: team_to_review,&lt;br /&gt;
      type: 'ReviewResponseMap'&lt;br /&gt;
    )&lt;br /&gt;
    # error handling&lt;br /&gt;
  rescue StandardError =&amp;gt; e&lt;br /&gt;
    Rails.logger.error(&amp;quot;Failed to assign topic #{topic_id} to reviewer #{reviewer_id}: #{e.message}&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
    raise ArgumentError, 'Topic or assignment is missing' if topic.nil? || topic.assignment_id.nil?&lt;br /&gt;
    raise ArgumentError, 'Matched topics must be a Hash' unless matched_topics.is_a?(Hash)&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
Once we change them to instance methods, we’ll need to modify their use in the code to instantiate the object first.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;Placeholder for implementation screenshots (before and after)&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-3.-schema.db-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;Schema.db&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We also need to remove the foreign key reference to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model since the &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model already has access to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;, and that is the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; we should be referencing. This will also involve modifying the database to remove the foreign key reference to the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Steps required:&lt;br /&gt;
&lt;br /&gt;
# Review the records in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table in the database to see if there are any records and if those records have values for &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;&lt;br /&gt;
# Create a migration file using the command: &amp;lt;code&amp;gt;rails generate migration RemoveAssignmentFromReviewBids&amp;lt;/code&amp;gt;&lt;br /&gt;
# In the generated file, indicate the change that needs to be made using the change method and migration columns &amp;lt;code&amp;gt;remove_foreign_key&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;remove_column&amp;lt;/code&amp;gt;. Migration should be straightforward since there is no cascading behavior (i.e., &amp;lt;code&amp;gt;dependent:destroy&amp;lt;/code&amp;gt;) to worry about.&lt;br /&gt;
# Once comfortable with the updates to the generated file, run the migration using the &amp;lt;code&amp;gt;rails db:migrate&amp;lt;/code&amp;gt; command.&lt;br /&gt;
# Run a query in the DB to ensure no orphaned records are in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table after the updates.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;Placeholder for implementation screenshots (before and after)&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-4.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implement Edge Case Testing ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| In the previous implementation wiki, there are edge cases which are not exhaustively tested. Should test those edge cases thoroughly and add more edge case testing - link&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span class=&amp;quot;mark&amp;quot;&amp;gt;TODO&amp;lt;/span&amp;gt;:We just got initial test results back. We still need to figure out why tests are failing and what may be slipping through the cracks.[[File:Test expertiza.png|411x459px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-8-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design Patterns and Principles ===&lt;br /&gt;
Below are the design patterns and principles we plan to use to address the problem statements outlined in the project requirements:&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
** Controller Responsibility&lt;br /&gt;
*** To address the issue of authentication logic residing in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, we will refactor the code to ensure the controller solely handles request and response flows.&lt;br /&gt;
*** Authentication logic will be moved to dedicated methods in &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt;, making the codebase easier to maintain and test.&lt;br /&gt;
** View Simplification&lt;br /&gt;
*** Currently, the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; view contains excessive business logic, violating SRP.&lt;br /&gt;
*** We will move this logic into helper methods or models, ensuring each component has a single responsibility and improving code readability.&lt;br /&gt;
&lt;br /&gt;
* Separation of Concerns (SoC)&lt;br /&gt;
** By relocating authorization logic out of the controller, we separate authentication from request handling.&lt;br /&gt;
** Adhering to SoC enhances readability and maintainability by allowing each part of the application to focus on its specific role.&lt;br /&gt;
* Don't Repeat Yourself (DRY)&lt;br /&gt;
** We will inspect the existing implementation for code duplication, particularly in the controller actions.&lt;br /&gt;
** Any repetitive code will be refactored or removed to reduce redundancy and improve clarity.&lt;br /&gt;
&lt;br /&gt;
* Model-View-Controller (MVC) Adherence&lt;br /&gt;
** Moving business logic from the view aligns with MVC best practices.&lt;br /&gt;
** By ensuring the view only presents data without processing it, we maintain clear boundaries between the model, view, and controller layers.&lt;br /&gt;
&lt;br /&gt;
* Dependency Inversion Principle (DIP)&lt;br /&gt;
** We plan to update class methods to instance methods, allowing the controller to depend on abstractions rather than concrete implementations.&lt;br /&gt;
** This change leverages Ruby's duck typing, providing flexibility and making the code easier to maintain and test.&lt;br /&gt;
&lt;br /&gt;
* Factory Pattern&lt;br /&gt;
** We will implement a factory pattern for the common processing of &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpBid&amp;lt;/code&amp;gt;, encapsulating object creation and promoting code reuse.&lt;br /&gt;
&lt;br /&gt;
* Law of Demeter (Principle of Least Knowledge)&lt;br /&gt;
** We'll delegate interactions appropriately to prevent tightly coupled code, especially when dealing with topics and teams.&lt;br /&gt;
** Clear and intentional naming during refactoring will enhance code clarity and maintainability.&lt;br /&gt;
&lt;br /&gt;
* Adapter Pattern&lt;br /&gt;
** To add messaging to bid states for users, we'll use the adapter pattern to allow different components to work together without modifying their existing code.&lt;br /&gt;
&lt;br /&gt;
== Files Modified/Added ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-1.-controllers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
review_bids_controller.rb&lt;br /&gt;
&lt;br /&gt;
signup_sheet_controller.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-2.-models&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
review_bid.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-3.-helpers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
&lt;br /&gt;
authorization_helper.rb&lt;br /&gt;
&lt;br /&gt;
Review_bids_helper.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;viii.-test-plan&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
The Excel spreadsheet below details our test plan for testing the code we changed or created to ensure code coverage. The process involved reviewing the code and determining all the possible scenarios that needed to be covered based on what the method was doing, then creating test cases accordingly.&lt;br /&gt;
&lt;br /&gt;
* [https://docs.google.com/spreadsheets/d/1iHzOdSwlTtxOtf5VsNdwOaCP8NzYMmiJ/edit?usp=drive_link Open Test Matrix Spreadsheet]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/review_bids_controller_spec.rb ReviewBidsController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/sign_up_sheet_controller_spec.rb SignUpSheetController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/models/review_bid_spec.rb ReviewBid model updated test cases]&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-1.-mentor&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Ed Gehringer&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-2.-members&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Members ===&lt;br /&gt;
&lt;br /&gt;
* Gavin Teague&lt;br /&gt;
* Janice Uwujaren&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xi.-links&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
Previous Wiki: [https://wiki.expertiza.ncsu.edu/index.php/CSC/ECE_517_Fall_2021_-_E2151._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;Link&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xii.-references&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/document/d/1LHVacylQmB15I6VcYUA5nuC7GRRU9szHzZhjlPcE-PA/edit?tab=t.0#heading=h.u0oycoj6e0ev &amp;lt;u&amp;gt;Project Instructions&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;appendix-a-uml-class-diagram-plantuml-code-for-review-bids&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Juwujar</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160971</id>
		<title>CSC/ECE 517 Fall 2024 - E2485. Allow reviewers to bid on what to review</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160971"/>
		<updated>2024-12-10T22:52:34Z</updated>

		<summary type="html">&lt;p&gt;Juwujar: /* Refactor ReviewBidsController#index action */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
This project will fix the defects and improve the functionality in Expertiza of the Reviewer Bidding feature, a process where reviewers can bid for assignments they want to review. It will make the functionality smoother, more reliable, and align with Expertiza's collaborative learning objectives.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iii.-problem-statements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Problem Statements ==&lt;br /&gt;
&lt;br /&gt;
Assigning reviews to users on Expertiza involves a complex bidding algorithm that requires optimization before production deployment. Below are key areas we plan to address as per the project requirements:&lt;br /&gt;
* Code Refactoring and Best Practices&lt;br /&gt;
** '''Utilize Authentication Utilities''': Modify the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; function in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to use existing authentication utilities, enhancing code reusability and adhering to the Single Responsibility Principle (SRP) and Separation of Concerns (SoC).&lt;br /&gt;
** '''Eliminate Code Duplication''': Refactor &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; to comply with the Don't Repeat Yourself (DRY) principle.&lt;br /&gt;
** '''Rename Functions for Clarity''': Change &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; to better reflect its purpose.&lt;br /&gt;
** '''Refactor Methods in '''&amp;lt;code&amp;gt;review_bid.rb&amp;lt;/code&amp;gt;''':''': Convert class methods to instance methods or relocate them to helper classes for improved code organization.&lt;br /&gt;
* Functionality Enhancements&lt;br /&gt;
** '''Handle Missing Bids Gracefully''': Adjust the bidding algorithm to accommodate students who do not bid, allowing them to select from the remaining options.&lt;br /&gt;
** '''Support Both Individual and Team Assignments''': Ensure the code functions correctly for participant and team-based assignments when signing up for topics.&lt;br /&gt;
* User Interface Improvements&lt;br /&gt;
** '''Increase Transparency in the Bidding Process''': Display messages indicating how many students are eligible to bid, how many have submitted bids, and the deadline.&lt;br /&gt;
** '''Accurate Bid Status Indicators''': Ensure topic colors change correctly based on the number of outstanding bids, providing clear visual feedback to users.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Exhaustive Edge Case Testing''': Conduct thorough testing for edge cases not previously covered, adding additional test cases as needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iv.-design-goals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Design Goals ==&lt;br /&gt;
&lt;br /&gt;
While addressing the existing defects and enhancing the Reviewer Bidding feature, we plan to adhere to the following design guidelines:&lt;br /&gt;
&lt;br /&gt;
* Code Quality and Maintainability&lt;br /&gt;
** '''Improve Code Structure: '''Implement refactoring strategies to enhance code readability and maintainability, ensuring adherence to best practices like the DRY principle.&lt;br /&gt;
** '''Standardize Authentication Logic: '''Utilize a centralized authentication utility to promote consistency and reusability across the application.&lt;br /&gt;
* System Stability and Flexibility&lt;br /&gt;
** '''Enhance Algorithm Robustness: '''Modify the bidding algorithm to handle scenarios where students do not bid, ensuring the system remains stable under all conditions.&lt;br /&gt;
** '''Ensure Assignment Versatility: '''Design the system to seamlessly support both individual and team assignments without additional configuration.&lt;br /&gt;
* User Experience Enhancements&lt;br /&gt;
** '''Increase Bidding Transparency: '''Develop user interface elements that clearly display bidding status, deadlines, and participation metrics.&lt;br /&gt;
** '''Implement Dynamic Visual Feedback: '''Use real-time visual cues, such as color changes, to inform users about the bidding status instantly.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Develop Comprehensive Test Suites: '''Create extensive automated tests covering edge cases and critical functionalities to guarantee reliable system behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;v.-uml-diagram&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== UML Diagram ==&lt;br /&gt;
&lt;br /&gt;
Below is a visual representation of the models associated with the review bids functionality, depicting the data structures and their relationships.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml class diagram2.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi.-implementation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
'''ReviewBid: '''The &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; manages the bidding process, where participants indicate their preferences for reviewing different topics or assignments. This helps distribute review tasks fairly based on participants' interests.&lt;br /&gt;
&lt;br /&gt;
'''Assignment: '''The &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model represents team or individual assignments in Expertiza. It stores all the essential information required to keep the assignment process organized, like managing participants, setting due dates, forming teams, and overseeing the review process.&lt;br /&gt;
&lt;br /&gt;
'''SignUpTopic: '''The &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt; model identifies specific topics or areas within an assignment for which participants can sign up. It manages the availability of these topics, tracks bids, coordinates team sign-ups, and assigns topics based on both bids and participant availability. &lt;br /&gt;
&lt;br /&gt;
'''Participant: '''The &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model is about the individual users who participate in an assignment. This includes students, instructors, teaching assistants, or other relevant roles. This model is responsible for managing key user information, monitoring interactions with the assignment, keeping track of team memberships, and recording things like badges and review grades.&lt;br /&gt;
&lt;br /&gt;
== Implementation ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1.-decoupling-authorization-logic-from-reviewbidscontroller&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Decoupling Authorization Logic from &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| '''&amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; needs to use the authentication utilities'''&lt;br /&gt;
&lt;br /&gt;
The function &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; should use the available authentication utilities to support the Single Responsibility Principle and Separation of Concerns and to support code reusability.&lt;br /&gt;
&lt;br /&gt;
| '''Authentication Consistency:''' Remove authentication logic in the controller and call logic in the authentication utility class instead to ensure consistent use of the same logic across the application, foster reusability, and eliminate DRY.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
==== Authorization Flow Before Refactoring ====&lt;br /&gt;
The sequence diagram depicts the authorization process within the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;. When a user invokes an action, the controller checks if the action is allowed by verifying the user's role through the &amp;lt;code&amp;gt;current_role_name()&amp;lt;/code&amp;gt; method in the &amp;lt;code&amp;gt;ApplicationController&amp;lt;/code&amp;gt;. Depending on the action and the user's role, the controller either authorizes the user to proceed or denies access, ultimately executing the action logic if authorized and responding to the user.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 1.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We simplified the &amp;lt;code&amp;gt;action_allowed?&amp;lt;/code&amp;gt; method by decoupling authorization logic from the controller to use helper methods in the &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt; module. The &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; should not handle authentication, which is a violation of SRP. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;vertical-align:top;&amp;quot; | Original Code !! style=&amp;quot;vertical-align:top;&amp;quot; | Refactored Code&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index'&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator',&lt;br /&gt;
     'Student'].include? current_role_name and&lt;br /&gt;
        ((%w[list].include? action_name) ? are_needed_authorizations_present?(params[:id], &amp;quot;participant&amp;quot;, &amp;quot;reader&amp;quot;, &amp;quot;submitter&amp;quot;, &amp;quot;reviewer&amp;quot;) : true)&lt;br /&gt;
  else&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator'].include? current_role_name&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index', 'list'&lt;br /&gt;
    current_user_has_student_privileges? &amp;amp;&amp;amp; list_authorization_check&lt;br /&gt;
  else&lt;br /&gt;
    current_user_has_ta_privileges?&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; action ===&lt;br /&gt;
Previously, the &amp;lt;code&amp;gt;ReviewBidsController's index&amp;lt;/code&amp;gt; action was overloaded with responsibilities—it handled everything from locating and validating assignments to fetching review mappings and counting completed reviews. This all-in-one approach made the controller action messy. &lt;br /&gt;
&lt;br /&gt;
We have moved the business logic from the controller action and into dedicated service objects. With this change, we introduced two key service objects: &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; focuses on retrieving the participant along with their assignments, while the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; takes on the task of managing review mappings and calculating review-related stats, like how many reviews have been completed. &lt;br /&gt;
&lt;br /&gt;
This separation of responsibilities aligns with the Single Responsibility Principle. The logic for fetching assignments, retrieving review mappings, and counting reviews is now more flexible and reusable throughout the application. For example, we used methods in the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; in both the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; and the &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;, helping to avoid code duplication.The article https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial served as a guide on when and how to use service objects in Rails.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
  return unless current_user_id?(@participant.user_id)&lt;br /&gt;
  @assignment = @participant.assignment&lt;br /&gt;
  @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
  # Finding how many reviews have been completed&lt;br /&gt;
  @num_reviews_completed = 0&lt;br /&gt;
  @review_mappings.each do |map|&lt;br /&gt;
    @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # render view for completing reviews after review bidding has been completed&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @assignment = participant_service.assignment&lt;br /&gt;
  unless @assignment.is_a?(Assignment)&lt;br /&gt;
    flash[:error] = 'Assignment not found.'&lt;br /&gt;
    redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  @review_mappings = review_service.review_mappings&lt;br /&gt;
  @num_reviews_completed = review_service.review_counts[:completed]&lt;br /&gt;
&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Optimize &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Action functionality ===&lt;br /&gt;
In the earlier version of the set_priority action in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, the server only needed a couple of key pieces of information: an array of topic IDs (params[:topic]) and the participant ID (params[:id}). From there, it would figure out the assignment_id by using the first topic as a reference. While this worked in simple cases, it could lead to problems if any data was missing or didn’t line up correctly.&lt;br /&gt;
&lt;br /&gt;
With the new implementation, the action now expects clear and structured parameters, including participant_id, an array of topic IDs, and assignment_id. This change ensures that the server always receives complete and reliable information, preventing issues caused by missing or inconsistent data. It also removes the need for the server to perform unnecessary lookups, allowing the controller to focus on its main job: validating inputs (like making sure the topics in params[:topic] belong to the same assignment as the participant), updating the database, handling errors properly, and sending the correct response back to the view.&lt;br /&gt;
&lt;br /&gt;
While these updates have cleaned up the set_priority action, they still contain a lot of business logic, like validations and priority updates. To make the code even more maintainable, there’s room for further refactoring—perhaps moving some of this logic into a service class or model method to streamline the controller even more.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | Old Code&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    if params[:topic].nil?&lt;br /&gt;
      ReviewBid.where(participant_id: params[:id]).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      participant = AssignmentParticipant.find_by(id: params[:id])&lt;br /&gt;
      assignment_id = SignUpTopic.find(params[:topic].first).assignment.id&lt;br /&gt;
      @bids = ReviewBid.where(participant_id: params[:id])&lt;br /&gt;
      signed_up_topics = ReviewBid.where(participant_id: params[:id]).map(&amp;amp;:signuptopic_id)&lt;br /&gt;
      signed_up_topics -= params[:topic].map(&amp;amp;:to_i)&lt;br /&gt;
      signed_up_topics.each do |topic|&lt;br /&gt;
        ReviewBid.where(signuptopic_id: topic, participant_id: params[:id]).destroy_all&lt;br /&gt;
      end&lt;br /&gt;
      params[:topic].each_with_index do |topic_id, index|&lt;br /&gt;
        bid_existence = ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id])&lt;br /&gt;
        if bid_existence.empty?&lt;br /&gt;
          ReviewBid.create(priority: index + 1, signuptopic_id: topic_id, participant_id: params[:id], assignment_id: assignment_id)&lt;br /&gt;
        else&lt;br /&gt;
          ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id]).update_all(priority: index + 1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to action: 'show', assignment_id: params[:assignment_id], id: params[:id]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    participant_id = params[:participant_id].to_i&lt;br /&gt;
    selected_topic_ids = params[:topic]&amp;amp;.map(&amp;amp;:to_i) || []&lt;br /&gt;
    assignment_id = params[:assignment_id].to_i&lt;br /&gt;
&lt;br /&gt;
    unless current_user_can_modify_participant?(participant_id)&lt;br /&gt;
      flash[:error] = 'You are not authorized to modify these bids.'&lt;br /&gt;
      respond_to do |format|&lt;br /&gt;
        format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
        format.json { render json: { status: 'unauthorized' }, status: :unauthorized }&lt;br /&gt;
      end&lt;br /&gt;
      return&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    if selected_topic_ids.empty?&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      assignment = Assignment.find_by(id: assignment_id)&lt;br /&gt;
      if assignment.nil?&lt;br /&gt;
        flash[:error] = &amp;quot;Invalid assignment.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_assignment' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      unless SignUpTopic.where(id: selected_topic_ids, assignment_id: assignment_id).count == selected_topic_ids.size&lt;br /&gt;
        flash[:error] = &amp;quot;One or more selected topics are invalid.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_topics' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).where.not(signuptopic_id: selected_topic_ids).destroy_all&lt;br /&gt;
&lt;br /&gt;
      selected_topic_ids.each_with_index do |topic_id, index|&lt;br /&gt;
        bid = ReviewBid.find_or_initialize_by(signuptopic_id: topic_id, participant_id: participant_id)&lt;br /&gt;
        bid.priority = index + 1&lt;br /&gt;
        bid.assignment_id = assignment_id&lt;br /&gt;
        bid.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_to action: 'show', assignment_id: assignment_id, id: participant_id, notice: 'Review bids updated successfully.' }&lt;br /&gt;
      format.json { render json: { status: 'success' }, status: :ok }&lt;br /&gt;
    end&lt;br /&gt;
  rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
    flash[:error] = &amp;quot;Failed to update priorities: #{e.message}&amp;quot;&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
      format.json { render json: { status: 'error', message: e.message }, status: :unprocessable_entity }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Client Side Changes to Support &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Changes ====&lt;br /&gt;
After tidying up the server-side logic, we needed to refactor the client-side code to ensure it passed the correct information from the view to the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action. On the client side, we now grab the topicIDs and send them as &amp;lt;code&amp;gt;selected_topic_ids&amp;lt;/code&amp;gt;, allowing the controller to figure out which topics were selected and in what order.  &lt;br /&gt;
&lt;br /&gt;
We also extracted the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, making it a lot easier to send data with AJAX and keeping things flexible. When working with jQuery, AJAX is awesome because it lets us update the server without reloading the page, making the user experience smooth and responsive. When we pull the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, we can grab it along with other data, like the list of topics, and include it in the request. This way, we don’t have to rely on the URL to know which participant we’re working with, which makes the code cleaner and more adaptable.&lt;br /&gt;
&lt;br /&gt;
If we were to rely on the route (as opposed to using the DOM) for the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt;, every AJAX request would need to be tied to a specific URL structure. That’s limiting, especially if the route doesn’t naturally include the participant ID, as can be the case with collection routes versus member routes in Rails. &lt;br /&gt;
&lt;br /&gt;
Note: One thing that was interesting is the mix of JavaScript and CoffeeScript code. It wasn't clear if we are keeping both and refactoring to simple JavaScript (or possibly TypeScript in the future).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery -&amp;gt;&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: -&amp;gt;&lt;br /&gt;
      $.post($(this).data('update-url'), $(this).sortable('serialize'))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery(document).ready ($) -&amp;gt;&lt;br /&gt;
  sendUpdatedOrder = -&amp;gt;&lt;br /&gt;
    sortedIDs = $(this).sortable('toArray')&lt;br /&gt;
&lt;br /&gt;
    topicIDs = sortedIDs.map (id) -&amp;gt;&lt;br /&gt;
      id.replace 'topic_', ''&lt;br /&gt;
&lt;br /&gt;
    participant_id = $(this).data('participant-id')&lt;br /&gt;
&lt;br /&gt;
    assignment_id = new URL($(this).data('update-url'), window.location.origin).searchParams.get('assignment_id')&lt;br /&gt;
&lt;br /&gt;
    $.ajax&lt;br /&gt;
      type: 'POST'&lt;br /&gt;
      url: $(this).data('update-url')&lt;br /&gt;
      data:&lt;br /&gt;
        topic: topicIDs&lt;br /&gt;
        participant_id: participant_id&lt;br /&gt;
        assignment_id: assignment_id&lt;br /&gt;
      success: (response) -&amp;gt;&lt;br /&gt;
        console.log 'Selections updated successfully.'&lt;br /&gt;
      error: (xhr, status, error) -&amp;gt;&lt;br /&gt;
        alert 'An error occurred while saving your selections. Please try again.'&lt;br /&gt;
        console.error error&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: sendUpdatedOrder&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-1.-signupsheetcontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; Updates ===&lt;br /&gt;
&lt;br /&gt;
The ReviewBidsController's index method renders the review_bids_others_work action in &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt;. However, the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; does not include the controller action for this view. We need to find the old implementation of the controller action, add it back, and review and update it for any DRY violations.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # provides variables for reviewing page located at views/review_bids/others_work.html.erb&lt;br /&gt;
  def index&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
    return unless current_user_id?(@participant.user_id)&lt;br /&gt;
&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
    # Finding how many reviews have been completed&lt;br /&gt;
    @num_reviews_completed = 0&lt;br /&gt;
    @review_mappings.each do |map|&lt;br /&gt;
      @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # render view for completing reviews after review bidding has been completed&lt;br /&gt;
    render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
  end  &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-2.-reviewbidsotherswork-view-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;review_bids_others_work.html.erb&amp;lt;/code&amp;gt; View Updates ====&lt;br /&gt;
&lt;br /&gt;
The code only contains a view with this name and no associated controller action. Reviewing the code in the view, we noticed some SRP violations where there is much logic in the presentation layer. Set up helper methods in the controller to handle some of that logic or see if the model can handle some of that logic.&lt;br /&gt;
&lt;br /&gt;
Note: The view is technically part of the SignUpSheetController, but it's being rendered by the ReviewBidsController, which initially threw us off. After chatting with the professor, we decided not to change this view. He indicated that the plan is to reuse these views for both assignment bidding and review bidding.&lt;br /&gt;
&lt;br /&gt;
We would suggest building the assignment bidding and review bidding features separately first to make sure each one works on its own. Once that’s done, we can go back and clean things up by combining any shared functionality. This could include creating shared views or even introducing an abstract model to handle both types of bidding in a cleaner way. It could also mean replacing the ReviewBidsController with something more generic, like a BidsController, to keep things reusable and easier to maintain down the line.&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Rename &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; method ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; should be &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt;&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:''' Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The primary activity here is to refactor the method name &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; and fix the hardcoded URL. However, the match topic bidding algorithm no longer works since Heroku is no longer free, per the professor. The reference link below has detailed information about the web service that we can leverage to gain that understanding.&lt;br /&gt;
&lt;br /&gt;
Reference Link for Webservice Details: [https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Updated Implementation ====&lt;br /&gt;
The original requirement was to rename the run_bidding_algorithm method to assign_reviewers, but after digging into the code, we realized that renaming it didn’t make sense. The method runs the bidding algorithm to match topics, and there’s already a separate method to handle assigning review topics.&lt;br /&gt;
&lt;br /&gt;
However, we improved the logic in the assign_bidding method (which is called by the run_bidding_algorithm) by introducing a separate BiddingAlgorithmService object. This keeps the business logic out of the controller action, which makes things cleaner and more maintainable.&lt;br /&gt;
&lt;br /&gt;
==== review_bids_controller ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def run_bidding_algorithm&lt;br /&gt;
    review_bid = ReviewBid.new&lt;br /&gt;
    bidding_data = review_bid.bidding_data&lt;br /&gt;
    matched_topics = BiddingAlgorithmService.new(bidding_data).run&lt;br /&gt;
    raise ArgumentError, 'Failed to assign reviewers. Please try again later.' unless matched_topics&lt;br /&gt;
&lt;br /&gt;
    matched_topics&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== bidding_algorithm_service ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# frozen_string_literal: true&lt;br /&gt;
&lt;br /&gt;
# The `BiddingAlgorithmService` run the bid assignment algorithm&lt;br /&gt;
# Sends student IDs, topic IDs, student preferences, and timestamps to the web service&lt;br /&gt;
# The web service returns the matched assignments in the JSON response body&lt;br /&gt;
class BiddingAlgorithmService&lt;br /&gt;
  SERVICE_URL = 'http://app-csc517.herokuapp.com/match_topics'.freeze&lt;br /&gt;
&lt;br /&gt;
  # MOCK_DATA is based on the documentation detailing how the webservice behaves.&lt;br /&gt;
  # Reference: https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review#Webservice&lt;br /&gt;
  MOCK_DATA = {&lt;br /&gt;
    36239 =&amp;gt; [3970, 3972, 3975],&lt;br /&gt;
    36240 =&amp;gt; [3973, 3974, 3972],&lt;br /&gt;
    36241 =&amp;gt; [3969, 3971, 3972],&lt;br /&gt;
    36242 =&amp;gt; [3969, 3971, 3973],&lt;br /&gt;
    36243 =&amp;gt; [3969, 3970, 3971]&lt;br /&gt;
  }.freeze&lt;br /&gt;
&lt;br /&gt;
  def initialize(bidding_data)&lt;br /&gt;
    @bidding_data = bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def run&lt;br /&gt;
    return MOCK_DATA if Rails.application.config.use_mock_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
    perform_request&lt;br /&gt;
  rescue RestClient::ExceptionWithResponse, JSON::ParserError&lt;br /&gt;
    false&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def perform_request&lt;br /&gt;
    response = RestClient.post(&lt;br /&gt;
      SERVICE_URL,&lt;br /&gt;
      @bidding_data.to_json,&lt;br /&gt;
      content_type: :json,&lt;br /&gt;
      accept: :json&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    validate_response(response)&lt;br /&gt;
    JSON.parse(response.body)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def validate_response(response)&lt;br /&gt;
    raise 'Invalid response format' unless response.headers[:content_type] &amp;amp;&amp;amp; response.headers[:content_type].include?('application/json')&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Resolve Functionality Issues for Non Bidders ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Currently it doesn't work if some student does not bid. In this case, algorithm needs to be fixed to ignore anyone who didn’t bid, and let them choose from what’s left over.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to do several things within this controller. We will set a default state for non-bidders that we can point to to give them options to select topics to review from the leftovers. We will consolidate &amp;lt;code&amp;gt;@sign_up_topics&amp;lt;/code&amp;gt; to resolve the DRY violation. Lastly, we will remove business logic from the show method and push it to a new helper to conform to the SRP.&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # Additional method&lt;br /&gt;
&lt;br /&gt;
  # Provides variables for review bidding page&lt;br /&gt;
  def show&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id].to_i)&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @sign_up_topics = SignUpTopic.where(assignment_id: @assignment.id, private_to: nil)&lt;br /&gt;
    my_topic = SignedUpTeam.topic_id(@participant.parent_id, @participant.user_id)&lt;br /&gt;
    @sign_up_topics -= SignUpTopic.where(assignment_id: @assignment.id, id: my_topic)&lt;br /&gt;
    @num_participants = AssignmentParticipant.where(parent_id: @assignment.id).count&lt;br /&gt;
    @selected_topics = nil # This is used to list the topics assigned to review (i.e., select == assigned)&lt;br /&gt;
    @bids = ReviewBid.where(participant_id: @participant, assignment_id: @assignment.id)&lt;br /&gt;
    signed_up_topics = []&lt;br /&gt;
    &lt;br /&gt;
    @bids.each do |bid|&lt;br /&gt;
      sign_up_topic = SignUpTopic.find_by(id: bid.signuptopic_id)&lt;br /&gt;
      signed_up_topics &amp;lt;&amp;lt; sign_up_topic if sign_up_topic&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    signed_up_topics &amp;amp;= @sign_up_topics&lt;br /&gt;
    @sign_up_topics -= signed_up_topics&lt;br /&gt;
    @bids = signed_up_topics&lt;br /&gt;
    @num_of_topics = @sign_up_topics.size&lt;br /&gt;
    @assigned_review_maps = []&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: @assignment.id, reviewer_id: @participant.id).each do |review_map|&lt;br /&gt;
      @assigned_review_maps &amp;lt;&amp;lt; review_map&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Explicitly render view since it's in the sign up sheet views&lt;br /&gt;
    render 'sign_up_sheet/review_bids_show'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Additional methods&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-2.-reviewbidshelper-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
 # Assigns bidding topics to reviewers&lt;br /&gt;
  def assign_bidding&lt;br /&gt;
      assignment = validate_assignment(params[:assignment_id])&lt;br /&gt;
&lt;br /&gt;
      reviewers = validate_reviewers(assignment.id)&lt;br /&gt;
      reviewer_ids = reviewers.map(&amp;amp;:id)&lt;br /&gt;
&lt;br /&gt;
      matched_topics = run_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
      if matched_topics.blank?&lt;br /&gt;
        flash[:alert] = 'Topic or assignment is missing'&lt;br /&gt;
        redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      leftover_topics = find_leftover_topics(assignment.id, matched_topics)&lt;br /&gt;
      assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.new.assign_review_topics(matched_topics)&lt;br /&gt;
      assignment.update!(can_choose_topic_to_review: false)&lt;br /&gt;
&lt;br /&gt;
      flash[:notice] = 'Reviewers were successfully assigned to topics.'&lt;br /&gt;
      redirect_back fallback_location: root_path&lt;br /&gt;
    rescue ArgumentError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ArgumentError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: e.message&lt;br /&gt;
    rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::RecordInvalid: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue ActiveRecord::ActiveRecordError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::ActiveRecordError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue StandardError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;StandardError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers. Please try again later.'&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  #service methods here ...&lt;br /&gt;
&lt;br /&gt;
  def find_leftover_topics(assignment_id, matched_topics)&lt;br /&gt;
    all_topic_ids = SignUpTopic.where(assignment_id: assignment_id).pluck(:id)&lt;br /&gt;
    assigned_topic_ids = matched_topics.map { |match| match[:topic_id] }&lt;br /&gt;
&lt;br /&gt;
    # Calculate leftover topics&lt;br /&gt;
    all_topic_ids - assigned_topic_ids&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
    return if leftover_topics.blank?&lt;br /&gt;
&lt;br /&gt;
    # Find non-bidders by excluding already matched reviewers&lt;br /&gt;
    non_bidders = reviewer_ids - matched_topics.keys&lt;br /&gt;
&lt;br /&gt;
    # Assign leftover topics to non-bidders in a round-robin fashion&lt;br /&gt;
    non_bidders.each_with_index do |reviewer_id, index|&lt;br /&gt;
      topic_id = leftover_topics[index % leftover_topics.length]&lt;br /&gt;
      ReviewBid.create(priority: 1, signuptopic_id: topic_id, participant_id: reviewer_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirm Functionality for Single-person Teams ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Make sure your code works on individual assignments, i.e., assignments where participants sign up for topics instead of teams. In this case, a team is created for each participant when they sign up. So the code should work for assignments to which either individuals or teams submit.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to make an update to focus on topic rather than team or have it where teams are updated based on topic id.We will probably utilize sign up topic or a similar object to accomplish this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-5-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bid Status Messaging ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| To make the functionality more intuitive, include a message to say how many students are eligible to submit bids, how many have submitted their bids, when the deadline for submitting bids is.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
The logic for these messages will go into the helper file. It is effectively the same reasoning as our migration of other business/functionality code from the controller to the helper to conform to the SRP. It will go hand in hand with the SignUpTopic portion of the code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-6-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Why are the methods in review_bid.rb class methods? Can we change them to instance methods or move it to helpers?&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-1.-reviewbid-model-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model Updates ====&lt;br /&gt;
Since the current class methods are tied to &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model instances, we will change them to instance methods. There is no need to move them to helpers.&lt;br /&gt;
&lt;br /&gt;
===== Review Bid Model Flow Before Refactoring =====&lt;br /&gt;
This sequence diagram describes how the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; interacts with the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model in assigning review topics to reviewers according to their bids. Once the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; action is initiated by the user, the controller gathers the IDs of reviewers and calls &amp;lt;code&amp;gt;ReviewBid.bidding_data&amp;lt;/code&amp;gt; to gather bidding information. That is sent out to an external bidding algorithm, and the matched topics that come back are used by &amp;lt;code&amp;gt;ReviewBid.assign_review_topics&amp;lt;/code&amp;gt; to create &amp;lt;code&amp;gt;ReviewResponseMap&amp;lt;/code&amp;gt; entries, assigning topics to reviewers for the assignment.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
  belongs_to :assignment, class_name: 'Assignment'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  def self.bidding_data(assignment_id, reviewer_ids)&lt;br /&gt;
    # create basic hash and set basic hash data&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'users' =&amp;gt; {}, 'max_accepted_proposals' =&amp;gt; [] }&lt;br /&gt;
    bidding_data['tid'] = SignUpTopic.where(assignment_id: assignment_id).ids&lt;br /&gt;
    bidding_data['max_accepted_proposals'] = Assignment.where(id: assignment_id).pluck(:num_reviews_allowed).first&lt;br /&gt;
&lt;br /&gt;
    # loop through reviewer_ids to get reviewer specific bidding data&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      bidding_data['users'][reviewer_id] = reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # assigns topics to reviews as matched by the webservice algorithm&lt;br /&gt;
  def self.assign_review_topics(assignment_id, reviewer_ids, matched_topics, _min_num_reviews = 2)&lt;br /&gt;
    # if review response map already created, delete it&lt;br /&gt;
    if ReviewResponseMap.where(reviewed_object_id: assignment_id)&lt;br /&gt;
      ReviewResponseMap.where(reviewed_object_id: assignment_id).destroy_all&lt;br /&gt;
    end&lt;br /&gt;
    # loop through reviewer_ids to assign reviews to each reviewer&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      topics_to_assign = matched_topics[reviewer_id.to_s]&lt;br /&gt;
      topics_to_assign.each do |topic|&lt;br /&gt;
        assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method to assign a single topic to a reviewer&lt;br /&gt;
  def self.assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic).pluck(:team_id).first&lt;br /&gt;
    team_to_review.nil? ? [] : ReviewResponseMap.create(reviewed_object_id: assignment_id, reviewer_id: reviewer_id, reviewee_id: team_to_review, type: 'ReviewResponseMap')&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method for getting individual reviewer_ids bidding data&lt;br /&gt;
  # returns user's bidding data hash&lt;br /&gt;
  def self.reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    reviewer_user_id = AssignmentParticipant.find(reviewer_id).user_id&lt;br /&gt;
    self_topic = SignedUpTeam.topic_id(assignment_id, reviewer_user_id)&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'otid' =&amp;gt; self_topic, 'priority' =&amp;gt; [], 'time' =&amp;gt; [] }&lt;br /&gt;
    bids = ReviewBid.where(participant_id: reviewer_id)&lt;br /&gt;
&lt;br /&gt;
    # loop through each bid for a topic to get specific data&lt;br /&gt;
    bids.each do |bid|&lt;br /&gt;
      bidding_data['tid'] &amp;lt;&amp;lt; bid.signuptopic_id&lt;br /&gt;
      bidding_data['priority'] &amp;lt;&amp;lt; bid.priority&lt;br /&gt;
      bidding_data['time'] &amp;lt;&amp;lt; bid.updated_at&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  # Instance method to get bidding data for this review bid&lt;br /&gt;
  def bidding_data&lt;br /&gt;
    {&lt;br /&gt;
      'tid' =&amp;gt; topic_ids,&lt;br /&gt;
      'otid' =&amp;gt; self_topic,&lt;br /&gt;
      'priority' =&amp;gt; priorities,&lt;br /&gt;
      'time' =&amp;gt; updated_times&lt;br /&gt;
    }&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # bid object to limit db touches for similar data acquisitions&lt;br /&gt;
  def bids&lt;br /&gt;
    @bids ||= ReviewBid.where(participant_id: participant_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def topic_ids&lt;br /&gt;
    bids.pluck(:signuptopic_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def priorities&lt;br /&gt;
    bids.pluck(:priority)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def updated_times&lt;br /&gt;
    bids.pluck(:updated_at)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self_topic&lt;br /&gt;
    return nil unless topic &amp;amp;&amp;amp; topic.assignment_id&lt;br /&gt;
&lt;br /&gt;
    SignedUpTeam.topic_id(topic.assignment_id, participant.user_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign topics to reviews&lt;br /&gt;
  def assign_review_topics(matched_topics)&lt;br /&gt;
    validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: topic.assignment_id).destroy_all&lt;br /&gt;
&lt;br /&gt;
    matched_topics.each do |reviewer_id, topics|&lt;br /&gt;
      Array(topics).each { |topic_id| assign_topic_to_reviewer(reviewer_id, topic_id) }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign a single topic to a reviewer&lt;br /&gt;
  def assign_topic_to_reviewer(reviewer_id, topic_id)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic_id).pluck(:team_id).first&lt;br /&gt;
    return if team_to_review.nil?&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.create(&lt;br /&gt;
      reviewed_object_id: topic.assignment_id,&lt;br /&gt;
      reviewer_id: reviewer_id,&lt;br /&gt;
      reviewee_id: team_to_review,&lt;br /&gt;
      type: 'ReviewResponseMap'&lt;br /&gt;
    )&lt;br /&gt;
    # error handling&lt;br /&gt;
  rescue StandardError =&amp;gt; e&lt;br /&gt;
    Rails.logger.error(&amp;quot;Failed to assign topic #{topic_id} to reviewer #{reviewer_id}: #{e.message}&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
    raise ArgumentError, 'Topic or assignment is missing' if topic.nil? || topic.assignment_id.nil?&lt;br /&gt;
    raise ArgumentError, 'Matched topics must be a Hash' unless matched_topics.is_a?(Hash)&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
Once we change them to instance methods, we’ll need to modify their use in the code to instantiate the object first.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;Placeholder for implementation screenshots (before and after)&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-3.-schema.db-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;Schema.db&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We also need to remove the foreign key reference to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model since the &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model already has access to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;, and that is the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; we should be referencing. This will also involve modifying the database to remove the foreign key reference to the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Steps required:&lt;br /&gt;
&lt;br /&gt;
# Review the records in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table in the database to see if there are any records and if those records have values for &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;&lt;br /&gt;
# Create a migration file using the command: &amp;lt;code&amp;gt;rails generate migration RemoveAssignmentFromReviewBids&amp;lt;/code&amp;gt;&lt;br /&gt;
# In the generated file, indicate the change that needs to be made using the change method and migration columns &amp;lt;code&amp;gt;remove_foreign_key&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;remove_column&amp;lt;/code&amp;gt;. Migration should be straightforward since there is no cascading behavior (i.e., &amp;lt;code&amp;gt;dependent:destroy&amp;lt;/code&amp;gt;) to worry about.&lt;br /&gt;
# Once comfortable with the updates to the generated file, run the migration using the &amp;lt;code&amp;gt;rails db:migrate&amp;lt;/code&amp;gt; command.&lt;br /&gt;
# Run a query in the DB to ensure no orphaned records are in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table after the updates.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;Placeholder for implementation screenshots (before and after)&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-4.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implement Edge Case Testing ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| In the previous implementation wiki, there are edge cases which are not exhaustively tested. Should test those edge cases thoroughly and add more edge case testing - link&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span class=&amp;quot;mark&amp;quot;&amp;gt;TODO&amp;lt;/span&amp;gt;:We just got initial test results back. We still need to figure out why tests are failing and what may be slipping through the cracks.[[File:Test expertiza.png|411x459px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-8-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design Patterns and Principles ===&lt;br /&gt;
Below are the design patterns and principles we plan to use to address the problem statements outlined in the project requirements:&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
** Controller Responsibility&lt;br /&gt;
*** To address the issue of authentication logic residing in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, we will refactor the code to ensure the controller solely handles request and response flows.&lt;br /&gt;
*** Authentication logic will be moved to dedicated methods in &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt;, making the codebase easier to maintain and test.&lt;br /&gt;
** View Simplification&lt;br /&gt;
*** Currently, the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; view contains excessive business logic, violating SRP.&lt;br /&gt;
*** We will move this logic into helper methods or models, ensuring each component has a single responsibility and improving code readability.&lt;br /&gt;
&lt;br /&gt;
* Separation of Concerns (SoC)&lt;br /&gt;
** By relocating authorization logic out of the controller, we separate authentication from request handling.&lt;br /&gt;
** Adhering to SoC enhances readability and maintainability by allowing each part of the application to focus on its specific role.&lt;br /&gt;
* Don't Repeat Yourself (DRY)&lt;br /&gt;
** We will inspect the existing implementation for code duplication, particularly in the controller actions.&lt;br /&gt;
** Any repetitive code will be refactored or removed to reduce redundancy and improve clarity.&lt;br /&gt;
&lt;br /&gt;
* Model-View-Controller (MVC) Adherence&lt;br /&gt;
** Moving business logic from the view aligns with MVC best practices.&lt;br /&gt;
** By ensuring the view only presents data without processing it, we maintain clear boundaries between the model, view, and controller layers.&lt;br /&gt;
&lt;br /&gt;
* Dependency Inversion Principle (DIP)&lt;br /&gt;
** We plan to update class methods to instance methods, allowing the controller to depend on abstractions rather than concrete implementations.&lt;br /&gt;
** This change leverages Ruby's duck typing, providing flexibility and making the code easier to maintain and test.&lt;br /&gt;
&lt;br /&gt;
* Factory Pattern&lt;br /&gt;
** We will implement a factory pattern for the common processing of &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpBid&amp;lt;/code&amp;gt;, encapsulating object creation and promoting code reuse.&lt;br /&gt;
&lt;br /&gt;
* Law of Demeter (Principle of Least Knowledge)&lt;br /&gt;
** We'll delegate interactions appropriately to prevent tightly coupled code, especially when dealing with topics and teams.&lt;br /&gt;
** Clear and intentional naming during refactoring will enhance code clarity and maintainability.&lt;br /&gt;
&lt;br /&gt;
* Adapter Pattern&lt;br /&gt;
** To add messaging to bid states for users, we'll use the adapter pattern to allow different components to work together without modifying their existing code.&lt;br /&gt;
&lt;br /&gt;
== Files Modified/Added ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-1.-controllers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
review_bids_controller.rb&lt;br /&gt;
&lt;br /&gt;
signup_sheet_controller.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-2.-models&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
review_bid.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-3.-helpers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
&lt;br /&gt;
authorization_helper.rb&lt;br /&gt;
&lt;br /&gt;
Review_bids_helper.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;viii.-test-plan&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
The Excel spreadsheet below details our test plan for testing the code we changed or created to ensure code coverage. The process involved reviewing the code and determining all the possible scenarios that needed to be covered based on what the method was doing, then creating test cases accordingly.&lt;br /&gt;
&lt;br /&gt;
* [https://docs.google.com/spreadsheets/d/1iHzOdSwlTtxOtf5VsNdwOaCP8NzYMmiJ/edit?usp=drive_link Open Test Matrix Spreadsheet]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/review_bids_controller_spec.rb ReviewBidsController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/sign_up_sheet_controller_spec.rb SignUpSheetController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/models/review_bid_spec.rb ReviewBid model updated test cases]&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-1.-mentor&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Ed Gehringer&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-2.-members&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Members ===&lt;br /&gt;
&lt;br /&gt;
* Gavin Teague&lt;br /&gt;
* Janice Uwujaren&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xi.-links&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
Previous Wiki: [https://wiki.expertiza.ncsu.edu/index.php/CSC/ECE_517_Fall_2021_-_E2151._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;Link&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xii.-references&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/document/d/1LHVacylQmB15I6VcYUA5nuC7GRRU9szHzZhjlPcE-PA/edit?tab=t.0#heading=h.u0oycoj6e0ev &amp;lt;u&amp;gt;Project Instructions&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;appendix-a-uml-class-diagram-plantuml-code-for-review-bids&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Juwujar</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160958</id>
		<title>CSC/ECE 517 Fall 2024 - E2485. Allow reviewers to bid on what to review</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160958"/>
		<updated>2024-12-10T15:37:14Z</updated>

		<summary type="html">&lt;p&gt;Juwujar: /* Client Side Changes to Support ReviewBidsController#set_priority Changes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
This project will fix the defects and improve the functionality in Expertiza of the Reviewer Bidding feature, a process where reviewers can bid for assignments they want to review. It will make the functionality smoother, more reliable, and align with Expertiza's collaborative learning objectives.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iii.-problem-statements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Problem Statements ==&lt;br /&gt;
&lt;br /&gt;
Assigning reviews to users on Expertiza involves a complex bidding algorithm that requires optimization before production deployment. Below are key areas we plan to address as per the project requirements:&lt;br /&gt;
* Code Refactoring and Best Practices&lt;br /&gt;
** '''Utilize Authentication Utilities''': Modify the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; function in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to use existing authentication utilities, enhancing code reusability and adhering to the Single Responsibility Principle (SRP) and Separation of Concerns (SoC).&lt;br /&gt;
** '''Eliminate Code Duplication''': Refactor &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; to comply with the Don't Repeat Yourself (DRY) principle.&lt;br /&gt;
** '''Rename Functions for Clarity''': Change &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; to better reflect its purpose.&lt;br /&gt;
** '''Refactor Methods in '''&amp;lt;code&amp;gt;review_bid.rb&amp;lt;/code&amp;gt;''':''': Convert class methods to instance methods or relocate them to helper classes for improved code organization.&lt;br /&gt;
* Functionality Enhancements&lt;br /&gt;
** '''Handle Missing Bids Gracefully''': Adjust the bidding algorithm to accommodate students who do not bid, allowing them to select from the remaining options.&lt;br /&gt;
** '''Support Both Individual and Team Assignments''': Ensure the code functions correctly for participant and team-based assignments when signing up for topics.&lt;br /&gt;
* User Interface Improvements&lt;br /&gt;
** '''Increase Transparency in the Bidding Process''': Display messages indicating how many students are eligible to bid, how many have submitted bids, and the deadline.&lt;br /&gt;
** '''Accurate Bid Status Indicators''': Ensure topic colors change correctly based on the number of outstanding bids, providing clear visual feedback to users.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Exhaustive Edge Case Testing''': Conduct thorough testing for edge cases not previously covered, adding additional test cases as needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iv.-design-goals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Design Goals ==&lt;br /&gt;
&lt;br /&gt;
While addressing the existing defects and enhancing the Reviewer Bidding feature, we plan to adhere to the following design guidelines:&lt;br /&gt;
&lt;br /&gt;
* Code Quality and Maintainability&lt;br /&gt;
** '''Improve Code Structure: '''Implement refactoring strategies to enhance code readability and maintainability, ensuring adherence to best practices like the DRY principle.&lt;br /&gt;
** '''Standardize Authentication Logic: '''Utilize a centralized authentication utility to promote consistency and reusability across the application.&lt;br /&gt;
* System Stability and Flexibility&lt;br /&gt;
** '''Enhance Algorithm Robustness: '''Modify the bidding algorithm to handle scenarios where students do not bid, ensuring the system remains stable under all conditions.&lt;br /&gt;
** '''Ensure Assignment Versatility: '''Design the system to seamlessly support both individual and team assignments without additional configuration.&lt;br /&gt;
* User Experience Enhancements&lt;br /&gt;
** '''Increase Bidding Transparency: '''Develop user interface elements that clearly display bidding status, deadlines, and participation metrics.&lt;br /&gt;
** '''Implement Dynamic Visual Feedback: '''Use real-time visual cues, such as color changes, to inform users about the bidding status instantly.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Develop Comprehensive Test Suites: '''Create extensive automated tests covering edge cases and critical functionalities to guarantee reliable system behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;v.-uml-diagram&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== UML Diagram ==&lt;br /&gt;
&lt;br /&gt;
Below is a visual representation of the models associated with the review bids functionality, depicting the data structures and their relationships.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml class diagram2.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi.-implementation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
'''ReviewBid: '''The &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; manages the bidding process, where participants indicate their preferences for reviewing different topics or assignments. This helps distribute review tasks fairly based on participants' interests.&lt;br /&gt;
&lt;br /&gt;
'''Assignment: '''The &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model represents team or individual assignments in Expertiza. It stores all the essential information required to keep the assignment process organized, like managing participants, setting due dates, forming teams, and overseeing the review process.&lt;br /&gt;
&lt;br /&gt;
'''SignUpTopic: '''The &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt; model identifies specific topics or areas within an assignment for which participants can sign up. It manages the availability of these topics, tracks bids, coordinates team sign-ups, and assigns topics based on both bids and participant availability. &lt;br /&gt;
&lt;br /&gt;
'''Participant: '''The &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model is about the individual users who participate in an assignment. This includes students, instructors, teaching assistants, or other relevant roles. This model is responsible for managing key user information, monitoring interactions with the assignment, keeping track of team memberships, and recording things like badges and review grades.&lt;br /&gt;
&lt;br /&gt;
== Implementation ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1.-decoupling-authorization-logic-from-reviewbidscontroller&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Decoupling Authorization Logic from &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| '''&amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; needs to use the authentication utilities'''&lt;br /&gt;
&lt;br /&gt;
The function &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; should use the available authentication utilities to support the Single Responsibility Principle and Separation of Concerns and to support code reusability.&lt;br /&gt;
&lt;br /&gt;
| '''Authentication Consistency:''' Remove authentication logic in the controller and call logic in the authentication utility class instead to ensure consistent use of the same logic across the application, foster reusability, and eliminate DRY.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
==== Authorization Flow Before Refactoring ====&lt;br /&gt;
The sequence diagram depicts the authorization process within the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;. When a user invokes an action, the controller checks if the action is allowed by verifying the user's role through the &amp;lt;code&amp;gt;current_role_name()&amp;lt;/code&amp;gt; method in the &amp;lt;code&amp;gt;ApplicationController&amp;lt;/code&amp;gt;. Depending on the action and the user's role, the controller either authorizes the user to proceed or denies access, ultimately executing the action logic if authorized and responding to the user.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 1.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We simplified the &amp;lt;code&amp;gt;action_allowed?&amp;lt;/code&amp;gt; method by decoupling authorization logic from the controller to use helper methods in the &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt; module. The &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; should not handle authentication, which is a violation of SRP. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;vertical-align:top;&amp;quot; | Original Code !! style=&amp;quot;vertical-align:top;&amp;quot; | Refactored Code&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index'&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator',&lt;br /&gt;
     'Student'].include? current_role_name and&lt;br /&gt;
        ((%w[list].include? action_name) ? are_needed_authorizations_present?(params[:id], &amp;quot;participant&amp;quot;, &amp;quot;reader&amp;quot;, &amp;quot;submitter&amp;quot;, &amp;quot;reviewer&amp;quot;) : true)&lt;br /&gt;
  else&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator'].include? current_role_name&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index', 'list'&lt;br /&gt;
    current_user_has_student_privileges? &amp;amp;&amp;amp; list_authorization_check&lt;br /&gt;
  else&lt;br /&gt;
    current_user_has_ta_privileges?&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; action ===&lt;br /&gt;
Previously, the &amp;lt;code&amp;gt;ReviewBidsController's index&amp;lt;/code&amp;gt; action was overloaded with responsibilities—it handled everything from locating and validating assignments to fetching review mappings and counting completed reviews. This all-in-one approach made the controller action messy. &lt;br /&gt;
&lt;br /&gt;
We have moved the business logic from the controller action and into dedicated service objects. With this change, we introduced two key service objects: &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; focuses on retrieving the participant along with their assignments, while the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; takes on the task of managing review mappings and calculating review-related stats, like how many reviews have been completed. &lt;br /&gt;
&lt;br /&gt;
This separation of responsibilities aligns with the Single Responsibility Principle. The logic for fetching assignments, retrieving review mappings, and counting reviews is now more flexible and reusable throughout the application. For example, we used methods in the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; in both the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; and the &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;, helping to avoid code duplication.The article https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial provides more information on when and how to use service objects in Rails.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
  return unless current_user_id?(@participant.user_id)&lt;br /&gt;
  @assignment = @participant.assignment&lt;br /&gt;
  @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
  # Finding how many reviews have been completed&lt;br /&gt;
  @num_reviews_completed = 0&lt;br /&gt;
  @review_mappings.each do |map|&lt;br /&gt;
    @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # render view for completing reviews after review bidding has been completed&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @assignment = participant_service.assignment&lt;br /&gt;
  unless @assignment.is_a?(Assignment)&lt;br /&gt;
    flash[:error] = 'Assignment not found.'&lt;br /&gt;
    redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  @review_mappings = review_service.review_mappings&lt;br /&gt;
  @num_reviews_completed = review_service.review_counts[:completed]&lt;br /&gt;
&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Optimize &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Action functionality ===&lt;br /&gt;
In the earlier version of the set_priority action in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, the server only needed a couple of key pieces of information: an array of topic IDs (params[:topic]) and the participant ID (params[:id}). From there, it would figure out the assignment_id by using the first topic as a reference. While this worked in simple cases, it could lead to problems if any data was missing or didn’t line up correctly.&lt;br /&gt;
&lt;br /&gt;
With the new implementation, the action now expects clear and structured parameters, including participant_id, an array of topic IDs, and assignment_id. This change ensures that the server always receives complete and reliable information, preventing issues caused by missing or inconsistent data. It also removes the need for the server to perform unnecessary lookups, allowing the controller to focus on its main job: validating inputs (like making sure the topics in params[:topic] belong to the same assignment as the participant), updating the database, handling errors properly, and sending the correct response back to the view.&lt;br /&gt;
&lt;br /&gt;
While these updates have cleaned up the set_priority action, they still contain a lot of business logic, like validations and priority updates. To make the code even more maintainable, there’s room for further refactoring—perhaps moving some of this logic into a service class or model method to streamline the controller even more.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | Old Code&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    if params[:topic].nil?&lt;br /&gt;
      ReviewBid.where(participant_id: params[:id]).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      participant = AssignmentParticipant.find_by(id: params[:id])&lt;br /&gt;
      assignment_id = SignUpTopic.find(params[:topic].first).assignment.id&lt;br /&gt;
      @bids = ReviewBid.where(participant_id: params[:id])&lt;br /&gt;
      signed_up_topics = ReviewBid.where(participant_id: params[:id]).map(&amp;amp;:signuptopic_id)&lt;br /&gt;
      signed_up_topics -= params[:topic].map(&amp;amp;:to_i)&lt;br /&gt;
      signed_up_topics.each do |topic|&lt;br /&gt;
        ReviewBid.where(signuptopic_id: topic, participant_id: params[:id]).destroy_all&lt;br /&gt;
      end&lt;br /&gt;
      params[:topic].each_with_index do |topic_id, index|&lt;br /&gt;
        bid_existence = ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id])&lt;br /&gt;
        if bid_existence.empty?&lt;br /&gt;
          ReviewBid.create(priority: index + 1, signuptopic_id: topic_id, participant_id: params[:id], assignment_id: assignment_id)&lt;br /&gt;
        else&lt;br /&gt;
          ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id]).update_all(priority: index + 1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to action: 'show', assignment_id: params[:assignment_id], id: params[:id]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    participant_id = params[:participant_id].to_i&lt;br /&gt;
    selected_topic_ids = params[:topic]&amp;amp;.map(&amp;amp;:to_i) || []&lt;br /&gt;
    assignment_id = params[:assignment_id].to_i&lt;br /&gt;
&lt;br /&gt;
    unless current_user_can_modify_participant?(participant_id)&lt;br /&gt;
      flash[:error] = 'You are not authorized to modify these bids.'&lt;br /&gt;
      respond_to do |format|&lt;br /&gt;
        format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
        format.json { render json: { status: 'unauthorized' }, status: :unauthorized }&lt;br /&gt;
      end&lt;br /&gt;
      return&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    if selected_topic_ids.empty?&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      assignment = Assignment.find_by(id: assignment_id)&lt;br /&gt;
      if assignment.nil?&lt;br /&gt;
        flash[:error] = &amp;quot;Invalid assignment.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_assignment' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      unless SignUpTopic.where(id: selected_topic_ids, assignment_id: assignment_id).count == selected_topic_ids.size&lt;br /&gt;
        flash[:error] = &amp;quot;One or more selected topics are invalid.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_topics' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).where.not(signuptopic_id: selected_topic_ids).destroy_all&lt;br /&gt;
&lt;br /&gt;
      selected_topic_ids.each_with_index do |topic_id, index|&lt;br /&gt;
        bid = ReviewBid.find_or_initialize_by(signuptopic_id: topic_id, participant_id: participant_id)&lt;br /&gt;
        bid.priority = index + 1&lt;br /&gt;
        bid.assignment_id = assignment_id&lt;br /&gt;
        bid.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_to action: 'show', assignment_id: assignment_id, id: participant_id, notice: 'Review bids updated successfully.' }&lt;br /&gt;
      format.json { render json: { status: 'success' }, status: :ok }&lt;br /&gt;
    end&lt;br /&gt;
  rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
    flash[:error] = &amp;quot;Failed to update priorities: #{e.message}&amp;quot;&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
      format.json { render json: { status: 'error', message: e.message }, status: :unprocessable_entity }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Client Side Changes to Support &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Changes ====&lt;br /&gt;
After tidying up the server-side logic, we needed to refactor the client-side code to ensure it passed the correct information from the view to the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action. On the client side, we now grab the topicIDs and send them as &amp;lt;code&amp;gt;selected_topic_ids&amp;lt;/code&amp;gt;, allowing the controller to figure out which topics were selected and in what order.  &lt;br /&gt;
&lt;br /&gt;
We also extracted the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, making it a lot easier to send data with AJAX and keeping things flexible. When working with jQuery, AJAX is awesome because it lets us update the server without reloading the page, making the user experience smooth and responsive. When we pull the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, we can grab it along with other data, like the list of topics, and include it in the request. This way, we don’t have to rely on the URL to know which participant we’re working with, which makes the code cleaner and more adaptable.&lt;br /&gt;
&lt;br /&gt;
If we were to rely on the route (as opposed to using the DOM) for the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt;, every AJAX request would need to be tied to a specific URL structure. That’s limiting, especially if the route doesn’t naturally include the participant ID, as can be the case with collection routes versus member routes in Rails. &lt;br /&gt;
&lt;br /&gt;
Note: One thing that was interesting is the mix of JavaScript and CoffeeScript code. It wasn't clear if we are keeping both and refactoring to simple JavaScript (or possibly TypeScript in the future).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery -&amp;gt;&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: -&amp;gt;&lt;br /&gt;
      $.post($(this).data('update-url'), $(this).sortable('serialize'))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery(document).ready ($) -&amp;gt;&lt;br /&gt;
  sendUpdatedOrder = -&amp;gt;&lt;br /&gt;
    sortedIDs = $(this).sortable('toArray')&lt;br /&gt;
&lt;br /&gt;
    topicIDs = sortedIDs.map (id) -&amp;gt;&lt;br /&gt;
      id.replace 'topic_', ''&lt;br /&gt;
&lt;br /&gt;
    participant_id = $(this).data('participant-id')&lt;br /&gt;
&lt;br /&gt;
    assignment_id = new URL($(this).data('update-url'), window.location.origin).searchParams.get('assignment_id')&lt;br /&gt;
&lt;br /&gt;
    $.ajax&lt;br /&gt;
      type: 'POST'&lt;br /&gt;
      url: $(this).data('update-url')&lt;br /&gt;
      data:&lt;br /&gt;
        topic: topicIDs&lt;br /&gt;
        participant_id: participant_id&lt;br /&gt;
        assignment_id: assignment_id&lt;br /&gt;
      success: (response) -&amp;gt;&lt;br /&gt;
        console.log 'Selections updated successfully.'&lt;br /&gt;
      error: (xhr, status, error) -&amp;gt;&lt;br /&gt;
        alert 'An error occurred while saving your selections. Please try again.'&lt;br /&gt;
        console.error error&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: sendUpdatedOrder&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-1.-signupsheetcontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; Updates ===&lt;br /&gt;
&lt;br /&gt;
The ReviewBidsController's index method renders the review_bids_others_work action in &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt;. However, the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; does not include the controller action for this view. We need to find the old implementation of the controller action, add it back, and review and update it for any DRY violations.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # provides variables for reviewing page located at views/review_bids/others_work.html.erb&lt;br /&gt;
  def index&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
    return unless current_user_id?(@participant.user_id)&lt;br /&gt;
&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
    # Finding how many reviews have been completed&lt;br /&gt;
    @num_reviews_completed = 0&lt;br /&gt;
    @review_mappings.each do |map|&lt;br /&gt;
      @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # render view for completing reviews after review bidding has been completed&lt;br /&gt;
    render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
  end  &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-2.-reviewbidsotherswork-view-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;review_bids_others_work.html.erb&amp;lt;/code&amp;gt; View Updates ====&lt;br /&gt;
&lt;br /&gt;
The code only contains a view with this name and no associated controller action. Reviewing the code in the view, we noticed some SRP violations where there is much logic in the presentation layer. Set up helper methods in the controller to handle some of that logic or see if the model can handle some of that logic.&lt;br /&gt;
&lt;br /&gt;
Note: The view is technically part of the SignUpSheetController, but it's being rendered by the ReviewBidsController, which initially threw us off. After chatting with the professor, we decided not to change this view. He indicated that the plan is to reuse these views for both assignment bidding and review bidding.&lt;br /&gt;
&lt;br /&gt;
We would suggest building the assignment bidding and review bidding features separately first to make sure each one works on its own. Once that’s done, we can go back and clean things up by combining any shared functionality. This could include creating shared views or even introducing an abstract model to handle both types of bidding in a cleaner way. It could also mean replacing the ReviewBidsController with something more generic, like a BidsController, to keep things reusable and easier to maintain down the line.&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Rename &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; method ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; should be &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt;&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:''' Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The primary activity here is to refactor the method name &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; and fix the hardcoded URL. However, the match topic bidding algorithm no longer works since Heroku is no longer free, per the professor. The reference link below has detailed information about the web service that we can leverage to gain that understanding.&lt;br /&gt;
&lt;br /&gt;
Reference Link for Webservice Details: [https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Updated Implementation ====&lt;br /&gt;
The original requirement was to rename the run_bidding_algorithm method to assign_reviewers, but after digging into the code, we realized that renaming it didn’t make sense. The method runs the bidding algorithm to match topics, and there’s already a separate method to handle assigning review topics.&lt;br /&gt;
&lt;br /&gt;
However, we improved the logic in the assign_bidding method (which is called by the run_bidding_algorithm) by introducing a separate BiddingAlgorithmService object. This keeps the business logic out of the controller action, which makes things cleaner and more maintainable.&lt;br /&gt;
&lt;br /&gt;
==== review_bids_controller ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def run_bidding_algorithm&lt;br /&gt;
    review_bid = ReviewBid.new&lt;br /&gt;
    bidding_data = review_bid.bidding_data&lt;br /&gt;
    matched_topics = BiddingAlgorithmService.new(bidding_data).run&lt;br /&gt;
    raise ArgumentError, 'Failed to assign reviewers. Please try again later.' unless matched_topics&lt;br /&gt;
&lt;br /&gt;
    matched_topics&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== bidding_algorithm_service ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# frozen_string_literal: true&lt;br /&gt;
&lt;br /&gt;
# The `BiddingAlgorithmService` run the bid assignment algorithm&lt;br /&gt;
# Sends student IDs, topic IDs, student preferences, and timestamps to the web service&lt;br /&gt;
# The web service returns the matched assignments in the JSON response body&lt;br /&gt;
class BiddingAlgorithmService&lt;br /&gt;
  SERVICE_URL = 'http://app-csc517.herokuapp.com/match_topics'.freeze&lt;br /&gt;
&lt;br /&gt;
  # MOCK_DATA is based on the documentation detailing how the webservice behaves.&lt;br /&gt;
  # Reference: https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review#Webservice&lt;br /&gt;
  MOCK_DATA = {&lt;br /&gt;
    36239 =&amp;gt; [3970, 3972, 3975],&lt;br /&gt;
    36240 =&amp;gt; [3973, 3974, 3972],&lt;br /&gt;
    36241 =&amp;gt; [3969, 3971, 3972],&lt;br /&gt;
    36242 =&amp;gt; [3969, 3971, 3973],&lt;br /&gt;
    36243 =&amp;gt; [3969, 3970, 3971]&lt;br /&gt;
  }.freeze&lt;br /&gt;
&lt;br /&gt;
  def initialize(bidding_data)&lt;br /&gt;
    @bidding_data = bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def run&lt;br /&gt;
    return MOCK_DATA if Rails.application.config.use_mock_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
    perform_request&lt;br /&gt;
  rescue RestClient::ExceptionWithResponse, JSON::ParserError&lt;br /&gt;
    false&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def perform_request&lt;br /&gt;
    response = RestClient.post(&lt;br /&gt;
      SERVICE_URL,&lt;br /&gt;
      @bidding_data.to_json,&lt;br /&gt;
      content_type: :json,&lt;br /&gt;
      accept: :json&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    validate_response(response)&lt;br /&gt;
    JSON.parse(response.body)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def validate_response(response)&lt;br /&gt;
    raise 'Invalid response format' unless response.headers[:content_type] &amp;amp;&amp;amp; response.headers[:content_type].include?('application/json')&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Resolve Functionality Issues for Non Bidders ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Currently it doesn't work if some student does not bid. In this case, algorithm needs to be fixed to ignore anyone who didn’t bid, and let them choose from what’s left over.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to do several things within this controller. We will set a default state for non-bidders that we can point to to give them options to select topics to review from the leftovers. We will consolidate &amp;lt;code&amp;gt;@sign_up_topics&amp;lt;/code&amp;gt; to resolve the DRY violation. Lastly, we will remove business logic from the show method and push it to a new helper to conform to the SRP.&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # Additional method&lt;br /&gt;
&lt;br /&gt;
  # Provides variables for review bidding page&lt;br /&gt;
  def show&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id].to_i)&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @sign_up_topics = SignUpTopic.where(assignment_id: @assignment.id, private_to: nil)&lt;br /&gt;
    my_topic = SignedUpTeam.topic_id(@participant.parent_id, @participant.user_id)&lt;br /&gt;
    @sign_up_topics -= SignUpTopic.where(assignment_id: @assignment.id, id: my_topic)&lt;br /&gt;
    @num_participants = AssignmentParticipant.where(parent_id: @assignment.id).count&lt;br /&gt;
    @selected_topics = nil # This is used to list the topics assigned to review (i.e., select == assigned)&lt;br /&gt;
    @bids = ReviewBid.where(participant_id: @participant, assignment_id: @assignment.id)&lt;br /&gt;
    signed_up_topics = []&lt;br /&gt;
    &lt;br /&gt;
    @bids.each do |bid|&lt;br /&gt;
      sign_up_topic = SignUpTopic.find_by(id: bid.signuptopic_id)&lt;br /&gt;
      signed_up_topics &amp;lt;&amp;lt; sign_up_topic if sign_up_topic&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    signed_up_topics &amp;amp;= @sign_up_topics&lt;br /&gt;
    @sign_up_topics -= signed_up_topics&lt;br /&gt;
    @bids = signed_up_topics&lt;br /&gt;
    @num_of_topics = @sign_up_topics.size&lt;br /&gt;
    @assigned_review_maps = []&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: @assignment.id, reviewer_id: @participant.id).each do |review_map|&lt;br /&gt;
      @assigned_review_maps &amp;lt;&amp;lt; review_map&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Explicitly render view since it's in the sign up sheet views&lt;br /&gt;
    render 'sign_up_sheet/review_bids_show'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Additional methods&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-2.-reviewbidshelper-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
 # Assigns bidding topics to reviewers&lt;br /&gt;
  def assign_bidding&lt;br /&gt;
      assignment = validate_assignment(params[:assignment_id])&lt;br /&gt;
&lt;br /&gt;
      reviewers = validate_reviewers(assignment.id)&lt;br /&gt;
      reviewer_ids = reviewers.map(&amp;amp;:id)&lt;br /&gt;
&lt;br /&gt;
      matched_topics = run_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
      if matched_topics.blank?&lt;br /&gt;
        flash[:alert] = 'Topic or assignment is missing'&lt;br /&gt;
        redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      leftover_topics = find_leftover_topics(assignment.id, matched_topics)&lt;br /&gt;
      assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.new.assign_review_topics(matched_topics)&lt;br /&gt;
      assignment.update!(can_choose_topic_to_review: false)&lt;br /&gt;
&lt;br /&gt;
      flash[:notice] = 'Reviewers were successfully assigned to topics.'&lt;br /&gt;
      redirect_back fallback_location: root_path&lt;br /&gt;
    rescue ArgumentError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ArgumentError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: e.message&lt;br /&gt;
    rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::RecordInvalid: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue ActiveRecord::ActiveRecordError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::ActiveRecordError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue StandardError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;StandardError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers. Please try again later.'&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  #service methods here ...&lt;br /&gt;
&lt;br /&gt;
  def find_leftover_topics(assignment_id, matched_topics)&lt;br /&gt;
    all_topic_ids = SignUpTopic.where(assignment_id: assignment_id).pluck(:id)&lt;br /&gt;
    assigned_topic_ids = matched_topics.map { |match| match[:topic_id] }&lt;br /&gt;
&lt;br /&gt;
    # Calculate leftover topics&lt;br /&gt;
    all_topic_ids - assigned_topic_ids&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
    return if leftover_topics.blank?&lt;br /&gt;
&lt;br /&gt;
    # Find non-bidders by excluding already matched reviewers&lt;br /&gt;
    non_bidders = reviewer_ids - matched_topics.keys&lt;br /&gt;
&lt;br /&gt;
    # Assign leftover topics to non-bidders in a round-robin fashion&lt;br /&gt;
    non_bidders.each_with_index do |reviewer_id, index|&lt;br /&gt;
      topic_id = leftover_topics[index % leftover_topics.length]&lt;br /&gt;
      ReviewBid.create(priority: 1, signuptopic_id: topic_id, participant_id: reviewer_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirm Functionality for Single-person Teams ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Make sure your code works on individual assignments, i.e., assignments where participants sign up for topics instead of teams. In this case, a team is created for each participant when they sign up. So the code should work for assignments to which either individuals or teams submit.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to make an update to focus on topic rather than team or have it where teams are updated based on topic id.We will probably utilize sign up topic or a similar object to accomplish this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-5-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bid Status Messaging ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| To make the functionality more intuitive, include a message to say how many students are eligible to submit bids, how many have submitted their bids, when the deadline for submitting bids is.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
The logic for these messages will go into the helper file. It is effectively the same reasoning as our migration of other business/functionality code from the controller to the helper to conform to the SRP. It will go hand in hand with the SignUpTopic portion of the code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-6-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Why are the methods in review_bid.rb class methods? Can we change them to instance methods or move it to helpers?&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-1.-reviewbid-model-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model Updates ====&lt;br /&gt;
Since the current class methods are tied to &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model instances, we will change them to instance methods. There is no need to move them to helpers.&lt;br /&gt;
&lt;br /&gt;
===== Review Bid Model Flow Before Refactoring =====&lt;br /&gt;
This sequence diagram describes how the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; interacts with the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model in assigning review topics to reviewers according to their bids. Once the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; action is initiated by the user, the controller gathers the IDs of reviewers and calls &amp;lt;code&amp;gt;ReviewBid.bidding_data&amp;lt;/code&amp;gt; to gather bidding information. That is sent out to an external bidding algorithm, and the matched topics that come back are used by &amp;lt;code&amp;gt;ReviewBid.assign_review_topics&amp;lt;/code&amp;gt; to create &amp;lt;code&amp;gt;ReviewResponseMap&amp;lt;/code&amp;gt; entries, assigning topics to reviewers for the assignment.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
  belongs_to :assignment, class_name: 'Assignment'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  def self.bidding_data(assignment_id, reviewer_ids)&lt;br /&gt;
    # create basic hash and set basic hash data&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'users' =&amp;gt; {}, 'max_accepted_proposals' =&amp;gt; [] }&lt;br /&gt;
    bidding_data['tid'] = SignUpTopic.where(assignment_id: assignment_id).ids&lt;br /&gt;
    bidding_data['max_accepted_proposals'] = Assignment.where(id: assignment_id).pluck(:num_reviews_allowed).first&lt;br /&gt;
&lt;br /&gt;
    # loop through reviewer_ids to get reviewer specific bidding data&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      bidding_data['users'][reviewer_id] = reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # assigns topics to reviews as matched by the webservice algorithm&lt;br /&gt;
  def self.assign_review_topics(assignment_id, reviewer_ids, matched_topics, _min_num_reviews = 2)&lt;br /&gt;
    # if review response map already created, delete it&lt;br /&gt;
    if ReviewResponseMap.where(reviewed_object_id: assignment_id)&lt;br /&gt;
      ReviewResponseMap.where(reviewed_object_id: assignment_id).destroy_all&lt;br /&gt;
    end&lt;br /&gt;
    # loop through reviewer_ids to assign reviews to each reviewer&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      topics_to_assign = matched_topics[reviewer_id.to_s]&lt;br /&gt;
      topics_to_assign.each do |topic|&lt;br /&gt;
        assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method to assign a single topic to a reviewer&lt;br /&gt;
  def self.assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic).pluck(:team_id).first&lt;br /&gt;
    team_to_review.nil? ? [] : ReviewResponseMap.create(reviewed_object_id: assignment_id, reviewer_id: reviewer_id, reviewee_id: team_to_review, type: 'ReviewResponseMap')&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method for getting individual reviewer_ids bidding data&lt;br /&gt;
  # returns user's bidding data hash&lt;br /&gt;
  def self.reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    reviewer_user_id = AssignmentParticipant.find(reviewer_id).user_id&lt;br /&gt;
    self_topic = SignedUpTeam.topic_id(assignment_id, reviewer_user_id)&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'otid' =&amp;gt; self_topic, 'priority' =&amp;gt; [], 'time' =&amp;gt; [] }&lt;br /&gt;
    bids = ReviewBid.where(participant_id: reviewer_id)&lt;br /&gt;
&lt;br /&gt;
    # loop through each bid for a topic to get specific data&lt;br /&gt;
    bids.each do |bid|&lt;br /&gt;
      bidding_data['tid'] &amp;lt;&amp;lt; bid.signuptopic_id&lt;br /&gt;
      bidding_data['priority'] &amp;lt;&amp;lt; bid.priority&lt;br /&gt;
      bidding_data['time'] &amp;lt;&amp;lt; bid.updated_at&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  # Instance method to get bidding data for this review bid&lt;br /&gt;
  def bidding_data&lt;br /&gt;
    {&lt;br /&gt;
      'tid' =&amp;gt; topic_ids,&lt;br /&gt;
      'otid' =&amp;gt; self_topic,&lt;br /&gt;
      'priority' =&amp;gt; priorities,&lt;br /&gt;
      'time' =&amp;gt; updated_times&lt;br /&gt;
    }&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # bid object to limit db touches for similar data acquisitions&lt;br /&gt;
  def bids&lt;br /&gt;
    @bids ||= ReviewBid.where(participant_id: participant_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def topic_ids&lt;br /&gt;
    bids.pluck(:signuptopic_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def priorities&lt;br /&gt;
    bids.pluck(:priority)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def updated_times&lt;br /&gt;
    bids.pluck(:updated_at)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self_topic&lt;br /&gt;
    return nil unless topic &amp;amp;&amp;amp; topic.assignment_id&lt;br /&gt;
&lt;br /&gt;
    SignedUpTeam.topic_id(topic.assignment_id, participant.user_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign topics to reviews&lt;br /&gt;
  def assign_review_topics(matched_topics)&lt;br /&gt;
    validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: topic.assignment_id).destroy_all&lt;br /&gt;
&lt;br /&gt;
    matched_topics.each do |reviewer_id, topics|&lt;br /&gt;
      Array(topics).each { |topic_id| assign_topic_to_reviewer(reviewer_id, topic_id) }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign a single topic to a reviewer&lt;br /&gt;
  def assign_topic_to_reviewer(reviewer_id, topic_id)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic_id).pluck(:team_id).first&lt;br /&gt;
    return if team_to_review.nil?&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.create(&lt;br /&gt;
      reviewed_object_id: topic.assignment_id,&lt;br /&gt;
      reviewer_id: reviewer_id,&lt;br /&gt;
      reviewee_id: team_to_review,&lt;br /&gt;
      type: 'ReviewResponseMap'&lt;br /&gt;
    )&lt;br /&gt;
    # error handling&lt;br /&gt;
  rescue StandardError =&amp;gt; e&lt;br /&gt;
    Rails.logger.error(&amp;quot;Failed to assign topic #{topic_id} to reviewer #{reviewer_id}: #{e.message}&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
    raise ArgumentError, 'Topic or assignment is missing' if topic.nil? || topic.assignment_id.nil?&lt;br /&gt;
    raise ArgumentError, 'Matched topics must be a Hash' unless matched_topics.is_a?(Hash)&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
Once we change them to instance methods, we’ll need to modify their use in the code to instantiate the object first.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;Placeholder for implementation screenshots (before and after)&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-3.-schema.db-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;Schema.db&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We also need to remove the foreign key reference to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model since the &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model already has access to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;, and that is the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; we should be referencing. This will also involve modifying the database to remove the foreign key reference to the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Steps required:&lt;br /&gt;
&lt;br /&gt;
# Review the records in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table in the database to see if there are any records and if those records have values for &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;&lt;br /&gt;
# Create a migration file using the command: &amp;lt;code&amp;gt;rails generate migration RemoveAssignmentFromReviewBids&amp;lt;/code&amp;gt;&lt;br /&gt;
# In the generated file, indicate the change that needs to be made using the change method and migration columns &amp;lt;code&amp;gt;remove_foreign_key&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;remove_column&amp;lt;/code&amp;gt;. Migration should be straightforward since there is no cascading behavior (i.e., &amp;lt;code&amp;gt;dependent:destroy&amp;lt;/code&amp;gt;) to worry about.&lt;br /&gt;
# Once comfortable with the updates to the generated file, run the migration using the &amp;lt;code&amp;gt;rails db:migrate&amp;lt;/code&amp;gt; command.&lt;br /&gt;
# Run a query in the DB to ensure no orphaned records are in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table after the updates.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;Placeholder for implementation screenshots (before and after)&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-4.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implement Edge Case Testing ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| In the previous implementation wiki, there are edge cases which are not exhaustively tested. Should test those edge cases thoroughly and add more edge case testing - link&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span class=&amp;quot;mark&amp;quot;&amp;gt;TODO&amp;lt;/span&amp;gt;:We just got initial test results back. We still need to figure out why tests are failing and what may be slipping through the cracks.[[File:Test expertiza.png|411x459px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-8-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design Patterns and Principles ===&lt;br /&gt;
Below are the design patterns and principles we plan to use to address the problem statements outlined in the project requirements:&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
** Controller Responsibility&lt;br /&gt;
*** To address the issue of authentication logic residing in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, we will refactor the code to ensure the controller solely handles request and response flows.&lt;br /&gt;
*** Authentication logic will be moved to dedicated methods in &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt;, making the codebase easier to maintain and test.&lt;br /&gt;
** View Simplification&lt;br /&gt;
*** Currently, the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; view contains excessive business logic, violating SRP.&lt;br /&gt;
*** We will move this logic into helper methods or models, ensuring each component has a single responsibility and improving code readability.&lt;br /&gt;
&lt;br /&gt;
* Separation of Concerns (SoC)&lt;br /&gt;
** By relocating authorization logic out of the controller, we separate authentication from request handling.&lt;br /&gt;
** Adhering to SoC enhances readability and maintainability by allowing each part of the application to focus on its specific role.&lt;br /&gt;
* Don't Repeat Yourself (DRY)&lt;br /&gt;
** We will inspect the existing implementation for code duplication, particularly in the controller actions.&lt;br /&gt;
** Any repetitive code will be refactored or removed to reduce redundancy and improve clarity.&lt;br /&gt;
&lt;br /&gt;
* Model-View-Controller (MVC) Adherence&lt;br /&gt;
** Moving business logic from the view aligns with MVC best practices.&lt;br /&gt;
** By ensuring the view only presents data without processing it, we maintain clear boundaries between the model, view, and controller layers.&lt;br /&gt;
&lt;br /&gt;
* Dependency Inversion Principle (DIP)&lt;br /&gt;
** We plan to update class methods to instance methods, allowing the controller to depend on abstractions rather than concrete implementations.&lt;br /&gt;
** This change leverages Ruby's duck typing, providing flexibility and making the code easier to maintain and test.&lt;br /&gt;
&lt;br /&gt;
* Factory Pattern&lt;br /&gt;
** We will implement a factory pattern for the common processing of &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpBid&amp;lt;/code&amp;gt;, encapsulating object creation and promoting code reuse.&lt;br /&gt;
&lt;br /&gt;
* Law of Demeter (Principle of Least Knowledge)&lt;br /&gt;
** We'll delegate interactions appropriately to prevent tightly coupled code, especially when dealing with topics and teams.&lt;br /&gt;
** Clear and intentional naming during refactoring will enhance code clarity and maintainability.&lt;br /&gt;
&lt;br /&gt;
* Adapter Pattern&lt;br /&gt;
** To add messaging to bid states for users, we'll use the adapter pattern to allow different components to work together without modifying their existing code.&lt;br /&gt;
&lt;br /&gt;
== Files Modified/Added ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-1.-controllers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
review_bids_controller.rb&lt;br /&gt;
&lt;br /&gt;
signup_sheet_controller.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-2.-models&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
review_bid.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-3.-helpers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
&lt;br /&gt;
authorization_helper.rb&lt;br /&gt;
&lt;br /&gt;
Review_bids_helper.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;viii.-test-plan&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
The Excel spreadsheet below details our test plan for testing the code we changed or created to ensure code coverage. The process involved reviewing the code and determining all the possible scenarios that needed to be covered based on what the method was doing, then creating test cases accordingly.&lt;br /&gt;
&lt;br /&gt;
* [https://docs.google.com/spreadsheets/d/1iHzOdSwlTtxOtf5VsNdwOaCP8NzYMmiJ/edit?usp=drive_link Open Test Matrix Spreadsheet]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/review_bids_controller_spec.rb ReviewBidsController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/sign_up_sheet_controller_spec.rb SignUpSheetController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/models/review_bid_spec.rb ReviewBid model updated test cases]&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-1.-mentor&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Ed Gehringer&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-2.-members&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Members ===&lt;br /&gt;
&lt;br /&gt;
* Gavin Teague&lt;br /&gt;
* Janice Uwujaren&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xi.-links&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
Previous Wiki: [https://wiki.expertiza.ncsu.edu/index.php/CSC/ECE_517_Fall_2021_-_E2151._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;Link&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xii.-references&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/document/d/1LHVacylQmB15I6VcYUA5nuC7GRRU9szHzZhjlPcE-PA/edit?tab=t.0#heading=h.u0oycoj6e0ev &amp;lt;u&amp;gt;Project Instructions&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;appendix-a-uml-class-diagram-plantuml-code-for-review-bids&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Juwujar</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160957</id>
		<title>CSC/ECE 517 Fall 2024 - E2485. Allow reviewers to bid on what to review</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160957"/>
		<updated>2024-12-10T15:32:12Z</updated>

		<summary type="html">&lt;p&gt;Juwujar: /* Client Side Changes to Support ReviewBidsController#set_priority Changes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
This project will fix the defects and improve the functionality in Expertiza of the Reviewer Bidding feature, a process where reviewers can bid for assignments they want to review. It will make the functionality smoother, more reliable, and align with Expertiza's collaborative learning objectives.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iii.-problem-statements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Problem Statements ==&lt;br /&gt;
&lt;br /&gt;
Assigning reviews to users on Expertiza involves a complex bidding algorithm that requires optimization before production deployment. Below are key areas we plan to address as per the project requirements:&lt;br /&gt;
* Code Refactoring and Best Practices&lt;br /&gt;
** '''Utilize Authentication Utilities''': Modify the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; function in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to use existing authentication utilities, enhancing code reusability and adhering to the Single Responsibility Principle (SRP) and Separation of Concerns (SoC).&lt;br /&gt;
** '''Eliminate Code Duplication''': Refactor &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; to comply with the Don't Repeat Yourself (DRY) principle.&lt;br /&gt;
** '''Rename Functions for Clarity''': Change &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; to better reflect its purpose.&lt;br /&gt;
** '''Refactor Methods in '''&amp;lt;code&amp;gt;review_bid.rb&amp;lt;/code&amp;gt;''':''': Convert class methods to instance methods or relocate them to helper classes for improved code organization.&lt;br /&gt;
* Functionality Enhancements&lt;br /&gt;
** '''Handle Missing Bids Gracefully''': Adjust the bidding algorithm to accommodate students who do not bid, allowing them to select from the remaining options.&lt;br /&gt;
** '''Support Both Individual and Team Assignments''': Ensure the code functions correctly for participant and team-based assignments when signing up for topics.&lt;br /&gt;
* User Interface Improvements&lt;br /&gt;
** '''Increase Transparency in the Bidding Process''': Display messages indicating how many students are eligible to bid, how many have submitted bids, and the deadline.&lt;br /&gt;
** '''Accurate Bid Status Indicators''': Ensure topic colors change correctly based on the number of outstanding bids, providing clear visual feedback to users.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Exhaustive Edge Case Testing''': Conduct thorough testing for edge cases not previously covered, adding additional test cases as needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iv.-design-goals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Design Goals ==&lt;br /&gt;
&lt;br /&gt;
While addressing the existing defects and enhancing the Reviewer Bidding feature, we plan to adhere to the following design guidelines:&lt;br /&gt;
&lt;br /&gt;
* Code Quality and Maintainability&lt;br /&gt;
** '''Improve Code Structure: '''Implement refactoring strategies to enhance code readability and maintainability, ensuring adherence to best practices like the DRY principle.&lt;br /&gt;
** '''Standardize Authentication Logic: '''Utilize a centralized authentication utility to promote consistency and reusability across the application.&lt;br /&gt;
* System Stability and Flexibility&lt;br /&gt;
** '''Enhance Algorithm Robustness: '''Modify the bidding algorithm to handle scenarios where students do not bid, ensuring the system remains stable under all conditions.&lt;br /&gt;
** '''Ensure Assignment Versatility: '''Design the system to seamlessly support both individual and team assignments without additional configuration.&lt;br /&gt;
* User Experience Enhancements&lt;br /&gt;
** '''Increase Bidding Transparency: '''Develop user interface elements that clearly display bidding status, deadlines, and participation metrics.&lt;br /&gt;
** '''Implement Dynamic Visual Feedback: '''Use real-time visual cues, such as color changes, to inform users about the bidding status instantly.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Develop Comprehensive Test Suites: '''Create extensive automated tests covering edge cases and critical functionalities to guarantee reliable system behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;v.-uml-diagram&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== UML Diagram ==&lt;br /&gt;
&lt;br /&gt;
Below is a visual representation of the models associated with the review bids functionality, depicting the data structures and their relationships.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml class diagram2.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi.-implementation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
'''ReviewBid: '''The &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; manages the bidding process, where participants indicate their preferences for reviewing different topics or assignments. This helps distribute review tasks fairly based on participants' interests.&lt;br /&gt;
&lt;br /&gt;
'''Assignment: '''The &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model represents team or individual assignments in Expertiza. It stores all the essential information required to keep the assignment process organized, like managing participants, setting due dates, forming teams, and overseeing the review process.&lt;br /&gt;
&lt;br /&gt;
'''SignUpTopic: '''The &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt; model identifies specific topics or areas within an assignment for which participants can sign up. It manages the availability of these topics, tracks bids, coordinates team sign-ups, and assigns topics based on both bids and participant availability. &lt;br /&gt;
&lt;br /&gt;
'''Participant: '''The &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model is about the individual users who participate in an assignment. This includes students, instructors, teaching assistants, or other relevant roles. This model is responsible for managing key user information, monitoring interactions with the assignment, keeping track of team memberships, and recording things like badges and review grades.&lt;br /&gt;
&lt;br /&gt;
== Implementation ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1.-decoupling-authorization-logic-from-reviewbidscontroller&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Decoupling Authorization Logic from &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| '''&amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; needs to use the authentication utilities'''&lt;br /&gt;
&lt;br /&gt;
The function &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; should use the available authentication utilities to support the Single Responsibility Principle and Separation of Concerns and to support code reusability.&lt;br /&gt;
&lt;br /&gt;
| '''Authentication Consistency:''' Remove authentication logic in the controller and call logic in the authentication utility class instead to ensure consistent use of the same logic across the application, foster reusability, and eliminate DRY.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
==== Authorization Flow Before Refactoring ====&lt;br /&gt;
The sequence diagram depicts the authorization process within the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;. When a user invokes an action, the controller checks if the action is allowed by verifying the user's role through the &amp;lt;code&amp;gt;current_role_name()&amp;lt;/code&amp;gt; method in the &amp;lt;code&amp;gt;ApplicationController&amp;lt;/code&amp;gt;. Depending on the action and the user's role, the controller either authorizes the user to proceed or denies access, ultimately executing the action logic if authorized and responding to the user.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 1.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We simplified the &amp;lt;code&amp;gt;action_allowed?&amp;lt;/code&amp;gt; method by decoupling authorization logic from the controller to use helper methods in the &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt; module. The &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; should not handle authentication, which is a violation of SRP. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;vertical-align:top;&amp;quot; | Original Code !! style=&amp;quot;vertical-align:top;&amp;quot; | Refactored Code&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index'&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator',&lt;br /&gt;
     'Student'].include? current_role_name and&lt;br /&gt;
        ((%w[list].include? action_name) ? are_needed_authorizations_present?(params[:id], &amp;quot;participant&amp;quot;, &amp;quot;reader&amp;quot;, &amp;quot;submitter&amp;quot;, &amp;quot;reviewer&amp;quot;) : true)&lt;br /&gt;
  else&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator'].include? current_role_name&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index', 'list'&lt;br /&gt;
    current_user_has_student_privileges? &amp;amp;&amp;amp; list_authorization_check&lt;br /&gt;
  else&lt;br /&gt;
    current_user_has_ta_privileges?&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; action ===&lt;br /&gt;
Previously, the &amp;lt;code&amp;gt;ReviewBidsController's index&amp;lt;/code&amp;gt; action was overloaded with responsibilities—it handled everything from locating and validating assignments to fetching review mappings and counting completed reviews. This all-in-one approach made the controller action messy. &lt;br /&gt;
&lt;br /&gt;
We have moved the business logic from the controller action and into dedicated service objects. With this change, we introduced two key service objects: &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; focuses on retrieving the participant along with their assignments, while the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; takes on the task of managing review mappings and calculating review-related stats, like how many reviews have been completed. &lt;br /&gt;
&lt;br /&gt;
This separation of responsibilities aligns with the Single Responsibility Principle. The logic for fetching assignments, retrieving review mappings, and counting reviews is now more flexible and reusable throughout the application. For example, we used methods in the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; in both the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; and the &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;, helping to avoid code duplication.The article https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial provides more information on when and how to use service objects in Rails.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
  return unless current_user_id?(@participant.user_id)&lt;br /&gt;
  @assignment = @participant.assignment&lt;br /&gt;
  @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
  # Finding how many reviews have been completed&lt;br /&gt;
  @num_reviews_completed = 0&lt;br /&gt;
  @review_mappings.each do |map|&lt;br /&gt;
    @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # render view for completing reviews after review bidding has been completed&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @assignment = participant_service.assignment&lt;br /&gt;
  unless @assignment.is_a?(Assignment)&lt;br /&gt;
    flash[:error] = 'Assignment not found.'&lt;br /&gt;
    redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  @review_mappings = review_service.review_mappings&lt;br /&gt;
  @num_reviews_completed = review_service.review_counts[:completed]&lt;br /&gt;
&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Optimize &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Action functionality ===&lt;br /&gt;
In the earlier version of the set_priority action in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, the server only needed a couple of key pieces of information: an array of topic IDs (params[:topic]) and the participant ID (params[:id}). From there, it would figure out the assignment_id by using the first topic as a reference. While this worked in simple cases, it could lead to problems if any data was missing or didn’t line up correctly.&lt;br /&gt;
&lt;br /&gt;
With the new implementation, the action now expects clear and structured parameters, including participant_id, an array of topic IDs, and assignment_id. This change ensures that the server always receives complete and reliable information, preventing issues caused by missing or inconsistent data. It also removes the need for the server to perform unnecessary lookups, allowing the controller to focus on its main job: validating inputs (like making sure the topics in params[:topic] belong to the same assignment as the participant), updating the database, handling errors properly, and sending the correct response back to the view.&lt;br /&gt;
&lt;br /&gt;
While these updates have cleaned up the set_priority action, they still contain a lot of business logic, like validations and priority updates. To make the code even more maintainable, there’s room for further refactoring—perhaps moving some of this logic into a service class or model method to streamline the controller even more.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | Old Code&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    if params[:topic].nil?&lt;br /&gt;
      ReviewBid.where(participant_id: params[:id]).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      participant = AssignmentParticipant.find_by(id: params[:id])&lt;br /&gt;
      assignment_id = SignUpTopic.find(params[:topic].first).assignment.id&lt;br /&gt;
      @bids = ReviewBid.where(participant_id: params[:id])&lt;br /&gt;
      signed_up_topics = ReviewBid.where(participant_id: params[:id]).map(&amp;amp;:signuptopic_id)&lt;br /&gt;
      signed_up_topics -= params[:topic].map(&amp;amp;:to_i)&lt;br /&gt;
      signed_up_topics.each do |topic|&lt;br /&gt;
        ReviewBid.where(signuptopic_id: topic, participant_id: params[:id]).destroy_all&lt;br /&gt;
      end&lt;br /&gt;
      params[:topic].each_with_index do |topic_id, index|&lt;br /&gt;
        bid_existence = ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id])&lt;br /&gt;
        if bid_existence.empty?&lt;br /&gt;
          ReviewBid.create(priority: index + 1, signuptopic_id: topic_id, participant_id: params[:id], assignment_id: assignment_id)&lt;br /&gt;
        else&lt;br /&gt;
          ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id]).update_all(priority: index + 1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to action: 'show', assignment_id: params[:assignment_id], id: params[:id]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    participant_id = params[:participant_id].to_i&lt;br /&gt;
    selected_topic_ids = params[:topic]&amp;amp;.map(&amp;amp;:to_i) || []&lt;br /&gt;
    assignment_id = params[:assignment_id].to_i&lt;br /&gt;
&lt;br /&gt;
    unless current_user_can_modify_participant?(participant_id)&lt;br /&gt;
      flash[:error] = 'You are not authorized to modify these bids.'&lt;br /&gt;
      respond_to do |format|&lt;br /&gt;
        format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
        format.json { render json: { status: 'unauthorized' }, status: :unauthorized }&lt;br /&gt;
      end&lt;br /&gt;
      return&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    if selected_topic_ids.empty?&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      assignment = Assignment.find_by(id: assignment_id)&lt;br /&gt;
      if assignment.nil?&lt;br /&gt;
        flash[:error] = &amp;quot;Invalid assignment.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_assignment' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      unless SignUpTopic.where(id: selected_topic_ids, assignment_id: assignment_id).count == selected_topic_ids.size&lt;br /&gt;
        flash[:error] = &amp;quot;One or more selected topics are invalid.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_topics' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).where.not(signuptopic_id: selected_topic_ids).destroy_all&lt;br /&gt;
&lt;br /&gt;
      selected_topic_ids.each_with_index do |topic_id, index|&lt;br /&gt;
        bid = ReviewBid.find_or_initialize_by(signuptopic_id: topic_id, participant_id: participant_id)&lt;br /&gt;
        bid.priority = index + 1&lt;br /&gt;
        bid.assignment_id = assignment_id&lt;br /&gt;
        bid.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_to action: 'show', assignment_id: assignment_id, id: participant_id, notice: 'Review bids updated successfully.' }&lt;br /&gt;
      format.json { render json: { status: 'success' }, status: :ok }&lt;br /&gt;
    end&lt;br /&gt;
  rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
    flash[:error] = &amp;quot;Failed to update priorities: #{e.message}&amp;quot;&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
      format.json { render json: { status: 'error', message: e.message }, status: :unprocessable_entity }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Client Side Changes to Support &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Changes ====&lt;br /&gt;
After tidying up the server-side logic, we needed to refactor the client-side code to ensure it passed the correct information from the view to the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action. On the client side, we now grab the topicIDs and send them as &amp;lt;code&amp;gt;selected_topic_ids&amp;lt;/code&amp;gt;, allowing the controller to figure out which topics were selected and in what order.  &lt;br /&gt;
&lt;br /&gt;
We also extracted the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, making it a lot easier to send data with AJAX and keeping things flexible. When working with jQuery, AJAX is awesome because it lets us update the server without reloading the page, making the user experience smooth and responsive. When we pull the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, we can grab it along with other data, like the list of topics, and include it in the request. This way, we don’t have to rely on the URL to know which participant we’re working with, which makes the code cleaner and more adaptable.&lt;br /&gt;
&lt;br /&gt;
If we were to rely on the route (as opposed to using the DOM) for the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt;, every AJAX request would need to be tied to a specific URL structure. That’s limiting, especially if the route doesn’t naturally include the participant ID, as can be the case with collection routes versus member routes in Rails. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery -&amp;gt;&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: -&amp;gt;&lt;br /&gt;
      $.post($(this).data('update-url'), $(this).sortable('serialize'))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery(document).ready ($) -&amp;gt;&lt;br /&gt;
  sendUpdatedOrder = -&amp;gt;&lt;br /&gt;
    sortedIDs = $(this).sortable('toArray')&lt;br /&gt;
&lt;br /&gt;
    topicIDs = sortedIDs.map (id) -&amp;gt;&lt;br /&gt;
      id.replace 'topic_', ''&lt;br /&gt;
&lt;br /&gt;
    participant_id = $(this).data('participant-id')&lt;br /&gt;
&lt;br /&gt;
    assignment_id = new URL($(this).data('update-url'), window.location.origin).searchParams.get('assignment_id')&lt;br /&gt;
&lt;br /&gt;
    $.ajax&lt;br /&gt;
      type: 'POST'&lt;br /&gt;
      url: $(this).data('update-url')&lt;br /&gt;
      data:&lt;br /&gt;
        topic: topicIDs&lt;br /&gt;
        participant_id: participant_id&lt;br /&gt;
        assignment_id: assignment_id&lt;br /&gt;
      success: (response) -&amp;gt;&lt;br /&gt;
        console.log 'Selections updated successfully.'&lt;br /&gt;
      error: (xhr, status, error) -&amp;gt;&lt;br /&gt;
        alert 'An error occurred while saving your selections. Please try again.'&lt;br /&gt;
        console.error error&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: sendUpdatedOrder&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-1.-signupsheetcontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; Updates ===&lt;br /&gt;
&lt;br /&gt;
The ReviewBidsController's index method renders the review_bids_others_work action in &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt;. However, the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; does not include the controller action for this view. We need to find the old implementation of the controller action, add it back, and review and update it for any DRY violations.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # provides variables for reviewing page located at views/review_bids/others_work.html.erb&lt;br /&gt;
  def index&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
    return unless current_user_id?(@participant.user_id)&lt;br /&gt;
&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
    # Finding how many reviews have been completed&lt;br /&gt;
    @num_reviews_completed = 0&lt;br /&gt;
    @review_mappings.each do |map|&lt;br /&gt;
      @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # render view for completing reviews after review bidding has been completed&lt;br /&gt;
    render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
  end  &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-2.-reviewbidsotherswork-view-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;review_bids_others_work.html.erb&amp;lt;/code&amp;gt; View Updates ====&lt;br /&gt;
&lt;br /&gt;
The code only contains a view with this name and no associated controller action. Reviewing the code in the view, we noticed some SRP violations where there is much logic in the presentation layer. Set up helper methods in the controller to handle some of that logic or see if the model can handle some of that logic.&lt;br /&gt;
&lt;br /&gt;
Note: The view is technically part of the SignUpSheetController, but it's being rendered by the ReviewBidsController, which initially threw us off. After chatting with the professor, we decided not to change this view. He indicated that the plan is to reuse these views for both assignment bidding and review bidding.&lt;br /&gt;
&lt;br /&gt;
We would suggest building the assignment bidding and review bidding features separately first to make sure each one works on its own. Once that’s done, we can go back and clean things up by combining any shared functionality. This could include creating shared views or even introducing an abstract model to handle both types of bidding in a cleaner way. It could also mean replacing the ReviewBidsController with something more generic, like a BidsController, to keep things reusable and easier to maintain down the line.&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Rename &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; method ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; should be &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt;&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:''' Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The primary activity here is to refactor the method name &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; and fix the hardcoded URL. However, the match topic bidding algorithm no longer works since Heroku is no longer free, per the professor. The reference link below has detailed information about the web service that we can leverage to gain that understanding.&lt;br /&gt;
&lt;br /&gt;
Reference Link for Webservice Details: [https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Updated Implementation ====&lt;br /&gt;
The original requirement was to rename the run_bidding_algorithm method to assign_reviewers, but after digging into the code, we realized that renaming it didn’t make sense. The method runs the bidding algorithm to match topics, and there’s already a separate method to handle assigning review topics.&lt;br /&gt;
&lt;br /&gt;
However, we improved the logic in the assign_bidding method (which is called by the run_bidding_algorithm) by introducing a separate BiddingAlgorithmService object. This keeps the business logic out of the controller action, which makes things cleaner and more maintainable.&lt;br /&gt;
&lt;br /&gt;
==== review_bids_controller ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def run_bidding_algorithm&lt;br /&gt;
    review_bid = ReviewBid.new&lt;br /&gt;
    bidding_data = review_bid.bidding_data&lt;br /&gt;
    matched_topics = BiddingAlgorithmService.new(bidding_data).run&lt;br /&gt;
    raise ArgumentError, 'Failed to assign reviewers. Please try again later.' unless matched_topics&lt;br /&gt;
&lt;br /&gt;
    matched_topics&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== bidding_algorithm_service ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# frozen_string_literal: true&lt;br /&gt;
&lt;br /&gt;
# The `BiddingAlgorithmService` run the bid assignment algorithm&lt;br /&gt;
# Sends student IDs, topic IDs, student preferences, and timestamps to the web service&lt;br /&gt;
# The web service returns the matched assignments in the JSON response body&lt;br /&gt;
class BiddingAlgorithmService&lt;br /&gt;
  SERVICE_URL = 'http://app-csc517.herokuapp.com/match_topics'.freeze&lt;br /&gt;
&lt;br /&gt;
  # MOCK_DATA is based on the documentation detailing how the webservice behaves.&lt;br /&gt;
  # Reference: https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review#Webservice&lt;br /&gt;
  MOCK_DATA = {&lt;br /&gt;
    36239 =&amp;gt; [3970, 3972, 3975],&lt;br /&gt;
    36240 =&amp;gt; [3973, 3974, 3972],&lt;br /&gt;
    36241 =&amp;gt; [3969, 3971, 3972],&lt;br /&gt;
    36242 =&amp;gt; [3969, 3971, 3973],&lt;br /&gt;
    36243 =&amp;gt; [3969, 3970, 3971]&lt;br /&gt;
  }.freeze&lt;br /&gt;
&lt;br /&gt;
  def initialize(bidding_data)&lt;br /&gt;
    @bidding_data = bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def run&lt;br /&gt;
    return MOCK_DATA if Rails.application.config.use_mock_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
    perform_request&lt;br /&gt;
  rescue RestClient::ExceptionWithResponse, JSON::ParserError&lt;br /&gt;
    false&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def perform_request&lt;br /&gt;
    response = RestClient.post(&lt;br /&gt;
      SERVICE_URL,&lt;br /&gt;
      @bidding_data.to_json,&lt;br /&gt;
      content_type: :json,&lt;br /&gt;
      accept: :json&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    validate_response(response)&lt;br /&gt;
    JSON.parse(response.body)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def validate_response(response)&lt;br /&gt;
    raise 'Invalid response format' unless response.headers[:content_type] &amp;amp;&amp;amp; response.headers[:content_type].include?('application/json')&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Resolve Functionality Issues for Non Bidders ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Currently it doesn't work if some student does not bid. In this case, algorithm needs to be fixed to ignore anyone who didn’t bid, and let them choose from what’s left over.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to do several things within this controller. We will set a default state for non-bidders that we can point to to give them options to select topics to review from the leftovers. We will consolidate &amp;lt;code&amp;gt;@sign_up_topics&amp;lt;/code&amp;gt; to resolve the DRY violation. Lastly, we will remove business logic from the show method and push it to a new helper to conform to the SRP.&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # Additional method&lt;br /&gt;
&lt;br /&gt;
  # Provides variables for review bidding page&lt;br /&gt;
  def show&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id].to_i)&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @sign_up_topics = SignUpTopic.where(assignment_id: @assignment.id, private_to: nil)&lt;br /&gt;
    my_topic = SignedUpTeam.topic_id(@participant.parent_id, @participant.user_id)&lt;br /&gt;
    @sign_up_topics -= SignUpTopic.where(assignment_id: @assignment.id, id: my_topic)&lt;br /&gt;
    @num_participants = AssignmentParticipant.where(parent_id: @assignment.id).count&lt;br /&gt;
    @selected_topics = nil # This is used to list the topics assigned to review (i.e., select == assigned)&lt;br /&gt;
    @bids = ReviewBid.where(participant_id: @participant, assignment_id: @assignment.id)&lt;br /&gt;
    signed_up_topics = []&lt;br /&gt;
    &lt;br /&gt;
    @bids.each do |bid|&lt;br /&gt;
      sign_up_topic = SignUpTopic.find_by(id: bid.signuptopic_id)&lt;br /&gt;
      signed_up_topics &amp;lt;&amp;lt; sign_up_topic if sign_up_topic&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    signed_up_topics &amp;amp;= @sign_up_topics&lt;br /&gt;
    @sign_up_topics -= signed_up_topics&lt;br /&gt;
    @bids = signed_up_topics&lt;br /&gt;
    @num_of_topics = @sign_up_topics.size&lt;br /&gt;
    @assigned_review_maps = []&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: @assignment.id, reviewer_id: @participant.id).each do |review_map|&lt;br /&gt;
      @assigned_review_maps &amp;lt;&amp;lt; review_map&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Explicitly render view since it's in the sign up sheet views&lt;br /&gt;
    render 'sign_up_sheet/review_bids_show'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Additional methods&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-2.-reviewbidshelper-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
 # Assigns bidding topics to reviewers&lt;br /&gt;
  def assign_bidding&lt;br /&gt;
      assignment = validate_assignment(params[:assignment_id])&lt;br /&gt;
&lt;br /&gt;
      reviewers = validate_reviewers(assignment.id)&lt;br /&gt;
      reviewer_ids = reviewers.map(&amp;amp;:id)&lt;br /&gt;
&lt;br /&gt;
      matched_topics = run_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
      if matched_topics.blank?&lt;br /&gt;
        flash[:alert] = 'Topic or assignment is missing'&lt;br /&gt;
        redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      leftover_topics = find_leftover_topics(assignment.id, matched_topics)&lt;br /&gt;
      assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.new.assign_review_topics(matched_topics)&lt;br /&gt;
      assignment.update!(can_choose_topic_to_review: false)&lt;br /&gt;
&lt;br /&gt;
      flash[:notice] = 'Reviewers were successfully assigned to topics.'&lt;br /&gt;
      redirect_back fallback_location: root_path&lt;br /&gt;
    rescue ArgumentError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ArgumentError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: e.message&lt;br /&gt;
    rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::RecordInvalid: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue ActiveRecord::ActiveRecordError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::ActiveRecordError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue StandardError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;StandardError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers. Please try again later.'&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  #service methods here ...&lt;br /&gt;
&lt;br /&gt;
  def find_leftover_topics(assignment_id, matched_topics)&lt;br /&gt;
    all_topic_ids = SignUpTopic.where(assignment_id: assignment_id).pluck(:id)&lt;br /&gt;
    assigned_topic_ids = matched_topics.map { |match| match[:topic_id] }&lt;br /&gt;
&lt;br /&gt;
    # Calculate leftover topics&lt;br /&gt;
    all_topic_ids - assigned_topic_ids&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
    return if leftover_topics.blank?&lt;br /&gt;
&lt;br /&gt;
    # Find non-bidders by excluding already matched reviewers&lt;br /&gt;
    non_bidders = reviewer_ids - matched_topics.keys&lt;br /&gt;
&lt;br /&gt;
    # Assign leftover topics to non-bidders in a round-robin fashion&lt;br /&gt;
    non_bidders.each_with_index do |reviewer_id, index|&lt;br /&gt;
      topic_id = leftover_topics[index % leftover_topics.length]&lt;br /&gt;
      ReviewBid.create(priority: 1, signuptopic_id: topic_id, participant_id: reviewer_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirm Functionality for Single-person Teams ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Make sure your code works on individual assignments, i.e., assignments where participants sign up for topics instead of teams. In this case, a team is created for each participant when they sign up. So the code should work for assignments to which either individuals or teams submit.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to make an update to focus on topic rather than team or have it where teams are updated based on topic id.We will probably utilize sign up topic or a similar object to accomplish this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-5-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bid Status Messaging ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| To make the functionality more intuitive, include a message to say how many students are eligible to submit bids, how many have submitted their bids, when the deadline for submitting bids is.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
The logic for these messages will go into the helper file. It is effectively the same reasoning as our migration of other business/functionality code from the controller to the helper to conform to the SRP. It will go hand in hand with the SignUpTopic portion of the code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-6-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Why are the methods in review_bid.rb class methods? Can we change them to instance methods or move it to helpers?&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-1.-reviewbid-model-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model Updates ====&lt;br /&gt;
Since the current class methods are tied to &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model instances, we will change them to instance methods. There is no need to move them to helpers.&lt;br /&gt;
&lt;br /&gt;
===== Review Bid Model Flow Before Refactoring =====&lt;br /&gt;
This sequence diagram describes how the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; interacts with the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model in assigning review topics to reviewers according to their bids. Once the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; action is initiated by the user, the controller gathers the IDs of reviewers and calls &amp;lt;code&amp;gt;ReviewBid.bidding_data&amp;lt;/code&amp;gt; to gather bidding information. That is sent out to an external bidding algorithm, and the matched topics that come back are used by &amp;lt;code&amp;gt;ReviewBid.assign_review_topics&amp;lt;/code&amp;gt; to create &amp;lt;code&amp;gt;ReviewResponseMap&amp;lt;/code&amp;gt; entries, assigning topics to reviewers for the assignment.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
  belongs_to :assignment, class_name: 'Assignment'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  def self.bidding_data(assignment_id, reviewer_ids)&lt;br /&gt;
    # create basic hash and set basic hash data&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'users' =&amp;gt; {}, 'max_accepted_proposals' =&amp;gt; [] }&lt;br /&gt;
    bidding_data['tid'] = SignUpTopic.where(assignment_id: assignment_id).ids&lt;br /&gt;
    bidding_data['max_accepted_proposals'] = Assignment.where(id: assignment_id).pluck(:num_reviews_allowed).first&lt;br /&gt;
&lt;br /&gt;
    # loop through reviewer_ids to get reviewer specific bidding data&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      bidding_data['users'][reviewer_id] = reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # assigns topics to reviews as matched by the webservice algorithm&lt;br /&gt;
  def self.assign_review_topics(assignment_id, reviewer_ids, matched_topics, _min_num_reviews = 2)&lt;br /&gt;
    # if review response map already created, delete it&lt;br /&gt;
    if ReviewResponseMap.where(reviewed_object_id: assignment_id)&lt;br /&gt;
      ReviewResponseMap.where(reviewed_object_id: assignment_id).destroy_all&lt;br /&gt;
    end&lt;br /&gt;
    # loop through reviewer_ids to assign reviews to each reviewer&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      topics_to_assign = matched_topics[reviewer_id.to_s]&lt;br /&gt;
      topics_to_assign.each do |topic|&lt;br /&gt;
        assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method to assign a single topic to a reviewer&lt;br /&gt;
  def self.assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic).pluck(:team_id).first&lt;br /&gt;
    team_to_review.nil? ? [] : ReviewResponseMap.create(reviewed_object_id: assignment_id, reviewer_id: reviewer_id, reviewee_id: team_to_review, type: 'ReviewResponseMap')&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method for getting individual reviewer_ids bidding data&lt;br /&gt;
  # returns user's bidding data hash&lt;br /&gt;
  def self.reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    reviewer_user_id = AssignmentParticipant.find(reviewer_id).user_id&lt;br /&gt;
    self_topic = SignedUpTeam.topic_id(assignment_id, reviewer_user_id)&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'otid' =&amp;gt; self_topic, 'priority' =&amp;gt; [], 'time' =&amp;gt; [] }&lt;br /&gt;
    bids = ReviewBid.where(participant_id: reviewer_id)&lt;br /&gt;
&lt;br /&gt;
    # loop through each bid for a topic to get specific data&lt;br /&gt;
    bids.each do |bid|&lt;br /&gt;
      bidding_data['tid'] &amp;lt;&amp;lt; bid.signuptopic_id&lt;br /&gt;
      bidding_data['priority'] &amp;lt;&amp;lt; bid.priority&lt;br /&gt;
      bidding_data['time'] &amp;lt;&amp;lt; bid.updated_at&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  # Instance method to get bidding data for this review bid&lt;br /&gt;
  def bidding_data&lt;br /&gt;
    {&lt;br /&gt;
      'tid' =&amp;gt; topic_ids,&lt;br /&gt;
      'otid' =&amp;gt; self_topic,&lt;br /&gt;
      'priority' =&amp;gt; priorities,&lt;br /&gt;
      'time' =&amp;gt; updated_times&lt;br /&gt;
    }&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # bid object to limit db touches for similar data acquisitions&lt;br /&gt;
  def bids&lt;br /&gt;
    @bids ||= ReviewBid.where(participant_id: participant_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def topic_ids&lt;br /&gt;
    bids.pluck(:signuptopic_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def priorities&lt;br /&gt;
    bids.pluck(:priority)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def updated_times&lt;br /&gt;
    bids.pluck(:updated_at)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self_topic&lt;br /&gt;
    return nil unless topic &amp;amp;&amp;amp; topic.assignment_id&lt;br /&gt;
&lt;br /&gt;
    SignedUpTeam.topic_id(topic.assignment_id, participant.user_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign topics to reviews&lt;br /&gt;
  def assign_review_topics(matched_topics)&lt;br /&gt;
    validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: topic.assignment_id).destroy_all&lt;br /&gt;
&lt;br /&gt;
    matched_topics.each do |reviewer_id, topics|&lt;br /&gt;
      Array(topics).each { |topic_id| assign_topic_to_reviewer(reviewer_id, topic_id) }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign a single topic to a reviewer&lt;br /&gt;
  def assign_topic_to_reviewer(reviewer_id, topic_id)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic_id).pluck(:team_id).first&lt;br /&gt;
    return if team_to_review.nil?&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.create(&lt;br /&gt;
      reviewed_object_id: topic.assignment_id,&lt;br /&gt;
      reviewer_id: reviewer_id,&lt;br /&gt;
      reviewee_id: team_to_review,&lt;br /&gt;
      type: 'ReviewResponseMap'&lt;br /&gt;
    )&lt;br /&gt;
    # error handling&lt;br /&gt;
  rescue StandardError =&amp;gt; e&lt;br /&gt;
    Rails.logger.error(&amp;quot;Failed to assign topic #{topic_id} to reviewer #{reviewer_id}: #{e.message}&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
    raise ArgumentError, 'Topic or assignment is missing' if topic.nil? || topic.assignment_id.nil?&lt;br /&gt;
    raise ArgumentError, 'Matched topics must be a Hash' unless matched_topics.is_a?(Hash)&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
Once we change them to instance methods, we’ll need to modify their use in the code to instantiate the object first.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;Placeholder for implementation screenshots (before and after)&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-3.-schema.db-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;Schema.db&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We also need to remove the foreign key reference to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model since the &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model already has access to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;, and that is the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; we should be referencing. This will also involve modifying the database to remove the foreign key reference to the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Steps required:&lt;br /&gt;
&lt;br /&gt;
# Review the records in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table in the database to see if there are any records and if those records have values for &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;&lt;br /&gt;
# Create a migration file using the command: &amp;lt;code&amp;gt;rails generate migration RemoveAssignmentFromReviewBids&amp;lt;/code&amp;gt;&lt;br /&gt;
# In the generated file, indicate the change that needs to be made using the change method and migration columns &amp;lt;code&amp;gt;remove_foreign_key&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;remove_column&amp;lt;/code&amp;gt;. Migration should be straightforward since there is no cascading behavior (i.e., &amp;lt;code&amp;gt;dependent:destroy&amp;lt;/code&amp;gt;) to worry about.&lt;br /&gt;
# Once comfortable with the updates to the generated file, run the migration using the &amp;lt;code&amp;gt;rails db:migrate&amp;lt;/code&amp;gt; command.&lt;br /&gt;
# Run a query in the DB to ensure no orphaned records are in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table after the updates.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;Placeholder for implementation screenshots (before and after)&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-4.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implement Edge Case Testing ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| In the previous implementation wiki, there are edge cases which are not exhaustively tested. Should test those edge cases thoroughly and add more edge case testing - link&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span class=&amp;quot;mark&amp;quot;&amp;gt;TODO&amp;lt;/span&amp;gt;:We just got initial test results back. We still need to figure out why tests are failing and what may be slipping through the cracks.[[File:Test expertiza.png|411x459px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-8-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design Patterns and Principles ===&lt;br /&gt;
Below are the design patterns and principles we plan to use to address the problem statements outlined in the project requirements:&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
** Controller Responsibility&lt;br /&gt;
*** To address the issue of authentication logic residing in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, we will refactor the code to ensure the controller solely handles request and response flows.&lt;br /&gt;
*** Authentication logic will be moved to dedicated methods in &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt;, making the codebase easier to maintain and test.&lt;br /&gt;
** View Simplification&lt;br /&gt;
*** Currently, the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; view contains excessive business logic, violating SRP.&lt;br /&gt;
*** We will move this logic into helper methods or models, ensuring each component has a single responsibility and improving code readability.&lt;br /&gt;
&lt;br /&gt;
* Separation of Concerns (SoC)&lt;br /&gt;
** By relocating authorization logic out of the controller, we separate authentication from request handling.&lt;br /&gt;
** Adhering to SoC enhances readability and maintainability by allowing each part of the application to focus on its specific role.&lt;br /&gt;
* Don't Repeat Yourself (DRY)&lt;br /&gt;
** We will inspect the existing implementation for code duplication, particularly in the controller actions.&lt;br /&gt;
** Any repetitive code will be refactored or removed to reduce redundancy and improve clarity.&lt;br /&gt;
&lt;br /&gt;
* Model-View-Controller (MVC) Adherence&lt;br /&gt;
** Moving business logic from the view aligns with MVC best practices.&lt;br /&gt;
** By ensuring the view only presents data without processing it, we maintain clear boundaries between the model, view, and controller layers.&lt;br /&gt;
&lt;br /&gt;
* Dependency Inversion Principle (DIP)&lt;br /&gt;
** We plan to update class methods to instance methods, allowing the controller to depend on abstractions rather than concrete implementations.&lt;br /&gt;
** This change leverages Ruby's duck typing, providing flexibility and making the code easier to maintain and test.&lt;br /&gt;
&lt;br /&gt;
* Factory Pattern&lt;br /&gt;
** We will implement a factory pattern for the common processing of &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpBid&amp;lt;/code&amp;gt;, encapsulating object creation and promoting code reuse.&lt;br /&gt;
&lt;br /&gt;
* Law of Demeter (Principle of Least Knowledge)&lt;br /&gt;
** We'll delegate interactions appropriately to prevent tightly coupled code, especially when dealing with topics and teams.&lt;br /&gt;
** Clear and intentional naming during refactoring will enhance code clarity and maintainability.&lt;br /&gt;
&lt;br /&gt;
* Adapter Pattern&lt;br /&gt;
** To add messaging to bid states for users, we'll use the adapter pattern to allow different components to work together without modifying their existing code.&lt;br /&gt;
&lt;br /&gt;
== Files Modified/Added ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-1.-controllers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
review_bids_controller.rb&lt;br /&gt;
&lt;br /&gt;
signup_sheet_controller.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-2.-models&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
review_bid.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-3.-helpers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
&lt;br /&gt;
authorization_helper.rb&lt;br /&gt;
&lt;br /&gt;
Review_bids_helper.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;viii.-test-plan&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
The Excel spreadsheet below details our test plan for testing the code we changed or created to ensure code coverage. The process involved reviewing the code and determining all the possible scenarios that needed to be covered based on what the method was doing, then creating test cases accordingly.&lt;br /&gt;
&lt;br /&gt;
* [https://docs.google.com/spreadsheets/d/1iHzOdSwlTtxOtf5VsNdwOaCP8NzYMmiJ/edit?usp=drive_link Open Test Matrix Spreadsheet]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/review_bids_controller_spec.rb ReviewBidsController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/sign_up_sheet_controller_spec.rb SignUpSheetController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/models/review_bid_spec.rb ReviewBid model updated test cases]&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-1.-mentor&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Ed Gehringer&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-2.-members&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Members ===&lt;br /&gt;
&lt;br /&gt;
* Gavin Teague&lt;br /&gt;
* Janice Uwujaren&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xi.-links&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
Previous Wiki: [https://wiki.expertiza.ncsu.edu/index.php/CSC/ECE_517_Fall_2021_-_E2151._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;Link&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xii.-references&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/document/d/1LHVacylQmB15I6VcYUA5nuC7GRRU9szHzZhjlPcE-PA/edit?tab=t.0#heading=h.u0oycoj6e0ev &amp;lt;u&amp;gt;Project Instructions&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;appendix-a-uml-class-diagram-plantuml-code-for-review-bids&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Juwujar</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160956</id>
		<title>CSC/ECE 517 Fall 2024 - E2485. Allow reviewers to bid on what to review</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160956"/>
		<updated>2024-12-10T15:28:10Z</updated>

		<summary type="html">&lt;p&gt;Juwujar: /* Client Side Changes to Support ReviewBidsController#set_priority Changes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
This project will fix the defects and improve the functionality in Expertiza of the Reviewer Bidding feature, a process where reviewers can bid for assignments they want to review. It will make the functionality smoother, more reliable, and align with Expertiza's collaborative learning objectives.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iii.-problem-statements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Problem Statements ==&lt;br /&gt;
&lt;br /&gt;
Assigning reviews to users on Expertiza involves a complex bidding algorithm that requires optimization before production deployment. Below are key areas we plan to address as per the project requirements:&lt;br /&gt;
* Code Refactoring and Best Practices&lt;br /&gt;
** '''Utilize Authentication Utilities''': Modify the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; function in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to use existing authentication utilities, enhancing code reusability and adhering to the Single Responsibility Principle (SRP) and Separation of Concerns (SoC).&lt;br /&gt;
** '''Eliminate Code Duplication''': Refactor &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; to comply with the Don't Repeat Yourself (DRY) principle.&lt;br /&gt;
** '''Rename Functions for Clarity''': Change &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; to better reflect its purpose.&lt;br /&gt;
** '''Refactor Methods in '''&amp;lt;code&amp;gt;review_bid.rb&amp;lt;/code&amp;gt;''':''': Convert class methods to instance methods or relocate them to helper classes for improved code organization.&lt;br /&gt;
* Functionality Enhancements&lt;br /&gt;
** '''Handle Missing Bids Gracefully''': Adjust the bidding algorithm to accommodate students who do not bid, allowing them to select from the remaining options.&lt;br /&gt;
** '''Support Both Individual and Team Assignments''': Ensure the code functions correctly for participant and team-based assignments when signing up for topics.&lt;br /&gt;
* User Interface Improvements&lt;br /&gt;
** '''Increase Transparency in the Bidding Process''': Display messages indicating how many students are eligible to bid, how many have submitted bids, and the deadline.&lt;br /&gt;
** '''Accurate Bid Status Indicators''': Ensure topic colors change correctly based on the number of outstanding bids, providing clear visual feedback to users.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Exhaustive Edge Case Testing''': Conduct thorough testing for edge cases not previously covered, adding additional test cases as needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iv.-design-goals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Design Goals ==&lt;br /&gt;
&lt;br /&gt;
While addressing the existing defects and enhancing the Reviewer Bidding feature, we plan to adhere to the following design guidelines:&lt;br /&gt;
&lt;br /&gt;
* Code Quality and Maintainability&lt;br /&gt;
** '''Improve Code Structure: '''Implement refactoring strategies to enhance code readability and maintainability, ensuring adherence to best practices like the DRY principle.&lt;br /&gt;
** '''Standardize Authentication Logic: '''Utilize a centralized authentication utility to promote consistency and reusability across the application.&lt;br /&gt;
* System Stability and Flexibility&lt;br /&gt;
** '''Enhance Algorithm Robustness: '''Modify the bidding algorithm to handle scenarios where students do not bid, ensuring the system remains stable under all conditions.&lt;br /&gt;
** '''Ensure Assignment Versatility: '''Design the system to seamlessly support both individual and team assignments without additional configuration.&lt;br /&gt;
* User Experience Enhancements&lt;br /&gt;
** '''Increase Bidding Transparency: '''Develop user interface elements that clearly display bidding status, deadlines, and participation metrics.&lt;br /&gt;
** '''Implement Dynamic Visual Feedback: '''Use real-time visual cues, such as color changes, to inform users about the bidding status instantly.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Develop Comprehensive Test Suites: '''Create extensive automated tests covering edge cases and critical functionalities to guarantee reliable system behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;v.-uml-diagram&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== UML Diagram ==&lt;br /&gt;
&lt;br /&gt;
Below is a visual representation of the models associated with the review bids functionality, depicting the data structures and their relationships.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml class diagram2.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi.-implementation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
'''ReviewBid: '''The &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; manages the bidding process, where participants indicate their preferences for reviewing different topics or assignments. This helps distribute review tasks fairly based on participants' interests.&lt;br /&gt;
&lt;br /&gt;
'''Assignment: '''The &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model represents team or individual assignments in Expertiza. It stores all the essential information required to keep the assignment process organized, like managing participants, setting due dates, forming teams, and overseeing the review process.&lt;br /&gt;
&lt;br /&gt;
'''SignUpTopic: '''The &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt; model identifies specific topics or areas within an assignment for which participants can sign up. It manages the availability of these topics, tracks bids, coordinates team sign-ups, and assigns topics based on both bids and participant availability. &lt;br /&gt;
&lt;br /&gt;
'''Participant: '''The &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model is about the individual users who participate in an assignment. This includes students, instructors, teaching assistants, or other relevant roles. This model is responsible for managing key user information, monitoring interactions with the assignment, keeping track of team memberships, and recording things like badges and review grades.&lt;br /&gt;
&lt;br /&gt;
== Implementation ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1.-decoupling-authorization-logic-from-reviewbidscontroller&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Decoupling Authorization Logic from &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| '''&amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; needs to use the authentication utilities'''&lt;br /&gt;
&lt;br /&gt;
The function &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; should use the available authentication utilities to support the Single Responsibility Principle and Separation of Concerns and to support code reusability.&lt;br /&gt;
&lt;br /&gt;
| '''Authentication Consistency:''' Remove authentication logic in the controller and call logic in the authentication utility class instead to ensure consistent use of the same logic across the application, foster reusability, and eliminate DRY.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
==== Authorization Flow Before Refactoring ====&lt;br /&gt;
The sequence diagram depicts the authorization process within the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;. When a user invokes an action, the controller checks if the action is allowed by verifying the user's role through the &amp;lt;code&amp;gt;current_role_name()&amp;lt;/code&amp;gt; method in the &amp;lt;code&amp;gt;ApplicationController&amp;lt;/code&amp;gt;. Depending on the action and the user's role, the controller either authorizes the user to proceed or denies access, ultimately executing the action logic if authorized and responding to the user.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 1.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We simplified the &amp;lt;code&amp;gt;action_allowed?&amp;lt;/code&amp;gt; method by decoupling authorization logic from the controller to use helper methods in the &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt; module. The &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; should not handle authentication, which is a violation of SRP. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;vertical-align:top;&amp;quot; | Original Code !! style=&amp;quot;vertical-align:top;&amp;quot; | Refactored Code&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index'&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator',&lt;br /&gt;
     'Student'].include? current_role_name and&lt;br /&gt;
        ((%w[list].include? action_name) ? are_needed_authorizations_present?(params[:id], &amp;quot;participant&amp;quot;, &amp;quot;reader&amp;quot;, &amp;quot;submitter&amp;quot;, &amp;quot;reviewer&amp;quot;) : true)&lt;br /&gt;
  else&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator'].include? current_role_name&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index', 'list'&lt;br /&gt;
    current_user_has_student_privileges? &amp;amp;&amp;amp; list_authorization_check&lt;br /&gt;
  else&lt;br /&gt;
    current_user_has_ta_privileges?&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; action ===&lt;br /&gt;
Previously, the &amp;lt;code&amp;gt;ReviewBidsController's index&amp;lt;/code&amp;gt; action was overloaded with responsibilities—it handled everything from locating and validating assignments to fetching review mappings and counting completed reviews. This all-in-one approach made the controller action messy. &lt;br /&gt;
&lt;br /&gt;
We have moved the business logic from the controller action and into dedicated service objects. With this change, we introduced two key service objects: &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; focuses on retrieving the participant along with their assignments, while the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; takes on the task of managing review mappings and calculating review-related stats, like how many reviews have been completed. &lt;br /&gt;
&lt;br /&gt;
This separation of responsibilities aligns with the Single Responsibility Principle. The logic for fetching assignments, retrieving review mappings, and counting reviews is now more flexible and reusable throughout the application. For example, we used methods in the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; in both the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; and the &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;, helping to avoid code duplication.The article https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial provides more information on when and how to use service objects in Rails.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
  return unless current_user_id?(@participant.user_id)&lt;br /&gt;
  @assignment = @participant.assignment&lt;br /&gt;
  @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
  # Finding how many reviews have been completed&lt;br /&gt;
  @num_reviews_completed = 0&lt;br /&gt;
  @review_mappings.each do |map|&lt;br /&gt;
    @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # render view for completing reviews after review bidding has been completed&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @assignment = participant_service.assignment&lt;br /&gt;
  unless @assignment.is_a?(Assignment)&lt;br /&gt;
    flash[:error] = 'Assignment not found.'&lt;br /&gt;
    redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  @review_mappings = review_service.review_mappings&lt;br /&gt;
  @num_reviews_completed = review_service.review_counts[:completed]&lt;br /&gt;
&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Optimize &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Action functionality ===&lt;br /&gt;
In the earlier version of the set_priority action in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, the server only needed a couple of key pieces of information: an array of topic IDs (params[:topic]) and the participant ID (params[:id}). From there, it would figure out the assignment_id by using the first topic as a reference. While this worked in simple cases, it could lead to problems if any data was missing or didn’t line up correctly.&lt;br /&gt;
&lt;br /&gt;
With the new implementation, the action now expects clear and structured parameters, including participant_id, an array of topic IDs, and assignment_id. This change ensures that the server always receives complete and reliable information, preventing issues caused by missing or inconsistent data. It also removes the need for the server to perform unnecessary lookups, allowing the controller to focus on its main job: validating inputs (like making sure the topics in params[:topic] belong to the same assignment as the participant), updating the database, handling errors properly, and sending the correct response back to the view.&lt;br /&gt;
&lt;br /&gt;
While these updates have cleaned up the set_priority action, they still contain a lot of business logic, like validations and priority updates. To make the code even more maintainable, there’s room for further refactoring—perhaps moving some of this logic into a service class or model method to streamline the controller even more.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | Old Code&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    if params[:topic].nil?&lt;br /&gt;
      ReviewBid.where(participant_id: params[:id]).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      participant = AssignmentParticipant.find_by(id: params[:id])&lt;br /&gt;
      assignment_id = SignUpTopic.find(params[:topic].first).assignment.id&lt;br /&gt;
      @bids = ReviewBid.where(participant_id: params[:id])&lt;br /&gt;
      signed_up_topics = ReviewBid.where(participant_id: params[:id]).map(&amp;amp;:signuptopic_id)&lt;br /&gt;
      signed_up_topics -= params[:topic].map(&amp;amp;:to_i)&lt;br /&gt;
      signed_up_topics.each do |topic|&lt;br /&gt;
        ReviewBid.where(signuptopic_id: topic, participant_id: params[:id]).destroy_all&lt;br /&gt;
      end&lt;br /&gt;
      params[:topic].each_with_index do |topic_id, index|&lt;br /&gt;
        bid_existence = ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id])&lt;br /&gt;
        if bid_existence.empty?&lt;br /&gt;
          ReviewBid.create(priority: index + 1, signuptopic_id: topic_id, participant_id: params[:id], assignment_id: assignment_id)&lt;br /&gt;
        else&lt;br /&gt;
          ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id]).update_all(priority: index + 1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to action: 'show', assignment_id: params[:assignment_id], id: params[:id]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    participant_id = params[:participant_id].to_i&lt;br /&gt;
    selected_topic_ids = params[:topic]&amp;amp;.map(&amp;amp;:to_i) || []&lt;br /&gt;
    assignment_id = params[:assignment_id].to_i&lt;br /&gt;
&lt;br /&gt;
    unless current_user_can_modify_participant?(participant_id)&lt;br /&gt;
      flash[:error] = 'You are not authorized to modify these bids.'&lt;br /&gt;
      respond_to do |format|&lt;br /&gt;
        format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
        format.json { render json: { status: 'unauthorized' }, status: :unauthorized }&lt;br /&gt;
      end&lt;br /&gt;
      return&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    if selected_topic_ids.empty?&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      assignment = Assignment.find_by(id: assignment_id)&lt;br /&gt;
      if assignment.nil?&lt;br /&gt;
        flash[:error] = &amp;quot;Invalid assignment.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_assignment' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      unless SignUpTopic.where(id: selected_topic_ids, assignment_id: assignment_id).count == selected_topic_ids.size&lt;br /&gt;
        flash[:error] = &amp;quot;One or more selected topics are invalid.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_topics' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).where.not(signuptopic_id: selected_topic_ids).destroy_all&lt;br /&gt;
&lt;br /&gt;
      selected_topic_ids.each_with_index do |topic_id, index|&lt;br /&gt;
        bid = ReviewBid.find_or_initialize_by(signuptopic_id: topic_id, participant_id: participant_id)&lt;br /&gt;
        bid.priority = index + 1&lt;br /&gt;
        bid.assignment_id = assignment_id&lt;br /&gt;
        bid.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_to action: 'show', assignment_id: assignment_id, id: participant_id, notice: 'Review bids updated successfully.' }&lt;br /&gt;
      format.json { render json: { status: 'success' }, status: :ok }&lt;br /&gt;
    end&lt;br /&gt;
  rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
    flash[:error] = &amp;quot;Failed to update priorities: #{e.message}&amp;quot;&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
      format.json { render json: { status: 'error', message: e.message }, status: :unprocessable_entity }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Client Side Changes to Support &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Changes ====&lt;br /&gt;
After tidying up the server-side logic, we needed to refactor the client-side code to ensure it passed the correct information from the view to the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action. On the client side, we now grab the topicIDs and send them as &amp;lt;code&amp;gt;selected_topic_ids&amp;lt;/code&amp;gt;, allowing the controller to figure out which topics were selected and in what order.  &lt;br /&gt;
&lt;br /&gt;
We also extracted the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, making it a lot easier to send data with AJAX and keeping things flexible. AJAX is awesome because it lets us update the server without reloading the page, making the user experience smooth and responsive. When we pull the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, we can grab it along with other data, like the list of topics, and include it in the request. This way, we don’t have to rely on the URL to know which participant we’re working with, which makes the code cleaner and more adaptable.&lt;br /&gt;
&lt;br /&gt;
If we were to rely on the route (as opposed to using the DOM) for the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt;, every AJAX request would need to be tied to a specific URL structure. That’s limiting, especially if the route doesn’t naturally include the participant ID, as can be the case with collection routes versus member routes in Rails. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery -&amp;gt;&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: -&amp;gt;&lt;br /&gt;
      $.post($(this).data('update-url'), $(this).sortable('serialize'))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery(document).ready ($) -&amp;gt;&lt;br /&gt;
  sendUpdatedOrder = -&amp;gt;&lt;br /&gt;
    sortedIDs = $(this).sortable('toArray')&lt;br /&gt;
&lt;br /&gt;
    topicIDs = sortedIDs.map (id) -&amp;gt;&lt;br /&gt;
      id.replace 'topic_', ''&lt;br /&gt;
&lt;br /&gt;
    participant_id = $(this).data('participant-id')&lt;br /&gt;
&lt;br /&gt;
    assignment_id = new URL($(this).data('update-url'), window.location.origin).searchParams.get('assignment_id')&lt;br /&gt;
&lt;br /&gt;
    $.ajax&lt;br /&gt;
      type: 'POST'&lt;br /&gt;
      url: $(this).data('update-url')&lt;br /&gt;
      data:&lt;br /&gt;
        topic: topicIDs&lt;br /&gt;
        participant_id: participant_id&lt;br /&gt;
        assignment_id: assignment_id&lt;br /&gt;
      success: (response) -&amp;gt;&lt;br /&gt;
        console.log 'Selections updated successfully.'&lt;br /&gt;
      error: (xhr, status, error) -&amp;gt;&lt;br /&gt;
        alert 'An error occurred while saving your selections. Please try again.'&lt;br /&gt;
        console.error error&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: sendUpdatedOrder&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-1.-signupsheetcontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; Updates ===&lt;br /&gt;
&lt;br /&gt;
The ReviewBidsController's index method renders the review_bids_others_work action in &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt;. However, the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; does not include the controller action for this view. We need to find the old implementation of the controller action, add it back, and review and update it for any DRY violations.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # provides variables for reviewing page located at views/review_bids/others_work.html.erb&lt;br /&gt;
  def index&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
    return unless current_user_id?(@participant.user_id)&lt;br /&gt;
&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
    # Finding how many reviews have been completed&lt;br /&gt;
    @num_reviews_completed = 0&lt;br /&gt;
    @review_mappings.each do |map|&lt;br /&gt;
      @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # render view for completing reviews after review bidding has been completed&lt;br /&gt;
    render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
  end  &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-2.-reviewbidsotherswork-view-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;review_bids_others_work.html.erb&amp;lt;/code&amp;gt; View Updates ====&lt;br /&gt;
&lt;br /&gt;
The code only contains a view with this name and no associated controller action. Reviewing the code in the view, we noticed some SRP violations where there is much logic in the presentation layer. Set up helper methods in the controller to handle some of that logic or see if the model can handle some of that logic.&lt;br /&gt;
&lt;br /&gt;
Note: The view is technically part of the SignUpSheetController, but it's being rendered by the ReviewBidsController, which initially threw us off. After chatting with the professor, we decided not to change this view. He indicated that the plan is to reuse these views for both assignment bidding and review bidding.&lt;br /&gt;
&lt;br /&gt;
We would suggest building the assignment bidding and review bidding features separately first to make sure each one works on its own. Once that’s done, we can go back and clean things up by combining any shared functionality. This could include creating shared views or even introducing an abstract model to handle both types of bidding in a cleaner way. It could also mean replacing the ReviewBidsController with something more generic, like a BidsController, to keep things reusable and easier to maintain down the line.&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Rename &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; method ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; should be &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt;&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:''' Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The primary activity here is to refactor the method name &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; and fix the hardcoded URL. However, the match topic bidding algorithm no longer works since Heroku is no longer free, per the professor. The reference link below has detailed information about the web service that we can leverage to gain that understanding.&lt;br /&gt;
&lt;br /&gt;
Reference Link for Webservice Details: [https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Updated Implementation ====&lt;br /&gt;
The original requirement was to rename the run_bidding_algorithm method to assign_reviewers, but after digging into the code, we realized that renaming it didn’t make sense. The method runs the bidding algorithm to match topics, and there’s already a separate method to handle assigning review topics.&lt;br /&gt;
&lt;br /&gt;
However, we improved the logic in the assign_bidding method (which is called by the run_bidding_algorithm) by introducing a separate BiddingAlgorithmService object. This keeps the business logic out of the controller action, which makes things cleaner and more maintainable.&lt;br /&gt;
&lt;br /&gt;
==== review_bids_controller ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def run_bidding_algorithm&lt;br /&gt;
    review_bid = ReviewBid.new&lt;br /&gt;
    bidding_data = review_bid.bidding_data&lt;br /&gt;
    matched_topics = BiddingAlgorithmService.new(bidding_data).run&lt;br /&gt;
    raise ArgumentError, 'Failed to assign reviewers. Please try again later.' unless matched_topics&lt;br /&gt;
&lt;br /&gt;
    matched_topics&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== bidding_algorithm_service ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# frozen_string_literal: true&lt;br /&gt;
&lt;br /&gt;
# The `BiddingAlgorithmService` run the bid assignment algorithm&lt;br /&gt;
# Sends student IDs, topic IDs, student preferences, and timestamps to the web service&lt;br /&gt;
# The web service returns the matched assignments in the JSON response body&lt;br /&gt;
class BiddingAlgorithmService&lt;br /&gt;
  SERVICE_URL = 'http://app-csc517.herokuapp.com/match_topics'.freeze&lt;br /&gt;
&lt;br /&gt;
  # MOCK_DATA is based on the documentation detailing how the webservice behaves.&lt;br /&gt;
  # Reference: https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review#Webservice&lt;br /&gt;
  MOCK_DATA = {&lt;br /&gt;
    36239 =&amp;gt; [3970, 3972, 3975],&lt;br /&gt;
    36240 =&amp;gt; [3973, 3974, 3972],&lt;br /&gt;
    36241 =&amp;gt; [3969, 3971, 3972],&lt;br /&gt;
    36242 =&amp;gt; [3969, 3971, 3973],&lt;br /&gt;
    36243 =&amp;gt; [3969, 3970, 3971]&lt;br /&gt;
  }.freeze&lt;br /&gt;
&lt;br /&gt;
  def initialize(bidding_data)&lt;br /&gt;
    @bidding_data = bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def run&lt;br /&gt;
    return MOCK_DATA if Rails.application.config.use_mock_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
    perform_request&lt;br /&gt;
  rescue RestClient::ExceptionWithResponse, JSON::ParserError&lt;br /&gt;
    false&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def perform_request&lt;br /&gt;
    response = RestClient.post(&lt;br /&gt;
      SERVICE_URL,&lt;br /&gt;
      @bidding_data.to_json,&lt;br /&gt;
      content_type: :json,&lt;br /&gt;
      accept: :json&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    validate_response(response)&lt;br /&gt;
    JSON.parse(response.body)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def validate_response(response)&lt;br /&gt;
    raise 'Invalid response format' unless response.headers[:content_type] &amp;amp;&amp;amp; response.headers[:content_type].include?('application/json')&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Resolve Functionality Issues for Non Bidders ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Currently it doesn't work if some student does not bid. In this case, algorithm needs to be fixed to ignore anyone who didn’t bid, and let them choose from what’s left over.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to do several things within this controller. We will set a default state for non-bidders that we can point to to give them options to select topics to review from the leftovers. We will consolidate &amp;lt;code&amp;gt;@sign_up_topics&amp;lt;/code&amp;gt; to resolve the DRY violation. Lastly, we will remove business logic from the show method and push it to a new helper to conform to the SRP.&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # Additional method&lt;br /&gt;
&lt;br /&gt;
  # Provides variables for review bidding page&lt;br /&gt;
  def show&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id].to_i)&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @sign_up_topics = SignUpTopic.where(assignment_id: @assignment.id, private_to: nil)&lt;br /&gt;
    my_topic = SignedUpTeam.topic_id(@participant.parent_id, @participant.user_id)&lt;br /&gt;
    @sign_up_topics -= SignUpTopic.where(assignment_id: @assignment.id, id: my_topic)&lt;br /&gt;
    @num_participants = AssignmentParticipant.where(parent_id: @assignment.id).count&lt;br /&gt;
    @selected_topics = nil # This is used to list the topics assigned to review (i.e., select == assigned)&lt;br /&gt;
    @bids = ReviewBid.where(participant_id: @participant, assignment_id: @assignment.id)&lt;br /&gt;
    signed_up_topics = []&lt;br /&gt;
    &lt;br /&gt;
    @bids.each do |bid|&lt;br /&gt;
      sign_up_topic = SignUpTopic.find_by(id: bid.signuptopic_id)&lt;br /&gt;
      signed_up_topics &amp;lt;&amp;lt; sign_up_topic if sign_up_topic&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    signed_up_topics &amp;amp;= @sign_up_topics&lt;br /&gt;
    @sign_up_topics -= signed_up_topics&lt;br /&gt;
    @bids = signed_up_topics&lt;br /&gt;
    @num_of_topics = @sign_up_topics.size&lt;br /&gt;
    @assigned_review_maps = []&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: @assignment.id, reviewer_id: @participant.id).each do |review_map|&lt;br /&gt;
      @assigned_review_maps &amp;lt;&amp;lt; review_map&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Explicitly render view since it's in the sign up sheet views&lt;br /&gt;
    render 'sign_up_sheet/review_bids_show'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Additional methods&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-2.-reviewbidshelper-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
 # Assigns bidding topics to reviewers&lt;br /&gt;
  def assign_bidding&lt;br /&gt;
      assignment = validate_assignment(params[:assignment_id])&lt;br /&gt;
&lt;br /&gt;
      reviewers = validate_reviewers(assignment.id)&lt;br /&gt;
      reviewer_ids = reviewers.map(&amp;amp;:id)&lt;br /&gt;
&lt;br /&gt;
      matched_topics = run_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
      if matched_topics.blank?&lt;br /&gt;
        flash[:alert] = 'Topic or assignment is missing'&lt;br /&gt;
        redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      leftover_topics = find_leftover_topics(assignment.id, matched_topics)&lt;br /&gt;
      assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.new.assign_review_topics(matched_topics)&lt;br /&gt;
      assignment.update!(can_choose_topic_to_review: false)&lt;br /&gt;
&lt;br /&gt;
      flash[:notice] = 'Reviewers were successfully assigned to topics.'&lt;br /&gt;
      redirect_back fallback_location: root_path&lt;br /&gt;
    rescue ArgumentError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ArgumentError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: e.message&lt;br /&gt;
    rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::RecordInvalid: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue ActiveRecord::ActiveRecordError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::ActiveRecordError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue StandardError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;StandardError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers. Please try again later.'&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  #service methods here ...&lt;br /&gt;
&lt;br /&gt;
  def find_leftover_topics(assignment_id, matched_topics)&lt;br /&gt;
    all_topic_ids = SignUpTopic.where(assignment_id: assignment_id).pluck(:id)&lt;br /&gt;
    assigned_topic_ids = matched_topics.map { |match| match[:topic_id] }&lt;br /&gt;
&lt;br /&gt;
    # Calculate leftover topics&lt;br /&gt;
    all_topic_ids - assigned_topic_ids&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
    return if leftover_topics.blank?&lt;br /&gt;
&lt;br /&gt;
    # Find non-bidders by excluding already matched reviewers&lt;br /&gt;
    non_bidders = reviewer_ids - matched_topics.keys&lt;br /&gt;
&lt;br /&gt;
    # Assign leftover topics to non-bidders in a round-robin fashion&lt;br /&gt;
    non_bidders.each_with_index do |reviewer_id, index|&lt;br /&gt;
      topic_id = leftover_topics[index % leftover_topics.length]&lt;br /&gt;
      ReviewBid.create(priority: 1, signuptopic_id: topic_id, participant_id: reviewer_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirm Functionality for Single-person Teams ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Make sure your code works on individual assignments, i.e., assignments where participants sign up for topics instead of teams. In this case, a team is created for each participant when they sign up. So the code should work for assignments to which either individuals or teams submit.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to make an update to focus on topic rather than team or have it where teams are updated based on topic id.We will probably utilize sign up topic or a similar object to accomplish this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-5-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bid Status Messaging ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| To make the functionality more intuitive, include a message to say how many students are eligible to submit bids, how many have submitted their bids, when the deadline for submitting bids is.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
The logic for these messages will go into the helper file. It is effectively the same reasoning as our migration of other business/functionality code from the controller to the helper to conform to the SRP. It will go hand in hand with the SignUpTopic portion of the code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-6-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Why are the methods in review_bid.rb class methods? Can we change them to instance methods or move it to helpers?&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-1.-reviewbid-model-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model Updates ====&lt;br /&gt;
Since the current class methods are tied to &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model instances, we will change them to instance methods. There is no need to move them to helpers.&lt;br /&gt;
&lt;br /&gt;
===== Review Bid Model Flow Before Refactoring =====&lt;br /&gt;
This sequence diagram describes how the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; interacts with the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model in assigning review topics to reviewers according to their bids. Once the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; action is initiated by the user, the controller gathers the IDs of reviewers and calls &amp;lt;code&amp;gt;ReviewBid.bidding_data&amp;lt;/code&amp;gt; to gather bidding information. That is sent out to an external bidding algorithm, and the matched topics that come back are used by &amp;lt;code&amp;gt;ReviewBid.assign_review_topics&amp;lt;/code&amp;gt; to create &amp;lt;code&amp;gt;ReviewResponseMap&amp;lt;/code&amp;gt; entries, assigning topics to reviewers for the assignment.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
  belongs_to :assignment, class_name: 'Assignment'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  def self.bidding_data(assignment_id, reviewer_ids)&lt;br /&gt;
    # create basic hash and set basic hash data&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'users' =&amp;gt; {}, 'max_accepted_proposals' =&amp;gt; [] }&lt;br /&gt;
    bidding_data['tid'] = SignUpTopic.where(assignment_id: assignment_id).ids&lt;br /&gt;
    bidding_data['max_accepted_proposals'] = Assignment.where(id: assignment_id).pluck(:num_reviews_allowed).first&lt;br /&gt;
&lt;br /&gt;
    # loop through reviewer_ids to get reviewer specific bidding data&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      bidding_data['users'][reviewer_id] = reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # assigns topics to reviews as matched by the webservice algorithm&lt;br /&gt;
  def self.assign_review_topics(assignment_id, reviewer_ids, matched_topics, _min_num_reviews = 2)&lt;br /&gt;
    # if review response map already created, delete it&lt;br /&gt;
    if ReviewResponseMap.where(reviewed_object_id: assignment_id)&lt;br /&gt;
      ReviewResponseMap.where(reviewed_object_id: assignment_id).destroy_all&lt;br /&gt;
    end&lt;br /&gt;
    # loop through reviewer_ids to assign reviews to each reviewer&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      topics_to_assign = matched_topics[reviewer_id.to_s]&lt;br /&gt;
      topics_to_assign.each do |topic|&lt;br /&gt;
        assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method to assign a single topic to a reviewer&lt;br /&gt;
  def self.assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic).pluck(:team_id).first&lt;br /&gt;
    team_to_review.nil? ? [] : ReviewResponseMap.create(reviewed_object_id: assignment_id, reviewer_id: reviewer_id, reviewee_id: team_to_review, type: 'ReviewResponseMap')&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method for getting individual reviewer_ids bidding data&lt;br /&gt;
  # returns user's bidding data hash&lt;br /&gt;
  def self.reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    reviewer_user_id = AssignmentParticipant.find(reviewer_id).user_id&lt;br /&gt;
    self_topic = SignedUpTeam.topic_id(assignment_id, reviewer_user_id)&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'otid' =&amp;gt; self_topic, 'priority' =&amp;gt; [], 'time' =&amp;gt; [] }&lt;br /&gt;
    bids = ReviewBid.where(participant_id: reviewer_id)&lt;br /&gt;
&lt;br /&gt;
    # loop through each bid for a topic to get specific data&lt;br /&gt;
    bids.each do |bid|&lt;br /&gt;
      bidding_data['tid'] &amp;lt;&amp;lt; bid.signuptopic_id&lt;br /&gt;
      bidding_data['priority'] &amp;lt;&amp;lt; bid.priority&lt;br /&gt;
      bidding_data['time'] &amp;lt;&amp;lt; bid.updated_at&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  # Instance method to get bidding data for this review bid&lt;br /&gt;
  def bidding_data&lt;br /&gt;
    {&lt;br /&gt;
      'tid' =&amp;gt; topic_ids,&lt;br /&gt;
      'otid' =&amp;gt; self_topic,&lt;br /&gt;
      'priority' =&amp;gt; priorities,&lt;br /&gt;
      'time' =&amp;gt; updated_times&lt;br /&gt;
    }&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # bid object to limit db touches for similar data acquisitions&lt;br /&gt;
  def bids&lt;br /&gt;
    @bids ||= ReviewBid.where(participant_id: participant_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def topic_ids&lt;br /&gt;
    bids.pluck(:signuptopic_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def priorities&lt;br /&gt;
    bids.pluck(:priority)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def updated_times&lt;br /&gt;
    bids.pluck(:updated_at)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self_topic&lt;br /&gt;
    return nil unless topic &amp;amp;&amp;amp; topic.assignment_id&lt;br /&gt;
&lt;br /&gt;
    SignedUpTeam.topic_id(topic.assignment_id, participant.user_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign topics to reviews&lt;br /&gt;
  def assign_review_topics(matched_topics)&lt;br /&gt;
    validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: topic.assignment_id).destroy_all&lt;br /&gt;
&lt;br /&gt;
    matched_topics.each do |reviewer_id, topics|&lt;br /&gt;
      Array(topics).each { |topic_id| assign_topic_to_reviewer(reviewer_id, topic_id) }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign a single topic to a reviewer&lt;br /&gt;
  def assign_topic_to_reviewer(reviewer_id, topic_id)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic_id).pluck(:team_id).first&lt;br /&gt;
    return if team_to_review.nil?&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.create(&lt;br /&gt;
      reviewed_object_id: topic.assignment_id,&lt;br /&gt;
      reviewer_id: reviewer_id,&lt;br /&gt;
      reviewee_id: team_to_review,&lt;br /&gt;
      type: 'ReviewResponseMap'&lt;br /&gt;
    )&lt;br /&gt;
    # error handling&lt;br /&gt;
  rescue StandardError =&amp;gt; e&lt;br /&gt;
    Rails.logger.error(&amp;quot;Failed to assign topic #{topic_id} to reviewer #{reviewer_id}: #{e.message}&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
    raise ArgumentError, 'Topic or assignment is missing' if topic.nil? || topic.assignment_id.nil?&lt;br /&gt;
    raise ArgumentError, 'Matched topics must be a Hash' unless matched_topics.is_a?(Hash)&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
Once we change them to instance methods, we’ll need to modify their use in the code to instantiate the object first.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;Placeholder for implementation screenshots (before and after)&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-3.-schema.db-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;Schema.db&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We also need to remove the foreign key reference to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model since the &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model already has access to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;, and that is the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; we should be referencing. This will also involve modifying the database to remove the foreign key reference to the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Steps required:&lt;br /&gt;
&lt;br /&gt;
# Review the records in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table in the database to see if there are any records and if those records have values for &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;&lt;br /&gt;
# Create a migration file using the command: &amp;lt;code&amp;gt;rails generate migration RemoveAssignmentFromReviewBids&amp;lt;/code&amp;gt;&lt;br /&gt;
# In the generated file, indicate the change that needs to be made using the change method and migration columns &amp;lt;code&amp;gt;remove_foreign_key&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;remove_column&amp;lt;/code&amp;gt;. Migration should be straightforward since there is no cascading behavior (i.e., &amp;lt;code&amp;gt;dependent:destroy&amp;lt;/code&amp;gt;) to worry about.&lt;br /&gt;
# Once comfortable with the updates to the generated file, run the migration using the &amp;lt;code&amp;gt;rails db:migrate&amp;lt;/code&amp;gt; command.&lt;br /&gt;
# Run a query in the DB to ensure no orphaned records are in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table after the updates.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;Placeholder for implementation screenshots (before and after)&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-4.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implement Edge Case Testing ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| In the previous implementation wiki, there are edge cases which are not exhaustively tested. Should test those edge cases thoroughly and add more edge case testing - link&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span class=&amp;quot;mark&amp;quot;&amp;gt;TODO&amp;lt;/span&amp;gt;:We just got initial test results back. We still need to figure out why tests are failing and what may be slipping through the cracks.[[File:Test expertiza.png|411x459px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-8-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design Patterns and Principles ===&lt;br /&gt;
Below are the design patterns and principles we plan to use to address the problem statements outlined in the project requirements:&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
** Controller Responsibility&lt;br /&gt;
*** To address the issue of authentication logic residing in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, we will refactor the code to ensure the controller solely handles request and response flows.&lt;br /&gt;
*** Authentication logic will be moved to dedicated methods in &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt;, making the codebase easier to maintain and test.&lt;br /&gt;
** View Simplification&lt;br /&gt;
*** Currently, the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; view contains excessive business logic, violating SRP.&lt;br /&gt;
*** We will move this logic into helper methods or models, ensuring each component has a single responsibility and improving code readability.&lt;br /&gt;
&lt;br /&gt;
* Separation of Concerns (SoC)&lt;br /&gt;
** By relocating authorization logic out of the controller, we separate authentication from request handling.&lt;br /&gt;
** Adhering to SoC enhances readability and maintainability by allowing each part of the application to focus on its specific role.&lt;br /&gt;
* Don't Repeat Yourself (DRY)&lt;br /&gt;
** We will inspect the existing implementation for code duplication, particularly in the controller actions.&lt;br /&gt;
** Any repetitive code will be refactored or removed to reduce redundancy and improve clarity.&lt;br /&gt;
&lt;br /&gt;
* Model-View-Controller (MVC) Adherence&lt;br /&gt;
** Moving business logic from the view aligns with MVC best practices.&lt;br /&gt;
** By ensuring the view only presents data without processing it, we maintain clear boundaries between the model, view, and controller layers.&lt;br /&gt;
&lt;br /&gt;
* Dependency Inversion Principle (DIP)&lt;br /&gt;
** We plan to update class methods to instance methods, allowing the controller to depend on abstractions rather than concrete implementations.&lt;br /&gt;
** This change leverages Ruby's duck typing, providing flexibility and making the code easier to maintain and test.&lt;br /&gt;
&lt;br /&gt;
* Factory Pattern&lt;br /&gt;
** We will implement a factory pattern for the common processing of &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpBid&amp;lt;/code&amp;gt;, encapsulating object creation and promoting code reuse.&lt;br /&gt;
&lt;br /&gt;
* Law of Demeter (Principle of Least Knowledge)&lt;br /&gt;
** We'll delegate interactions appropriately to prevent tightly coupled code, especially when dealing with topics and teams.&lt;br /&gt;
** Clear and intentional naming during refactoring will enhance code clarity and maintainability.&lt;br /&gt;
&lt;br /&gt;
* Adapter Pattern&lt;br /&gt;
** To add messaging to bid states for users, we'll use the adapter pattern to allow different components to work together without modifying their existing code.&lt;br /&gt;
&lt;br /&gt;
== Files Modified/Added ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-1.-controllers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
review_bids_controller.rb&lt;br /&gt;
&lt;br /&gt;
signup_sheet_controller.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-2.-models&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
review_bid.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-3.-helpers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
&lt;br /&gt;
authorization_helper.rb&lt;br /&gt;
&lt;br /&gt;
Review_bids_helper.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;viii.-test-plan&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
The Excel spreadsheet below details our test plan for testing the code we changed or created to ensure code coverage. The process involved reviewing the code and determining all the possible scenarios that needed to be covered based on what the method was doing, then creating test cases accordingly.&lt;br /&gt;
&lt;br /&gt;
* [https://docs.google.com/spreadsheets/d/1iHzOdSwlTtxOtf5VsNdwOaCP8NzYMmiJ/edit?usp=drive_link Open Test Matrix Spreadsheet]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/review_bids_controller_spec.rb ReviewBidsController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/sign_up_sheet_controller_spec.rb SignUpSheetController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/models/review_bid_spec.rb ReviewBid model updated test cases]&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-1.-mentor&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Ed Gehringer&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-2.-members&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Members ===&lt;br /&gt;
&lt;br /&gt;
* Gavin Teague&lt;br /&gt;
* Janice Uwujaren&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xi.-links&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
Previous Wiki: [https://wiki.expertiza.ncsu.edu/index.php/CSC/ECE_517_Fall_2021_-_E2151._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;Link&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xii.-references&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/document/d/1LHVacylQmB15I6VcYUA5nuC7GRRU9szHzZhjlPcE-PA/edit?tab=t.0#heading=h.u0oycoj6e0ev &amp;lt;u&amp;gt;Project Instructions&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;appendix-a-uml-class-diagram-plantuml-code-for-review-bids&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Juwujar</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160955</id>
		<title>CSC/ECE 517 Fall 2024 - E2485. Allow reviewers to bid on what to review</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160955"/>
		<updated>2024-12-10T15:24:06Z</updated>

		<summary type="html">&lt;p&gt;Juwujar: /* Refactor ReviewBidsController#index action */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
This project will fix the defects and improve the functionality in Expertiza of the Reviewer Bidding feature, a process where reviewers can bid for assignments they want to review. It will make the functionality smoother, more reliable, and align with Expertiza's collaborative learning objectives.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iii.-problem-statements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Problem Statements ==&lt;br /&gt;
&lt;br /&gt;
Assigning reviews to users on Expertiza involves a complex bidding algorithm that requires optimization before production deployment. Below are key areas we plan to address as per the project requirements:&lt;br /&gt;
* Code Refactoring and Best Practices&lt;br /&gt;
** '''Utilize Authentication Utilities''': Modify the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; function in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to use existing authentication utilities, enhancing code reusability and adhering to the Single Responsibility Principle (SRP) and Separation of Concerns (SoC).&lt;br /&gt;
** '''Eliminate Code Duplication''': Refactor &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; to comply with the Don't Repeat Yourself (DRY) principle.&lt;br /&gt;
** '''Rename Functions for Clarity''': Change &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; to better reflect its purpose.&lt;br /&gt;
** '''Refactor Methods in '''&amp;lt;code&amp;gt;review_bid.rb&amp;lt;/code&amp;gt;''':''': Convert class methods to instance methods or relocate them to helper classes for improved code organization.&lt;br /&gt;
* Functionality Enhancements&lt;br /&gt;
** '''Handle Missing Bids Gracefully''': Adjust the bidding algorithm to accommodate students who do not bid, allowing them to select from the remaining options.&lt;br /&gt;
** '''Support Both Individual and Team Assignments''': Ensure the code functions correctly for participant and team-based assignments when signing up for topics.&lt;br /&gt;
* User Interface Improvements&lt;br /&gt;
** '''Increase Transparency in the Bidding Process''': Display messages indicating how many students are eligible to bid, how many have submitted bids, and the deadline.&lt;br /&gt;
** '''Accurate Bid Status Indicators''': Ensure topic colors change correctly based on the number of outstanding bids, providing clear visual feedback to users.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Exhaustive Edge Case Testing''': Conduct thorough testing for edge cases not previously covered, adding additional test cases as needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iv.-design-goals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Design Goals ==&lt;br /&gt;
&lt;br /&gt;
While addressing the existing defects and enhancing the Reviewer Bidding feature, we plan to adhere to the following design guidelines:&lt;br /&gt;
&lt;br /&gt;
* Code Quality and Maintainability&lt;br /&gt;
** '''Improve Code Structure: '''Implement refactoring strategies to enhance code readability and maintainability, ensuring adherence to best practices like the DRY principle.&lt;br /&gt;
** '''Standardize Authentication Logic: '''Utilize a centralized authentication utility to promote consistency and reusability across the application.&lt;br /&gt;
* System Stability and Flexibility&lt;br /&gt;
** '''Enhance Algorithm Robustness: '''Modify the bidding algorithm to handle scenarios where students do not bid, ensuring the system remains stable under all conditions.&lt;br /&gt;
** '''Ensure Assignment Versatility: '''Design the system to seamlessly support both individual and team assignments without additional configuration.&lt;br /&gt;
* User Experience Enhancements&lt;br /&gt;
** '''Increase Bidding Transparency: '''Develop user interface elements that clearly display bidding status, deadlines, and participation metrics.&lt;br /&gt;
** '''Implement Dynamic Visual Feedback: '''Use real-time visual cues, such as color changes, to inform users about the bidding status instantly.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Develop Comprehensive Test Suites: '''Create extensive automated tests covering edge cases and critical functionalities to guarantee reliable system behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;v.-uml-diagram&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== UML Diagram ==&lt;br /&gt;
&lt;br /&gt;
Below is a visual representation of the models associated with the review bids functionality, depicting the data structures and their relationships.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml class diagram2.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi.-implementation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
'''ReviewBid: '''The &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; manages the bidding process, where participants indicate their preferences for reviewing different topics or assignments. This helps distribute review tasks fairly based on participants' interests.&lt;br /&gt;
&lt;br /&gt;
'''Assignment: '''The &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model represents team or individual assignments in Expertiza. It stores all the essential information required to keep the assignment process organized, like managing participants, setting due dates, forming teams, and overseeing the review process.&lt;br /&gt;
&lt;br /&gt;
'''SignUpTopic: '''The &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt; model identifies specific topics or areas within an assignment for which participants can sign up. It manages the availability of these topics, tracks bids, coordinates team sign-ups, and assigns topics based on both bids and participant availability. &lt;br /&gt;
&lt;br /&gt;
'''Participant: '''The &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model is about the individual users who participate in an assignment. This includes students, instructors, teaching assistants, or other relevant roles. This model is responsible for managing key user information, monitoring interactions with the assignment, keeping track of team memberships, and recording things like badges and review grades.&lt;br /&gt;
&lt;br /&gt;
== Implementation ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1.-decoupling-authorization-logic-from-reviewbidscontroller&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Decoupling Authorization Logic from &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| '''&amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; needs to use the authentication utilities'''&lt;br /&gt;
&lt;br /&gt;
The function &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; should use the available authentication utilities to support the Single Responsibility Principle and Separation of Concerns and to support code reusability.&lt;br /&gt;
&lt;br /&gt;
| '''Authentication Consistency:''' Remove authentication logic in the controller and call logic in the authentication utility class instead to ensure consistent use of the same logic across the application, foster reusability, and eliminate DRY.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
==== Authorization Flow Before Refactoring ====&lt;br /&gt;
The sequence diagram depicts the authorization process within the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;. When a user invokes an action, the controller checks if the action is allowed by verifying the user's role through the &amp;lt;code&amp;gt;current_role_name()&amp;lt;/code&amp;gt; method in the &amp;lt;code&amp;gt;ApplicationController&amp;lt;/code&amp;gt;. Depending on the action and the user's role, the controller either authorizes the user to proceed or denies access, ultimately executing the action logic if authorized and responding to the user.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 1.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We simplified the &amp;lt;code&amp;gt;action_allowed?&amp;lt;/code&amp;gt; method by decoupling authorization logic from the controller to use helper methods in the &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt; module. The &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; should not handle authentication, which is a violation of SRP. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;vertical-align:top;&amp;quot; | Original Code !! style=&amp;quot;vertical-align:top;&amp;quot; | Refactored Code&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index'&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator',&lt;br /&gt;
     'Student'].include? current_role_name and&lt;br /&gt;
        ((%w[list].include? action_name) ? are_needed_authorizations_present?(params[:id], &amp;quot;participant&amp;quot;, &amp;quot;reader&amp;quot;, &amp;quot;submitter&amp;quot;, &amp;quot;reviewer&amp;quot;) : true)&lt;br /&gt;
  else&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator'].include? current_role_name&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index', 'list'&lt;br /&gt;
    current_user_has_student_privileges? &amp;amp;&amp;amp; list_authorization_check&lt;br /&gt;
  else&lt;br /&gt;
    current_user_has_ta_privileges?&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; action ===&lt;br /&gt;
Previously, the &amp;lt;code&amp;gt;ReviewBidsController's index&amp;lt;/code&amp;gt; action was overloaded with responsibilities—it handled everything from locating and validating assignments to fetching review mappings and counting completed reviews. This all-in-one approach made the controller action messy. &lt;br /&gt;
&lt;br /&gt;
We have moved the business logic from the controller action and into dedicated service objects. With this change, we introduced two key service objects: &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; focuses on retrieving the participant along with their assignments, while the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; takes on the task of managing review mappings and calculating review-related stats, like how many reviews have been completed. &lt;br /&gt;
&lt;br /&gt;
This separation of responsibilities aligns with the Single Responsibility Principle. The logic for fetching assignments, retrieving review mappings, and counting reviews is now more flexible and reusable throughout the application. For example, we used methods in the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; in both the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; and the &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;, helping to avoid code duplication.The article https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial provides more information on when and how to use service objects in Rails.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
  return unless current_user_id?(@participant.user_id)&lt;br /&gt;
  @assignment = @participant.assignment&lt;br /&gt;
  @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
  # Finding how many reviews have been completed&lt;br /&gt;
  @num_reviews_completed = 0&lt;br /&gt;
  @review_mappings.each do |map|&lt;br /&gt;
    @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # render view for completing reviews after review bidding has been completed&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @assignment = participant_service.assignment&lt;br /&gt;
  unless @assignment.is_a?(Assignment)&lt;br /&gt;
    flash[:error] = 'Assignment not found.'&lt;br /&gt;
    redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  @review_mappings = review_service.review_mappings&lt;br /&gt;
  @num_reviews_completed = review_service.review_counts[:completed]&lt;br /&gt;
&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Optimize &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Action functionality ===&lt;br /&gt;
In the earlier version of the set_priority action in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, the server only needed a couple of key pieces of information: an array of topic IDs (params[:topic]) and the participant ID (params[:id}). From there, it would figure out the assignment_id by using the first topic as a reference. While this worked in simple cases, it could lead to problems if any data was missing or didn’t line up correctly.&lt;br /&gt;
&lt;br /&gt;
With the new implementation, the action now expects clear and structured parameters, including participant_id, an array of topic IDs, and assignment_id. This change ensures that the server always receives complete and reliable information, preventing issues caused by missing or inconsistent data. It also removes the need for the server to perform unnecessary lookups, allowing the controller to focus on its main job: validating inputs (like making sure the topics in params[:topic] belong to the same assignment as the participant), updating the database, handling errors properly, and sending the correct response back to the view.&lt;br /&gt;
&lt;br /&gt;
While these updates have cleaned up the set_priority action, they still contain a lot of business logic, like validations and priority updates. To make the code even more maintainable, there’s room for further refactoring—perhaps moving some of this logic into a service class or model method to streamline the controller even more.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | Old Code&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    if params[:topic].nil?&lt;br /&gt;
      ReviewBid.where(participant_id: params[:id]).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      participant = AssignmentParticipant.find_by(id: params[:id])&lt;br /&gt;
      assignment_id = SignUpTopic.find(params[:topic].first).assignment.id&lt;br /&gt;
      @bids = ReviewBid.where(participant_id: params[:id])&lt;br /&gt;
      signed_up_topics = ReviewBid.where(participant_id: params[:id]).map(&amp;amp;:signuptopic_id)&lt;br /&gt;
      signed_up_topics -= params[:topic].map(&amp;amp;:to_i)&lt;br /&gt;
      signed_up_topics.each do |topic|&lt;br /&gt;
        ReviewBid.where(signuptopic_id: topic, participant_id: params[:id]).destroy_all&lt;br /&gt;
      end&lt;br /&gt;
      params[:topic].each_with_index do |topic_id, index|&lt;br /&gt;
        bid_existence = ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id])&lt;br /&gt;
        if bid_existence.empty?&lt;br /&gt;
          ReviewBid.create(priority: index + 1, signuptopic_id: topic_id, participant_id: params[:id], assignment_id: assignment_id)&lt;br /&gt;
        else&lt;br /&gt;
          ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id]).update_all(priority: index + 1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to action: 'show', assignment_id: params[:assignment_id], id: params[:id]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    participant_id = params[:participant_id].to_i&lt;br /&gt;
    selected_topic_ids = params[:topic]&amp;amp;.map(&amp;amp;:to_i) || []&lt;br /&gt;
    assignment_id = params[:assignment_id].to_i&lt;br /&gt;
&lt;br /&gt;
    unless current_user_can_modify_participant?(participant_id)&lt;br /&gt;
      flash[:error] = 'You are not authorized to modify these bids.'&lt;br /&gt;
      respond_to do |format|&lt;br /&gt;
        format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
        format.json { render json: { status: 'unauthorized' }, status: :unauthorized }&lt;br /&gt;
      end&lt;br /&gt;
      return&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    if selected_topic_ids.empty?&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      assignment = Assignment.find_by(id: assignment_id)&lt;br /&gt;
      if assignment.nil?&lt;br /&gt;
        flash[:error] = &amp;quot;Invalid assignment.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_assignment' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      unless SignUpTopic.where(id: selected_topic_ids, assignment_id: assignment_id).count == selected_topic_ids.size&lt;br /&gt;
        flash[:error] = &amp;quot;One or more selected topics are invalid.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_topics' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).where.not(signuptopic_id: selected_topic_ids).destroy_all&lt;br /&gt;
&lt;br /&gt;
      selected_topic_ids.each_with_index do |topic_id, index|&lt;br /&gt;
        bid = ReviewBid.find_or_initialize_by(signuptopic_id: topic_id, participant_id: participant_id)&lt;br /&gt;
        bid.priority = index + 1&lt;br /&gt;
        bid.assignment_id = assignment_id&lt;br /&gt;
        bid.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_to action: 'show', assignment_id: assignment_id, id: participant_id, notice: 'Review bids updated successfully.' }&lt;br /&gt;
      format.json { render json: { status: 'success' }, status: :ok }&lt;br /&gt;
    end&lt;br /&gt;
  rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
    flash[:error] = &amp;quot;Failed to update priorities: #{e.message}&amp;quot;&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
      format.json { render json: { status: 'error', message: e.message }, status: :unprocessable_entity }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Client Side Changes to Support &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Changes ====&lt;br /&gt;
After tidying up the server-side logic, we needed to refactor the client-side code to ensure it passed the correct information from the view to the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action. On the client side, we now grab the topicIDs and send them as &amp;lt;code&amp;gt;selected_topic_ids&amp;lt;/code&amp;gt;, allowing the controller to figure out which topics were selected and in what order. It’s a cleaner and more reliable way to keep everything working together! &lt;br /&gt;
&lt;br /&gt;
We are also extracting the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, making it a lot easier to send data with AJAX and keeping things flexible. AJAX is awesome because it lets us update the server without reloading the page, making the user experience smooth and responsive. When we pull the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, we can grab it along with other data, like the list of topics, and include it in the request. This way, we don’t have to rely on the URL to know which participant we’re working with, which makes the code cleaner and more adaptable.&lt;br /&gt;
&lt;br /&gt;
If we were to rely on the route for the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt;, every AJAX request would need to be tied to a specific URL structure. That’s limiting, especially if the route doesn’t naturally include the participant ID, as can be the case with collection routes versus member routes in Rails. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery -&amp;gt;&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: -&amp;gt;&lt;br /&gt;
      $.post($(this).data('update-url'), $(this).sortable('serialize'))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery(document).ready ($) -&amp;gt;&lt;br /&gt;
  sendUpdatedOrder = -&amp;gt;&lt;br /&gt;
    sortedIDs = $(this).sortable('toArray')&lt;br /&gt;
&lt;br /&gt;
    topicIDs = sortedIDs.map (id) -&amp;gt;&lt;br /&gt;
      id.replace 'topic_', ''&lt;br /&gt;
&lt;br /&gt;
    participant_id = $(this).data('participant-id')&lt;br /&gt;
&lt;br /&gt;
    assignment_id = new URL($(this).data('update-url'), window.location.origin).searchParams.get('assignment_id')&lt;br /&gt;
&lt;br /&gt;
    $.ajax&lt;br /&gt;
      type: 'POST'&lt;br /&gt;
      url: $(this).data('update-url')&lt;br /&gt;
      data:&lt;br /&gt;
        topic: topicIDs&lt;br /&gt;
        participant_id: participant_id&lt;br /&gt;
        assignment_id: assignment_id&lt;br /&gt;
      success: (response) -&amp;gt;&lt;br /&gt;
        console.log 'Selections updated successfully.'&lt;br /&gt;
      error: (xhr, status, error) -&amp;gt;&lt;br /&gt;
        alert 'An error occurred while saving your selections. Please try again.'&lt;br /&gt;
        console.error error&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: sendUpdatedOrder&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-1.-signupsheetcontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; Updates ===&lt;br /&gt;
&lt;br /&gt;
The ReviewBidsController's index method renders the review_bids_others_work action in &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt;. However, the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; does not include the controller action for this view. We need to find the old implementation of the controller action, add it back, and review and update it for any DRY violations.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # provides variables for reviewing page located at views/review_bids/others_work.html.erb&lt;br /&gt;
  def index&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
    return unless current_user_id?(@participant.user_id)&lt;br /&gt;
&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
    # Finding how many reviews have been completed&lt;br /&gt;
    @num_reviews_completed = 0&lt;br /&gt;
    @review_mappings.each do |map|&lt;br /&gt;
      @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # render view for completing reviews after review bidding has been completed&lt;br /&gt;
    render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
  end  &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-2.-reviewbidsotherswork-view-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;review_bids_others_work.html.erb&amp;lt;/code&amp;gt; View Updates ====&lt;br /&gt;
&lt;br /&gt;
The code only contains a view with this name and no associated controller action. Reviewing the code in the view, we noticed some SRP violations where there is much logic in the presentation layer. Set up helper methods in the controller to handle some of that logic or see if the model can handle some of that logic.&lt;br /&gt;
&lt;br /&gt;
Note: The view is technically part of the SignUpSheetController, but it's being rendered by the ReviewBidsController, which initially threw us off. After chatting with the professor, we decided not to change this view. He indicated that the plan is to reuse these views for both assignment bidding and review bidding.&lt;br /&gt;
&lt;br /&gt;
We would suggest building the assignment bidding and review bidding features separately first to make sure each one works on its own. Once that’s done, we can go back and clean things up by combining any shared functionality. This could include creating shared views or even introducing an abstract model to handle both types of bidding in a cleaner way. It could also mean replacing the ReviewBidsController with something more generic, like a BidsController, to keep things reusable and easier to maintain down the line.&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Rename &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; method ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; should be &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt;&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:''' Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The primary activity here is to refactor the method name &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; and fix the hardcoded URL. However, the match topic bidding algorithm no longer works since Heroku is no longer free, per the professor. The reference link below has detailed information about the web service that we can leverage to gain that understanding.&lt;br /&gt;
&lt;br /&gt;
Reference Link for Webservice Details: [https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Updated Implementation ====&lt;br /&gt;
The original requirement was to rename the run_bidding_algorithm method to assign_reviewers, but after digging into the code, we realized that renaming it didn’t make sense. The method runs the bidding algorithm to match topics, and there’s already a separate method to handle assigning review topics.&lt;br /&gt;
&lt;br /&gt;
However, we improved the logic in the assign_bidding method (which is called by the run_bidding_algorithm) by introducing a separate BiddingAlgorithmService object. This keeps the business logic out of the controller action, which makes things cleaner and more maintainable.&lt;br /&gt;
&lt;br /&gt;
==== review_bids_controller ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def run_bidding_algorithm&lt;br /&gt;
    review_bid = ReviewBid.new&lt;br /&gt;
    bidding_data = review_bid.bidding_data&lt;br /&gt;
    matched_topics = BiddingAlgorithmService.new(bidding_data).run&lt;br /&gt;
    raise ArgumentError, 'Failed to assign reviewers. Please try again later.' unless matched_topics&lt;br /&gt;
&lt;br /&gt;
    matched_topics&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== bidding_algorithm_service ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# frozen_string_literal: true&lt;br /&gt;
&lt;br /&gt;
# The `BiddingAlgorithmService` run the bid assignment algorithm&lt;br /&gt;
# Sends student IDs, topic IDs, student preferences, and timestamps to the web service&lt;br /&gt;
# The web service returns the matched assignments in the JSON response body&lt;br /&gt;
class BiddingAlgorithmService&lt;br /&gt;
  SERVICE_URL = 'http://app-csc517.herokuapp.com/match_topics'.freeze&lt;br /&gt;
&lt;br /&gt;
  # MOCK_DATA is based on the documentation detailing how the webservice behaves.&lt;br /&gt;
  # Reference: https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review#Webservice&lt;br /&gt;
  MOCK_DATA = {&lt;br /&gt;
    36239 =&amp;gt; [3970, 3972, 3975],&lt;br /&gt;
    36240 =&amp;gt; [3973, 3974, 3972],&lt;br /&gt;
    36241 =&amp;gt; [3969, 3971, 3972],&lt;br /&gt;
    36242 =&amp;gt; [3969, 3971, 3973],&lt;br /&gt;
    36243 =&amp;gt; [3969, 3970, 3971]&lt;br /&gt;
  }.freeze&lt;br /&gt;
&lt;br /&gt;
  def initialize(bidding_data)&lt;br /&gt;
    @bidding_data = bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def run&lt;br /&gt;
    return MOCK_DATA if Rails.application.config.use_mock_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
    perform_request&lt;br /&gt;
  rescue RestClient::ExceptionWithResponse, JSON::ParserError&lt;br /&gt;
    false&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def perform_request&lt;br /&gt;
    response = RestClient.post(&lt;br /&gt;
      SERVICE_URL,&lt;br /&gt;
      @bidding_data.to_json,&lt;br /&gt;
      content_type: :json,&lt;br /&gt;
      accept: :json&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    validate_response(response)&lt;br /&gt;
    JSON.parse(response.body)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def validate_response(response)&lt;br /&gt;
    raise 'Invalid response format' unless response.headers[:content_type] &amp;amp;&amp;amp; response.headers[:content_type].include?('application/json')&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Resolve Functionality Issues for Non Bidders ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Currently it doesn't work if some student does not bid. In this case, algorithm needs to be fixed to ignore anyone who didn’t bid, and let them choose from what’s left over.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to do several things within this controller. We will set a default state for non-bidders that we can point to to give them options to select topics to review from the leftovers. We will consolidate &amp;lt;code&amp;gt;@sign_up_topics&amp;lt;/code&amp;gt; to resolve the DRY violation. Lastly, we will remove business logic from the show method and push it to a new helper to conform to the SRP.&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # Additional method&lt;br /&gt;
&lt;br /&gt;
  # Provides variables for review bidding page&lt;br /&gt;
  def show&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id].to_i)&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @sign_up_topics = SignUpTopic.where(assignment_id: @assignment.id, private_to: nil)&lt;br /&gt;
    my_topic = SignedUpTeam.topic_id(@participant.parent_id, @participant.user_id)&lt;br /&gt;
    @sign_up_topics -= SignUpTopic.where(assignment_id: @assignment.id, id: my_topic)&lt;br /&gt;
    @num_participants = AssignmentParticipant.where(parent_id: @assignment.id).count&lt;br /&gt;
    @selected_topics = nil # This is used to list the topics assigned to review (i.e., select == assigned)&lt;br /&gt;
    @bids = ReviewBid.where(participant_id: @participant, assignment_id: @assignment.id)&lt;br /&gt;
    signed_up_topics = []&lt;br /&gt;
    &lt;br /&gt;
    @bids.each do |bid|&lt;br /&gt;
      sign_up_topic = SignUpTopic.find_by(id: bid.signuptopic_id)&lt;br /&gt;
      signed_up_topics &amp;lt;&amp;lt; sign_up_topic if sign_up_topic&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    signed_up_topics &amp;amp;= @sign_up_topics&lt;br /&gt;
    @sign_up_topics -= signed_up_topics&lt;br /&gt;
    @bids = signed_up_topics&lt;br /&gt;
    @num_of_topics = @sign_up_topics.size&lt;br /&gt;
    @assigned_review_maps = []&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: @assignment.id, reviewer_id: @participant.id).each do |review_map|&lt;br /&gt;
      @assigned_review_maps &amp;lt;&amp;lt; review_map&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Explicitly render view since it's in the sign up sheet views&lt;br /&gt;
    render 'sign_up_sheet/review_bids_show'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Additional methods&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-2.-reviewbidshelper-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
 # Assigns bidding topics to reviewers&lt;br /&gt;
  def assign_bidding&lt;br /&gt;
      assignment = validate_assignment(params[:assignment_id])&lt;br /&gt;
&lt;br /&gt;
      reviewers = validate_reviewers(assignment.id)&lt;br /&gt;
      reviewer_ids = reviewers.map(&amp;amp;:id)&lt;br /&gt;
&lt;br /&gt;
      matched_topics = run_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
      if matched_topics.blank?&lt;br /&gt;
        flash[:alert] = 'Topic or assignment is missing'&lt;br /&gt;
        redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      leftover_topics = find_leftover_topics(assignment.id, matched_topics)&lt;br /&gt;
      assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.new.assign_review_topics(matched_topics)&lt;br /&gt;
      assignment.update!(can_choose_topic_to_review: false)&lt;br /&gt;
&lt;br /&gt;
      flash[:notice] = 'Reviewers were successfully assigned to topics.'&lt;br /&gt;
      redirect_back fallback_location: root_path&lt;br /&gt;
    rescue ArgumentError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ArgumentError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: e.message&lt;br /&gt;
    rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::RecordInvalid: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue ActiveRecord::ActiveRecordError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::ActiveRecordError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue StandardError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;StandardError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers. Please try again later.'&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  #service methods here ...&lt;br /&gt;
&lt;br /&gt;
  def find_leftover_topics(assignment_id, matched_topics)&lt;br /&gt;
    all_topic_ids = SignUpTopic.where(assignment_id: assignment_id).pluck(:id)&lt;br /&gt;
    assigned_topic_ids = matched_topics.map { |match| match[:topic_id] }&lt;br /&gt;
&lt;br /&gt;
    # Calculate leftover topics&lt;br /&gt;
    all_topic_ids - assigned_topic_ids&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
    return if leftover_topics.blank?&lt;br /&gt;
&lt;br /&gt;
    # Find non-bidders by excluding already matched reviewers&lt;br /&gt;
    non_bidders = reviewer_ids - matched_topics.keys&lt;br /&gt;
&lt;br /&gt;
    # Assign leftover topics to non-bidders in a round-robin fashion&lt;br /&gt;
    non_bidders.each_with_index do |reviewer_id, index|&lt;br /&gt;
      topic_id = leftover_topics[index % leftover_topics.length]&lt;br /&gt;
      ReviewBid.create(priority: 1, signuptopic_id: topic_id, participant_id: reviewer_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirm Functionality for Single-person Teams ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Make sure your code works on individual assignments, i.e., assignments where participants sign up for topics instead of teams. In this case, a team is created for each participant when they sign up. So the code should work for assignments to which either individuals or teams submit.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to make an update to focus on topic rather than team or have it where teams are updated based on topic id.We will probably utilize sign up topic or a similar object to accomplish this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-5-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bid Status Messaging ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| To make the functionality more intuitive, include a message to say how many students are eligible to submit bids, how many have submitted their bids, when the deadline for submitting bids is.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
The logic for these messages will go into the helper file. It is effectively the same reasoning as our migration of other business/functionality code from the controller to the helper to conform to the SRP. It will go hand in hand with the SignUpTopic portion of the code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-6-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Why are the methods in review_bid.rb class methods? Can we change them to instance methods or move it to helpers?&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-1.-reviewbid-model-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model Updates ====&lt;br /&gt;
Since the current class methods are tied to &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model instances, we will change them to instance methods. There is no need to move them to helpers.&lt;br /&gt;
&lt;br /&gt;
===== Review Bid Model Flow Before Refactoring =====&lt;br /&gt;
This sequence diagram describes how the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; interacts with the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model in assigning review topics to reviewers according to their bids. Once the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; action is initiated by the user, the controller gathers the IDs of reviewers and calls &amp;lt;code&amp;gt;ReviewBid.bidding_data&amp;lt;/code&amp;gt; to gather bidding information. That is sent out to an external bidding algorithm, and the matched topics that come back are used by &amp;lt;code&amp;gt;ReviewBid.assign_review_topics&amp;lt;/code&amp;gt; to create &amp;lt;code&amp;gt;ReviewResponseMap&amp;lt;/code&amp;gt; entries, assigning topics to reviewers for the assignment.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
  belongs_to :assignment, class_name: 'Assignment'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  def self.bidding_data(assignment_id, reviewer_ids)&lt;br /&gt;
    # create basic hash and set basic hash data&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'users' =&amp;gt; {}, 'max_accepted_proposals' =&amp;gt; [] }&lt;br /&gt;
    bidding_data['tid'] = SignUpTopic.where(assignment_id: assignment_id).ids&lt;br /&gt;
    bidding_data['max_accepted_proposals'] = Assignment.where(id: assignment_id).pluck(:num_reviews_allowed).first&lt;br /&gt;
&lt;br /&gt;
    # loop through reviewer_ids to get reviewer specific bidding data&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      bidding_data['users'][reviewer_id] = reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # assigns topics to reviews as matched by the webservice algorithm&lt;br /&gt;
  def self.assign_review_topics(assignment_id, reviewer_ids, matched_topics, _min_num_reviews = 2)&lt;br /&gt;
    # if review response map already created, delete it&lt;br /&gt;
    if ReviewResponseMap.where(reviewed_object_id: assignment_id)&lt;br /&gt;
      ReviewResponseMap.where(reviewed_object_id: assignment_id).destroy_all&lt;br /&gt;
    end&lt;br /&gt;
    # loop through reviewer_ids to assign reviews to each reviewer&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      topics_to_assign = matched_topics[reviewer_id.to_s]&lt;br /&gt;
      topics_to_assign.each do |topic|&lt;br /&gt;
        assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method to assign a single topic to a reviewer&lt;br /&gt;
  def self.assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic).pluck(:team_id).first&lt;br /&gt;
    team_to_review.nil? ? [] : ReviewResponseMap.create(reviewed_object_id: assignment_id, reviewer_id: reviewer_id, reviewee_id: team_to_review, type: 'ReviewResponseMap')&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method for getting individual reviewer_ids bidding data&lt;br /&gt;
  # returns user's bidding data hash&lt;br /&gt;
  def self.reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    reviewer_user_id = AssignmentParticipant.find(reviewer_id).user_id&lt;br /&gt;
    self_topic = SignedUpTeam.topic_id(assignment_id, reviewer_user_id)&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'otid' =&amp;gt; self_topic, 'priority' =&amp;gt; [], 'time' =&amp;gt; [] }&lt;br /&gt;
    bids = ReviewBid.where(participant_id: reviewer_id)&lt;br /&gt;
&lt;br /&gt;
    # loop through each bid for a topic to get specific data&lt;br /&gt;
    bids.each do |bid|&lt;br /&gt;
      bidding_data['tid'] &amp;lt;&amp;lt; bid.signuptopic_id&lt;br /&gt;
      bidding_data['priority'] &amp;lt;&amp;lt; bid.priority&lt;br /&gt;
      bidding_data['time'] &amp;lt;&amp;lt; bid.updated_at&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  # Instance method to get bidding data for this review bid&lt;br /&gt;
  def bidding_data&lt;br /&gt;
    {&lt;br /&gt;
      'tid' =&amp;gt; topic_ids,&lt;br /&gt;
      'otid' =&amp;gt; self_topic,&lt;br /&gt;
      'priority' =&amp;gt; priorities,&lt;br /&gt;
      'time' =&amp;gt; updated_times&lt;br /&gt;
    }&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # bid object to limit db touches for similar data acquisitions&lt;br /&gt;
  def bids&lt;br /&gt;
    @bids ||= ReviewBid.where(participant_id: participant_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def topic_ids&lt;br /&gt;
    bids.pluck(:signuptopic_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def priorities&lt;br /&gt;
    bids.pluck(:priority)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def updated_times&lt;br /&gt;
    bids.pluck(:updated_at)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self_topic&lt;br /&gt;
    return nil unless topic &amp;amp;&amp;amp; topic.assignment_id&lt;br /&gt;
&lt;br /&gt;
    SignedUpTeam.topic_id(topic.assignment_id, participant.user_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign topics to reviews&lt;br /&gt;
  def assign_review_topics(matched_topics)&lt;br /&gt;
    validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: topic.assignment_id).destroy_all&lt;br /&gt;
&lt;br /&gt;
    matched_topics.each do |reviewer_id, topics|&lt;br /&gt;
      Array(topics).each { |topic_id| assign_topic_to_reviewer(reviewer_id, topic_id) }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign a single topic to a reviewer&lt;br /&gt;
  def assign_topic_to_reviewer(reviewer_id, topic_id)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic_id).pluck(:team_id).first&lt;br /&gt;
    return if team_to_review.nil?&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.create(&lt;br /&gt;
      reviewed_object_id: topic.assignment_id,&lt;br /&gt;
      reviewer_id: reviewer_id,&lt;br /&gt;
      reviewee_id: team_to_review,&lt;br /&gt;
      type: 'ReviewResponseMap'&lt;br /&gt;
    )&lt;br /&gt;
    # error handling&lt;br /&gt;
  rescue StandardError =&amp;gt; e&lt;br /&gt;
    Rails.logger.error(&amp;quot;Failed to assign topic #{topic_id} to reviewer #{reviewer_id}: #{e.message}&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
    raise ArgumentError, 'Topic or assignment is missing' if topic.nil? || topic.assignment_id.nil?&lt;br /&gt;
    raise ArgumentError, 'Matched topics must be a Hash' unless matched_topics.is_a?(Hash)&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
Once we change them to instance methods, we’ll need to modify their use in the code to instantiate the object first.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;Placeholder for implementation screenshots (before and after)&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-3.-schema.db-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;Schema.db&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We also need to remove the foreign key reference to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model since the &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model already has access to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;, and that is the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; we should be referencing. This will also involve modifying the database to remove the foreign key reference to the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Steps required:&lt;br /&gt;
&lt;br /&gt;
# Review the records in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table in the database to see if there are any records and if those records have values for &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;&lt;br /&gt;
# Create a migration file using the command: &amp;lt;code&amp;gt;rails generate migration RemoveAssignmentFromReviewBids&amp;lt;/code&amp;gt;&lt;br /&gt;
# In the generated file, indicate the change that needs to be made using the change method and migration columns &amp;lt;code&amp;gt;remove_foreign_key&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;remove_column&amp;lt;/code&amp;gt;. Migration should be straightforward since there is no cascading behavior (i.e., &amp;lt;code&amp;gt;dependent:destroy&amp;lt;/code&amp;gt;) to worry about.&lt;br /&gt;
# Once comfortable with the updates to the generated file, run the migration using the &amp;lt;code&amp;gt;rails db:migrate&amp;lt;/code&amp;gt; command.&lt;br /&gt;
# Run a query in the DB to ensure no orphaned records are in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table after the updates.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;Placeholder for implementation screenshots (before and after)&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-4.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implement Edge Case Testing ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| In the previous implementation wiki, there are edge cases which are not exhaustively tested. Should test those edge cases thoroughly and add more edge case testing - link&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span class=&amp;quot;mark&amp;quot;&amp;gt;TODO&amp;lt;/span&amp;gt;:We just got initial test results back. We still need to figure out why tests are failing and what may be slipping through the cracks.[[File:Test expertiza.png|411x459px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-8-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design Patterns and Principles ===&lt;br /&gt;
Below are the design patterns and principles we plan to use to address the problem statements outlined in the project requirements:&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
** Controller Responsibility&lt;br /&gt;
*** To address the issue of authentication logic residing in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, we will refactor the code to ensure the controller solely handles request and response flows.&lt;br /&gt;
*** Authentication logic will be moved to dedicated methods in &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt;, making the codebase easier to maintain and test.&lt;br /&gt;
** View Simplification&lt;br /&gt;
*** Currently, the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; view contains excessive business logic, violating SRP.&lt;br /&gt;
*** We will move this logic into helper methods or models, ensuring each component has a single responsibility and improving code readability.&lt;br /&gt;
&lt;br /&gt;
* Separation of Concerns (SoC)&lt;br /&gt;
** By relocating authorization logic out of the controller, we separate authentication from request handling.&lt;br /&gt;
** Adhering to SoC enhances readability and maintainability by allowing each part of the application to focus on its specific role.&lt;br /&gt;
* Don't Repeat Yourself (DRY)&lt;br /&gt;
** We will inspect the existing implementation for code duplication, particularly in the controller actions.&lt;br /&gt;
** Any repetitive code will be refactored or removed to reduce redundancy and improve clarity.&lt;br /&gt;
&lt;br /&gt;
* Model-View-Controller (MVC) Adherence&lt;br /&gt;
** Moving business logic from the view aligns with MVC best practices.&lt;br /&gt;
** By ensuring the view only presents data without processing it, we maintain clear boundaries between the model, view, and controller layers.&lt;br /&gt;
&lt;br /&gt;
* Dependency Inversion Principle (DIP)&lt;br /&gt;
** We plan to update class methods to instance methods, allowing the controller to depend on abstractions rather than concrete implementations.&lt;br /&gt;
** This change leverages Ruby's duck typing, providing flexibility and making the code easier to maintain and test.&lt;br /&gt;
&lt;br /&gt;
* Factory Pattern&lt;br /&gt;
** We will implement a factory pattern for the common processing of &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpBid&amp;lt;/code&amp;gt;, encapsulating object creation and promoting code reuse.&lt;br /&gt;
&lt;br /&gt;
* Law of Demeter (Principle of Least Knowledge)&lt;br /&gt;
** We'll delegate interactions appropriately to prevent tightly coupled code, especially when dealing with topics and teams.&lt;br /&gt;
** Clear and intentional naming during refactoring will enhance code clarity and maintainability.&lt;br /&gt;
&lt;br /&gt;
* Adapter Pattern&lt;br /&gt;
** To add messaging to bid states for users, we'll use the adapter pattern to allow different components to work together without modifying their existing code.&lt;br /&gt;
&lt;br /&gt;
== Files Modified/Added ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-1.-controllers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
review_bids_controller.rb&lt;br /&gt;
&lt;br /&gt;
signup_sheet_controller.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-2.-models&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
review_bid.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-3.-helpers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
&lt;br /&gt;
authorization_helper.rb&lt;br /&gt;
&lt;br /&gt;
Review_bids_helper.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;viii.-test-plan&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
The Excel spreadsheet below details our test plan for testing the code we changed or created to ensure code coverage. The process involved reviewing the code and determining all the possible scenarios that needed to be covered based on what the method was doing, then creating test cases accordingly.&lt;br /&gt;
&lt;br /&gt;
* [https://docs.google.com/spreadsheets/d/1iHzOdSwlTtxOtf5VsNdwOaCP8NzYMmiJ/edit?usp=drive_link Open Test Matrix Spreadsheet]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/review_bids_controller_spec.rb ReviewBidsController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/sign_up_sheet_controller_spec.rb SignUpSheetController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/models/review_bid_spec.rb ReviewBid model updated test cases]&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-1.-mentor&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Ed Gehringer&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-2.-members&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Members ===&lt;br /&gt;
&lt;br /&gt;
* Gavin Teague&lt;br /&gt;
* Janice Uwujaren&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xi.-links&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
Previous Wiki: [https://wiki.expertiza.ncsu.edu/index.php/CSC/ECE_517_Fall_2021_-_E2151._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;Link&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xii.-references&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/document/d/1LHVacylQmB15I6VcYUA5nuC7GRRU9szHzZhjlPcE-PA/edit?tab=t.0#heading=h.u0oycoj6e0ev &amp;lt;u&amp;gt;Project Instructions&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;appendix-a-uml-class-diagram-plantuml-code-for-review-bids&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Juwujar</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160954</id>
		<title>CSC/ECE 517 Fall 2024 - E2485. Allow reviewers to bid on what to review</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160954"/>
		<updated>2024-12-10T15:20:56Z</updated>

		<summary type="html">&lt;p&gt;Juwujar: /* Test Plan */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
This project will fix the defects and improve the functionality in Expertiza of the Reviewer Bidding feature, a process where reviewers can bid for assignments they want to review. It will make the functionality smoother, more reliable, and align with Expertiza's collaborative learning objectives.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iii.-problem-statements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Problem Statements ==&lt;br /&gt;
&lt;br /&gt;
Assigning reviews to users on Expertiza involves a complex bidding algorithm that requires optimization before production deployment. Below are key areas we plan to address as per the project requirements:&lt;br /&gt;
* Code Refactoring and Best Practices&lt;br /&gt;
** '''Utilize Authentication Utilities''': Modify the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; function in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to use existing authentication utilities, enhancing code reusability and adhering to the Single Responsibility Principle (SRP) and Separation of Concerns (SoC).&lt;br /&gt;
** '''Eliminate Code Duplication''': Refactor &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; to comply with the Don't Repeat Yourself (DRY) principle.&lt;br /&gt;
** '''Rename Functions for Clarity''': Change &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; to better reflect its purpose.&lt;br /&gt;
** '''Refactor Methods in '''&amp;lt;code&amp;gt;review_bid.rb&amp;lt;/code&amp;gt;''':''': Convert class methods to instance methods or relocate them to helper classes for improved code organization.&lt;br /&gt;
* Functionality Enhancements&lt;br /&gt;
** '''Handle Missing Bids Gracefully''': Adjust the bidding algorithm to accommodate students who do not bid, allowing them to select from the remaining options.&lt;br /&gt;
** '''Support Both Individual and Team Assignments''': Ensure the code functions correctly for participant and team-based assignments when signing up for topics.&lt;br /&gt;
* User Interface Improvements&lt;br /&gt;
** '''Increase Transparency in the Bidding Process''': Display messages indicating how many students are eligible to bid, how many have submitted bids, and the deadline.&lt;br /&gt;
** '''Accurate Bid Status Indicators''': Ensure topic colors change correctly based on the number of outstanding bids, providing clear visual feedback to users.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Exhaustive Edge Case Testing''': Conduct thorough testing for edge cases not previously covered, adding additional test cases as needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iv.-design-goals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Design Goals ==&lt;br /&gt;
&lt;br /&gt;
While addressing the existing defects and enhancing the Reviewer Bidding feature, we plan to adhere to the following design guidelines:&lt;br /&gt;
&lt;br /&gt;
* Code Quality and Maintainability&lt;br /&gt;
** '''Improve Code Structure: '''Implement refactoring strategies to enhance code readability and maintainability, ensuring adherence to best practices like the DRY principle.&lt;br /&gt;
** '''Standardize Authentication Logic: '''Utilize a centralized authentication utility to promote consistency and reusability across the application.&lt;br /&gt;
* System Stability and Flexibility&lt;br /&gt;
** '''Enhance Algorithm Robustness: '''Modify the bidding algorithm to handle scenarios where students do not bid, ensuring the system remains stable under all conditions.&lt;br /&gt;
** '''Ensure Assignment Versatility: '''Design the system to seamlessly support both individual and team assignments without additional configuration.&lt;br /&gt;
* User Experience Enhancements&lt;br /&gt;
** '''Increase Bidding Transparency: '''Develop user interface elements that clearly display bidding status, deadlines, and participation metrics.&lt;br /&gt;
** '''Implement Dynamic Visual Feedback: '''Use real-time visual cues, such as color changes, to inform users about the bidding status instantly.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Develop Comprehensive Test Suites: '''Create extensive automated tests covering edge cases and critical functionalities to guarantee reliable system behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;v.-uml-diagram&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== UML Diagram ==&lt;br /&gt;
&lt;br /&gt;
Below is a visual representation of the models associated with the review bids functionality, depicting the data structures and their relationships.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml class diagram2.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi.-implementation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
'''ReviewBid: '''The &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; manages the bidding process, where participants indicate their preferences for reviewing different topics or assignments. This helps distribute review tasks fairly based on participants' interests.&lt;br /&gt;
&lt;br /&gt;
'''Assignment: '''The &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model represents team or individual assignments in Expertiza. It stores all the essential information required to keep the assignment process organized, like managing participants, setting due dates, forming teams, and overseeing the review process.&lt;br /&gt;
&lt;br /&gt;
'''SignUpTopic: '''The &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt; model identifies specific topics or areas within an assignment for which participants can sign up. It manages the availability of these topics, tracks bids, coordinates team sign-ups, and assigns topics based on both bids and participant availability. &lt;br /&gt;
&lt;br /&gt;
'''Participant: '''The &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model is about the individual users who participate in an assignment. This includes students, instructors, teaching assistants, or other relevant roles. This model is responsible for managing key user information, monitoring interactions with the assignment, keeping track of team memberships, and recording things like badges and review grades.&lt;br /&gt;
&lt;br /&gt;
== Implementation ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1.-decoupling-authorization-logic-from-reviewbidscontroller&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Decoupling Authorization Logic from &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| '''&amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; needs to use the authentication utilities'''&lt;br /&gt;
&lt;br /&gt;
The function &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; should use the available authentication utilities to support the Single Responsibility Principle and Separation of Concerns and to support code reusability.&lt;br /&gt;
&lt;br /&gt;
| '''Authentication Consistency:''' Remove authentication logic in the controller and call logic in the authentication utility class instead to ensure consistent use of the same logic across the application, foster reusability, and eliminate DRY.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
==== Authorization Flow Before Refactoring ====&lt;br /&gt;
The sequence diagram depicts the authorization process within the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;. When a user invokes an action, the controller checks if the action is allowed by verifying the user's role through the &amp;lt;code&amp;gt;current_role_name()&amp;lt;/code&amp;gt; method in the &amp;lt;code&amp;gt;ApplicationController&amp;lt;/code&amp;gt;. Depending on the action and the user's role, the controller either authorizes the user to proceed or denies access, ultimately executing the action logic if authorized and responding to the user.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 1.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We simplified the &amp;lt;code&amp;gt;action_allowed?&amp;lt;/code&amp;gt; method by decoupling authorization logic from the controller to use helper methods in the &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt; module. The &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; should not handle authentication, which is a violation of SRP. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;vertical-align:top;&amp;quot; | Original Code !! style=&amp;quot;vertical-align:top;&amp;quot; | Refactored Code&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index'&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator',&lt;br /&gt;
     'Student'].include? current_role_name and&lt;br /&gt;
        ((%w[list].include? action_name) ? are_needed_authorizations_present?(params[:id], &amp;quot;participant&amp;quot;, &amp;quot;reader&amp;quot;, &amp;quot;submitter&amp;quot;, &amp;quot;reviewer&amp;quot;) : true)&lt;br /&gt;
  else&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator'].include? current_role_name&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index', 'list'&lt;br /&gt;
    current_user_has_student_privileges? &amp;amp;&amp;amp; list_authorization_check&lt;br /&gt;
  else&lt;br /&gt;
    current_user_has_ta_privileges?&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; action ===&lt;br /&gt;
The refactor of the &amp;lt;code&amp;gt;ReviewBidsController's index&amp;lt;/code&amp;gt; action simplifies and streamlines the codebase, ensuring the action simply handles controller activities. Previously, the controller was overloaded with responsibilities—it handled everything from locating and validating assignments to fetching review mappings and counting completed reviews. This all-in-one approach made the controller action messy. &lt;br /&gt;
&lt;br /&gt;
Now we have moved the business logic out of the controller action and into dedicated service objects. With this change, we introduced two key service objects: &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; focuses on retrieving the participant along with their assignments, while the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; takes on the task of managing review mappings and calculating review-related stats, like how many reviews have been completed. &lt;br /&gt;
&lt;br /&gt;
This clear separation of responsibilities aligns with the Single Responsibility Principle, which helps each part of our code be more focused and easier to understand. With these new service objects, the code has become more reusable. The logic for fetching assignments, retrieving review mappings, and counting reviews is now more flexible and reusable throughout the application. For example, we used methods in the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; in both the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; and the &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;, helping to avoid code duplication.&lt;br /&gt;
&lt;br /&gt;
After moving the business logic to service objects, we can write unit tests to ensure each part of the code behaves as expected and prevents bugs. We also moved complex calculations, like those for completed reviews, into the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;, freeing the controller from dealing with the implementation details and promoting a cleaner design. The article https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial provides more information on when and how to use service objects in Rails.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
  return unless current_user_id?(@participant.user_id)&lt;br /&gt;
  @assignment = @participant.assignment&lt;br /&gt;
  @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
  # Finding how many reviews have been completed&lt;br /&gt;
  @num_reviews_completed = 0&lt;br /&gt;
  @review_mappings.each do |map|&lt;br /&gt;
    @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # render view for completing reviews after review bidding has been completed&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @assignment = participant_service.assignment&lt;br /&gt;
  unless @assignment.is_a?(Assignment)&lt;br /&gt;
    flash[:error] = 'Assignment not found.'&lt;br /&gt;
    redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  @review_mappings = review_service.review_mappings&lt;br /&gt;
  @num_reviews_completed = review_service.review_counts[:completed]&lt;br /&gt;
&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Optimize &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Action functionality ===&lt;br /&gt;
In the earlier version of the set_priority action in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, the server only needed a couple of key pieces of information: an array of topic IDs (params[:topic]) and the participant ID (params[:id}). From there, it would figure out the assignment_id by using the first topic as a reference. While this worked in simple cases, it could lead to problems if any data was missing or didn’t line up correctly.&lt;br /&gt;
&lt;br /&gt;
With the new implementation, the action now expects clear and structured parameters, including participant_id, an array of topic IDs, and assignment_id. This change ensures that the server always receives complete and reliable information, preventing issues caused by missing or inconsistent data. It also removes the need for the server to perform unnecessary lookups, allowing the controller to focus on its main job: validating inputs (like making sure the topics in params[:topic] belong to the same assignment as the participant), updating the database, handling errors properly, and sending the correct response back to the view.&lt;br /&gt;
&lt;br /&gt;
While these updates have cleaned up the set_priority action, they still contain a lot of business logic, like validations and priority updates. To make the code even more maintainable, there’s room for further refactoring—perhaps moving some of this logic into a service class or model method to streamline the controller even more.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | Old Code&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    if params[:topic].nil?&lt;br /&gt;
      ReviewBid.where(participant_id: params[:id]).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      participant = AssignmentParticipant.find_by(id: params[:id])&lt;br /&gt;
      assignment_id = SignUpTopic.find(params[:topic].first).assignment.id&lt;br /&gt;
      @bids = ReviewBid.where(participant_id: params[:id])&lt;br /&gt;
      signed_up_topics = ReviewBid.where(participant_id: params[:id]).map(&amp;amp;:signuptopic_id)&lt;br /&gt;
      signed_up_topics -= params[:topic].map(&amp;amp;:to_i)&lt;br /&gt;
      signed_up_topics.each do |topic|&lt;br /&gt;
        ReviewBid.where(signuptopic_id: topic, participant_id: params[:id]).destroy_all&lt;br /&gt;
      end&lt;br /&gt;
      params[:topic].each_with_index do |topic_id, index|&lt;br /&gt;
        bid_existence = ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id])&lt;br /&gt;
        if bid_existence.empty?&lt;br /&gt;
          ReviewBid.create(priority: index + 1, signuptopic_id: topic_id, participant_id: params[:id], assignment_id: assignment_id)&lt;br /&gt;
        else&lt;br /&gt;
          ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id]).update_all(priority: index + 1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to action: 'show', assignment_id: params[:assignment_id], id: params[:id]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    participant_id = params[:participant_id].to_i&lt;br /&gt;
    selected_topic_ids = params[:topic]&amp;amp;.map(&amp;amp;:to_i) || []&lt;br /&gt;
    assignment_id = params[:assignment_id].to_i&lt;br /&gt;
&lt;br /&gt;
    unless current_user_can_modify_participant?(participant_id)&lt;br /&gt;
      flash[:error] = 'You are not authorized to modify these bids.'&lt;br /&gt;
      respond_to do |format|&lt;br /&gt;
        format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
        format.json { render json: { status: 'unauthorized' }, status: :unauthorized }&lt;br /&gt;
      end&lt;br /&gt;
      return&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    if selected_topic_ids.empty?&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      assignment = Assignment.find_by(id: assignment_id)&lt;br /&gt;
      if assignment.nil?&lt;br /&gt;
        flash[:error] = &amp;quot;Invalid assignment.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_assignment' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      unless SignUpTopic.where(id: selected_topic_ids, assignment_id: assignment_id).count == selected_topic_ids.size&lt;br /&gt;
        flash[:error] = &amp;quot;One or more selected topics are invalid.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_topics' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).where.not(signuptopic_id: selected_topic_ids).destroy_all&lt;br /&gt;
&lt;br /&gt;
      selected_topic_ids.each_with_index do |topic_id, index|&lt;br /&gt;
        bid = ReviewBid.find_or_initialize_by(signuptopic_id: topic_id, participant_id: participant_id)&lt;br /&gt;
        bid.priority = index + 1&lt;br /&gt;
        bid.assignment_id = assignment_id&lt;br /&gt;
        bid.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_to action: 'show', assignment_id: assignment_id, id: participant_id, notice: 'Review bids updated successfully.' }&lt;br /&gt;
      format.json { render json: { status: 'success' }, status: :ok }&lt;br /&gt;
    end&lt;br /&gt;
  rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
    flash[:error] = &amp;quot;Failed to update priorities: #{e.message}&amp;quot;&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
      format.json { render json: { status: 'error', message: e.message }, status: :unprocessable_entity }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Client Side Changes to Support &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Changes ====&lt;br /&gt;
After tidying up the server-side logic, we needed to refactor the client-side code to ensure it passed the correct information from the view to the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action. On the client side, we now grab the topicIDs and send them as &amp;lt;code&amp;gt;selected_topic_ids&amp;lt;/code&amp;gt;, allowing the controller to figure out which topics were selected and in what order. It’s a cleaner and more reliable way to keep everything working together! &lt;br /&gt;
&lt;br /&gt;
We are also extracting the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, making it a lot easier to send data with AJAX and keeping things flexible. AJAX is awesome because it lets us update the server without reloading the page, making the user experience smooth and responsive. When we pull the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, we can grab it along with other data, like the list of topics, and include it in the request. This way, we don’t have to rely on the URL to know which participant we’re working with, which makes the code cleaner and more adaptable.&lt;br /&gt;
&lt;br /&gt;
If we were to rely on the route for the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt;, every AJAX request would need to be tied to a specific URL structure. That’s limiting, especially if the route doesn’t naturally include the participant ID, as can be the case with collection routes versus member routes in Rails. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery -&amp;gt;&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: -&amp;gt;&lt;br /&gt;
      $.post($(this).data('update-url'), $(this).sortable('serialize'))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery(document).ready ($) -&amp;gt;&lt;br /&gt;
  sendUpdatedOrder = -&amp;gt;&lt;br /&gt;
    sortedIDs = $(this).sortable('toArray')&lt;br /&gt;
&lt;br /&gt;
    topicIDs = sortedIDs.map (id) -&amp;gt;&lt;br /&gt;
      id.replace 'topic_', ''&lt;br /&gt;
&lt;br /&gt;
    participant_id = $(this).data('participant-id')&lt;br /&gt;
&lt;br /&gt;
    assignment_id = new URL($(this).data('update-url'), window.location.origin).searchParams.get('assignment_id')&lt;br /&gt;
&lt;br /&gt;
    $.ajax&lt;br /&gt;
      type: 'POST'&lt;br /&gt;
      url: $(this).data('update-url')&lt;br /&gt;
      data:&lt;br /&gt;
        topic: topicIDs&lt;br /&gt;
        participant_id: participant_id&lt;br /&gt;
        assignment_id: assignment_id&lt;br /&gt;
      success: (response) -&amp;gt;&lt;br /&gt;
        console.log 'Selections updated successfully.'&lt;br /&gt;
      error: (xhr, status, error) -&amp;gt;&lt;br /&gt;
        alert 'An error occurred while saving your selections. Please try again.'&lt;br /&gt;
        console.error error&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: sendUpdatedOrder&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-1.-signupsheetcontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; Updates ===&lt;br /&gt;
&lt;br /&gt;
The ReviewBidsController's index method renders the review_bids_others_work action in &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt;. However, the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; does not include the controller action for this view. We need to find the old implementation of the controller action, add it back, and review and update it for any DRY violations.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # provides variables for reviewing page located at views/review_bids/others_work.html.erb&lt;br /&gt;
  def index&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
    return unless current_user_id?(@participant.user_id)&lt;br /&gt;
&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
    # Finding how many reviews have been completed&lt;br /&gt;
    @num_reviews_completed = 0&lt;br /&gt;
    @review_mappings.each do |map|&lt;br /&gt;
      @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # render view for completing reviews after review bidding has been completed&lt;br /&gt;
    render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
  end  &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-2.-reviewbidsotherswork-view-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;review_bids_others_work.html.erb&amp;lt;/code&amp;gt; View Updates ====&lt;br /&gt;
&lt;br /&gt;
The code only contains a view with this name and no associated controller action. Reviewing the code in the view, we noticed some SRP violations where there is much logic in the presentation layer. Set up helper methods in the controller to handle some of that logic or see if the model can handle some of that logic.&lt;br /&gt;
&lt;br /&gt;
Note: The view is technically part of the SignUpSheetController, but it's being rendered by the ReviewBidsController, which initially threw us off. After chatting with the professor, we decided not to change this view. He indicated that the plan is to reuse these views for both assignment bidding and review bidding.&lt;br /&gt;
&lt;br /&gt;
We would suggest building the assignment bidding and review bidding features separately first to make sure each one works on its own. Once that’s done, we can go back and clean things up by combining any shared functionality. This could include creating shared views or even introducing an abstract model to handle both types of bidding in a cleaner way. It could also mean replacing the ReviewBidsController with something more generic, like a BidsController, to keep things reusable and easier to maintain down the line.&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Rename &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; method ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; should be &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt;&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:''' Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The primary activity here is to refactor the method name &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; and fix the hardcoded URL. However, the match topic bidding algorithm no longer works since Heroku is no longer free, per the professor. The reference link below has detailed information about the web service that we can leverage to gain that understanding.&lt;br /&gt;
&lt;br /&gt;
Reference Link for Webservice Details: [https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Updated Implementation ====&lt;br /&gt;
The original requirement was to rename the run_bidding_algorithm method to assign_reviewers, but after digging into the code, we realized that renaming it didn’t make sense. The method runs the bidding algorithm to match topics, and there’s already a separate method to handle assigning review topics.&lt;br /&gt;
&lt;br /&gt;
However, we improved the logic in the assign_bidding method (which is called by the run_bidding_algorithm) by introducing a separate BiddingAlgorithmService object. This keeps the business logic out of the controller action, which makes things cleaner and more maintainable.&lt;br /&gt;
&lt;br /&gt;
==== review_bids_controller ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def run_bidding_algorithm&lt;br /&gt;
    review_bid = ReviewBid.new&lt;br /&gt;
    bidding_data = review_bid.bidding_data&lt;br /&gt;
    matched_topics = BiddingAlgorithmService.new(bidding_data).run&lt;br /&gt;
    raise ArgumentError, 'Failed to assign reviewers. Please try again later.' unless matched_topics&lt;br /&gt;
&lt;br /&gt;
    matched_topics&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== bidding_algorithm_service ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# frozen_string_literal: true&lt;br /&gt;
&lt;br /&gt;
# The `BiddingAlgorithmService` run the bid assignment algorithm&lt;br /&gt;
# Sends student IDs, topic IDs, student preferences, and timestamps to the web service&lt;br /&gt;
# The web service returns the matched assignments in the JSON response body&lt;br /&gt;
class BiddingAlgorithmService&lt;br /&gt;
  SERVICE_URL = 'http://app-csc517.herokuapp.com/match_topics'.freeze&lt;br /&gt;
&lt;br /&gt;
  # MOCK_DATA is based on the documentation detailing how the webservice behaves.&lt;br /&gt;
  # Reference: https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review#Webservice&lt;br /&gt;
  MOCK_DATA = {&lt;br /&gt;
    36239 =&amp;gt; [3970, 3972, 3975],&lt;br /&gt;
    36240 =&amp;gt; [3973, 3974, 3972],&lt;br /&gt;
    36241 =&amp;gt; [3969, 3971, 3972],&lt;br /&gt;
    36242 =&amp;gt; [3969, 3971, 3973],&lt;br /&gt;
    36243 =&amp;gt; [3969, 3970, 3971]&lt;br /&gt;
  }.freeze&lt;br /&gt;
&lt;br /&gt;
  def initialize(bidding_data)&lt;br /&gt;
    @bidding_data = bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def run&lt;br /&gt;
    return MOCK_DATA if Rails.application.config.use_mock_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
    perform_request&lt;br /&gt;
  rescue RestClient::ExceptionWithResponse, JSON::ParserError&lt;br /&gt;
    false&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def perform_request&lt;br /&gt;
    response = RestClient.post(&lt;br /&gt;
      SERVICE_URL,&lt;br /&gt;
      @bidding_data.to_json,&lt;br /&gt;
      content_type: :json,&lt;br /&gt;
      accept: :json&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    validate_response(response)&lt;br /&gt;
    JSON.parse(response.body)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def validate_response(response)&lt;br /&gt;
    raise 'Invalid response format' unless response.headers[:content_type] &amp;amp;&amp;amp; response.headers[:content_type].include?('application/json')&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Resolve Functionality Issues for Non Bidders ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Currently it doesn't work if some student does not bid. In this case, algorithm needs to be fixed to ignore anyone who didn’t bid, and let them choose from what’s left over.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to do several things within this controller. We will set a default state for non-bidders that we can point to to give them options to select topics to review from the leftovers. We will consolidate &amp;lt;code&amp;gt;@sign_up_topics&amp;lt;/code&amp;gt; to resolve the DRY violation. Lastly, we will remove business logic from the show method and push it to a new helper to conform to the SRP.&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # Additional method&lt;br /&gt;
&lt;br /&gt;
  # Provides variables for review bidding page&lt;br /&gt;
  def show&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id].to_i)&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @sign_up_topics = SignUpTopic.where(assignment_id: @assignment.id, private_to: nil)&lt;br /&gt;
    my_topic = SignedUpTeam.topic_id(@participant.parent_id, @participant.user_id)&lt;br /&gt;
    @sign_up_topics -= SignUpTopic.where(assignment_id: @assignment.id, id: my_topic)&lt;br /&gt;
    @num_participants = AssignmentParticipant.where(parent_id: @assignment.id).count&lt;br /&gt;
    @selected_topics = nil # This is used to list the topics assigned to review (i.e., select == assigned)&lt;br /&gt;
    @bids = ReviewBid.where(participant_id: @participant, assignment_id: @assignment.id)&lt;br /&gt;
    signed_up_topics = []&lt;br /&gt;
    &lt;br /&gt;
    @bids.each do |bid|&lt;br /&gt;
      sign_up_topic = SignUpTopic.find_by(id: bid.signuptopic_id)&lt;br /&gt;
      signed_up_topics &amp;lt;&amp;lt; sign_up_topic if sign_up_topic&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    signed_up_topics &amp;amp;= @sign_up_topics&lt;br /&gt;
    @sign_up_topics -= signed_up_topics&lt;br /&gt;
    @bids = signed_up_topics&lt;br /&gt;
    @num_of_topics = @sign_up_topics.size&lt;br /&gt;
    @assigned_review_maps = []&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: @assignment.id, reviewer_id: @participant.id).each do |review_map|&lt;br /&gt;
      @assigned_review_maps &amp;lt;&amp;lt; review_map&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Explicitly render view since it's in the sign up sheet views&lt;br /&gt;
    render 'sign_up_sheet/review_bids_show'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Additional methods&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-2.-reviewbidshelper-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
 # Assigns bidding topics to reviewers&lt;br /&gt;
  def assign_bidding&lt;br /&gt;
      assignment = validate_assignment(params[:assignment_id])&lt;br /&gt;
&lt;br /&gt;
      reviewers = validate_reviewers(assignment.id)&lt;br /&gt;
      reviewer_ids = reviewers.map(&amp;amp;:id)&lt;br /&gt;
&lt;br /&gt;
      matched_topics = run_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
      if matched_topics.blank?&lt;br /&gt;
        flash[:alert] = 'Topic or assignment is missing'&lt;br /&gt;
        redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      leftover_topics = find_leftover_topics(assignment.id, matched_topics)&lt;br /&gt;
      assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.new.assign_review_topics(matched_topics)&lt;br /&gt;
      assignment.update!(can_choose_topic_to_review: false)&lt;br /&gt;
&lt;br /&gt;
      flash[:notice] = 'Reviewers were successfully assigned to topics.'&lt;br /&gt;
      redirect_back fallback_location: root_path&lt;br /&gt;
    rescue ArgumentError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ArgumentError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: e.message&lt;br /&gt;
    rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::RecordInvalid: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue ActiveRecord::ActiveRecordError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::ActiveRecordError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue StandardError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;StandardError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers. Please try again later.'&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  #service methods here ...&lt;br /&gt;
&lt;br /&gt;
  def find_leftover_topics(assignment_id, matched_topics)&lt;br /&gt;
    all_topic_ids = SignUpTopic.where(assignment_id: assignment_id).pluck(:id)&lt;br /&gt;
    assigned_topic_ids = matched_topics.map { |match| match[:topic_id] }&lt;br /&gt;
&lt;br /&gt;
    # Calculate leftover topics&lt;br /&gt;
    all_topic_ids - assigned_topic_ids&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
    return if leftover_topics.blank?&lt;br /&gt;
&lt;br /&gt;
    # Find non-bidders by excluding already matched reviewers&lt;br /&gt;
    non_bidders = reviewer_ids - matched_topics.keys&lt;br /&gt;
&lt;br /&gt;
    # Assign leftover topics to non-bidders in a round-robin fashion&lt;br /&gt;
    non_bidders.each_with_index do |reviewer_id, index|&lt;br /&gt;
      topic_id = leftover_topics[index % leftover_topics.length]&lt;br /&gt;
      ReviewBid.create(priority: 1, signuptopic_id: topic_id, participant_id: reviewer_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirm Functionality for Single-person Teams ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Make sure your code works on individual assignments, i.e., assignments where participants sign up for topics instead of teams. In this case, a team is created for each participant when they sign up. So the code should work for assignments to which either individuals or teams submit.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to make an update to focus on topic rather than team or have it where teams are updated based on topic id.We will probably utilize sign up topic or a similar object to accomplish this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-5-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bid Status Messaging ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| To make the functionality more intuitive, include a message to say how many students are eligible to submit bids, how many have submitted their bids, when the deadline for submitting bids is.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
The logic for these messages will go into the helper file. It is effectively the same reasoning as our migration of other business/functionality code from the controller to the helper to conform to the SRP. It will go hand in hand with the SignUpTopic portion of the code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-6-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Why are the methods in review_bid.rb class methods? Can we change them to instance methods or move it to helpers?&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-1.-reviewbid-model-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model Updates ====&lt;br /&gt;
Since the current class methods are tied to &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model instances, we will change them to instance methods. There is no need to move them to helpers.&lt;br /&gt;
&lt;br /&gt;
===== Review Bid Model Flow Before Refactoring =====&lt;br /&gt;
This sequence diagram describes how the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; interacts with the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model in assigning review topics to reviewers according to their bids. Once the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; action is initiated by the user, the controller gathers the IDs of reviewers and calls &amp;lt;code&amp;gt;ReviewBid.bidding_data&amp;lt;/code&amp;gt; to gather bidding information. That is sent out to an external bidding algorithm, and the matched topics that come back are used by &amp;lt;code&amp;gt;ReviewBid.assign_review_topics&amp;lt;/code&amp;gt; to create &amp;lt;code&amp;gt;ReviewResponseMap&amp;lt;/code&amp;gt; entries, assigning topics to reviewers for the assignment.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
  belongs_to :assignment, class_name: 'Assignment'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  def self.bidding_data(assignment_id, reviewer_ids)&lt;br /&gt;
    # create basic hash and set basic hash data&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'users' =&amp;gt; {}, 'max_accepted_proposals' =&amp;gt; [] }&lt;br /&gt;
    bidding_data['tid'] = SignUpTopic.where(assignment_id: assignment_id).ids&lt;br /&gt;
    bidding_data['max_accepted_proposals'] = Assignment.where(id: assignment_id).pluck(:num_reviews_allowed).first&lt;br /&gt;
&lt;br /&gt;
    # loop through reviewer_ids to get reviewer specific bidding data&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      bidding_data['users'][reviewer_id] = reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # assigns topics to reviews as matched by the webservice algorithm&lt;br /&gt;
  def self.assign_review_topics(assignment_id, reviewer_ids, matched_topics, _min_num_reviews = 2)&lt;br /&gt;
    # if review response map already created, delete it&lt;br /&gt;
    if ReviewResponseMap.where(reviewed_object_id: assignment_id)&lt;br /&gt;
      ReviewResponseMap.where(reviewed_object_id: assignment_id).destroy_all&lt;br /&gt;
    end&lt;br /&gt;
    # loop through reviewer_ids to assign reviews to each reviewer&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      topics_to_assign = matched_topics[reviewer_id.to_s]&lt;br /&gt;
      topics_to_assign.each do |topic|&lt;br /&gt;
        assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method to assign a single topic to a reviewer&lt;br /&gt;
  def self.assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic).pluck(:team_id).first&lt;br /&gt;
    team_to_review.nil? ? [] : ReviewResponseMap.create(reviewed_object_id: assignment_id, reviewer_id: reviewer_id, reviewee_id: team_to_review, type: 'ReviewResponseMap')&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method for getting individual reviewer_ids bidding data&lt;br /&gt;
  # returns user's bidding data hash&lt;br /&gt;
  def self.reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    reviewer_user_id = AssignmentParticipant.find(reviewer_id).user_id&lt;br /&gt;
    self_topic = SignedUpTeam.topic_id(assignment_id, reviewer_user_id)&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'otid' =&amp;gt; self_topic, 'priority' =&amp;gt; [], 'time' =&amp;gt; [] }&lt;br /&gt;
    bids = ReviewBid.where(participant_id: reviewer_id)&lt;br /&gt;
&lt;br /&gt;
    # loop through each bid for a topic to get specific data&lt;br /&gt;
    bids.each do |bid|&lt;br /&gt;
      bidding_data['tid'] &amp;lt;&amp;lt; bid.signuptopic_id&lt;br /&gt;
      bidding_data['priority'] &amp;lt;&amp;lt; bid.priority&lt;br /&gt;
      bidding_data['time'] &amp;lt;&amp;lt; bid.updated_at&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  # Instance method to get bidding data for this review bid&lt;br /&gt;
  def bidding_data&lt;br /&gt;
    {&lt;br /&gt;
      'tid' =&amp;gt; topic_ids,&lt;br /&gt;
      'otid' =&amp;gt; self_topic,&lt;br /&gt;
      'priority' =&amp;gt; priorities,&lt;br /&gt;
      'time' =&amp;gt; updated_times&lt;br /&gt;
    }&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # bid object to limit db touches for similar data acquisitions&lt;br /&gt;
  def bids&lt;br /&gt;
    @bids ||= ReviewBid.where(participant_id: participant_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def topic_ids&lt;br /&gt;
    bids.pluck(:signuptopic_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def priorities&lt;br /&gt;
    bids.pluck(:priority)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def updated_times&lt;br /&gt;
    bids.pluck(:updated_at)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self_topic&lt;br /&gt;
    return nil unless topic &amp;amp;&amp;amp; topic.assignment_id&lt;br /&gt;
&lt;br /&gt;
    SignedUpTeam.topic_id(topic.assignment_id, participant.user_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign topics to reviews&lt;br /&gt;
  def assign_review_topics(matched_topics)&lt;br /&gt;
    validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: topic.assignment_id).destroy_all&lt;br /&gt;
&lt;br /&gt;
    matched_topics.each do |reviewer_id, topics|&lt;br /&gt;
      Array(topics).each { |topic_id| assign_topic_to_reviewer(reviewer_id, topic_id) }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign a single topic to a reviewer&lt;br /&gt;
  def assign_topic_to_reviewer(reviewer_id, topic_id)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic_id).pluck(:team_id).first&lt;br /&gt;
    return if team_to_review.nil?&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.create(&lt;br /&gt;
      reviewed_object_id: topic.assignment_id,&lt;br /&gt;
      reviewer_id: reviewer_id,&lt;br /&gt;
      reviewee_id: team_to_review,&lt;br /&gt;
      type: 'ReviewResponseMap'&lt;br /&gt;
    )&lt;br /&gt;
    # error handling&lt;br /&gt;
  rescue StandardError =&amp;gt; e&lt;br /&gt;
    Rails.logger.error(&amp;quot;Failed to assign topic #{topic_id} to reviewer #{reviewer_id}: #{e.message}&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
    raise ArgumentError, 'Topic or assignment is missing' if topic.nil? || topic.assignment_id.nil?&lt;br /&gt;
    raise ArgumentError, 'Matched topics must be a Hash' unless matched_topics.is_a?(Hash)&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
Once we change them to instance methods, we’ll need to modify their use in the code to instantiate the object first.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;Placeholder for implementation screenshots (before and after)&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-3.-schema.db-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;Schema.db&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We also need to remove the foreign key reference to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model since the &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model already has access to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;, and that is the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; we should be referencing. This will also involve modifying the database to remove the foreign key reference to the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Steps required:&lt;br /&gt;
&lt;br /&gt;
# Review the records in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table in the database to see if there are any records and if those records have values for &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;&lt;br /&gt;
# Create a migration file using the command: &amp;lt;code&amp;gt;rails generate migration RemoveAssignmentFromReviewBids&amp;lt;/code&amp;gt;&lt;br /&gt;
# In the generated file, indicate the change that needs to be made using the change method and migration columns &amp;lt;code&amp;gt;remove_foreign_key&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;remove_column&amp;lt;/code&amp;gt;. Migration should be straightforward since there is no cascading behavior (i.e., &amp;lt;code&amp;gt;dependent:destroy&amp;lt;/code&amp;gt;) to worry about.&lt;br /&gt;
# Once comfortable with the updates to the generated file, run the migration using the &amp;lt;code&amp;gt;rails db:migrate&amp;lt;/code&amp;gt; command.&lt;br /&gt;
# Run a query in the DB to ensure no orphaned records are in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table after the updates.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;Placeholder for implementation screenshots (before and after)&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-4.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implement Edge Case Testing ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| In the previous implementation wiki, there are edge cases which are not exhaustively tested. Should test those edge cases thoroughly and add more edge case testing - link&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span class=&amp;quot;mark&amp;quot;&amp;gt;TODO&amp;lt;/span&amp;gt;:We just got initial test results back. We still need to figure out why tests are failing and what may be slipping through the cracks.[[File:Test expertiza.png|411x459px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-8-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design Patterns and Principles ===&lt;br /&gt;
Below are the design patterns and principles we plan to use to address the problem statements outlined in the project requirements:&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
** Controller Responsibility&lt;br /&gt;
*** To address the issue of authentication logic residing in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, we will refactor the code to ensure the controller solely handles request and response flows.&lt;br /&gt;
*** Authentication logic will be moved to dedicated methods in &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt;, making the codebase easier to maintain and test.&lt;br /&gt;
** View Simplification&lt;br /&gt;
*** Currently, the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; view contains excessive business logic, violating SRP.&lt;br /&gt;
*** We will move this logic into helper methods or models, ensuring each component has a single responsibility and improving code readability.&lt;br /&gt;
&lt;br /&gt;
* Separation of Concerns (SoC)&lt;br /&gt;
** By relocating authorization logic out of the controller, we separate authentication from request handling.&lt;br /&gt;
** Adhering to SoC enhances readability and maintainability by allowing each part of the application to focus on its specific role.&lt;br /&gt;
* Don't Repeat Yourself (DRY)&lt;br /&gt;
** We will inspect the existing implementation for code duplication, particularly in the controller actions.&lt;br /&gt;
** Any repetitive code will be refactored or removed to reduce redundancy and improve clarity.&lt;br /&gt;
&lt;br /&gt;
* Model-View-Controller (MVC) Adherence&lt;br /&gt;
** Moving business logic from the view aligns with MVC best practices.&lt;br /&gt;
** By ensuring the view only presents data without processing it, we maintain clear boundaries between the model, view, and controller layers.&lt;br /&gt;
&lt;br /&gt;
* Dependency Inversion Principle (DIP)&lt;br /&gt;
** We plan to update class methods to instance methods, allowing the controller to depend on abstractions rather than concrete implementations.&lt;br /&gt;
** This change leverages Ruby's duck typing, providing flexibility and making the code easier to maintain and test.&lt;br /&gt;
&lt;br /&gt;
* Factory Pattern&lt;br /&gt;
** We will implement a factory pattern for the common processing of &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpBid&amp;lt;/code&amp;gt;, encapsulating object creation and promoting code reuse.&lt;br /&gt;
&lt;br /&gt;
* Law of Demeter (Principle of Least Knowledge)&lt;br /&gt;
** We'll delegate interactions appropriately to prevent tightly coupled code, especially when dealing with topics and teams.&lt;br /&gt;
** Clear and intentional naming during refactoring will enhance code clarity and maintainability.&lt;br /&gt;
&lt;br /&gt;
* Adapter Pattern&lt;br /&gt;
** To add messaging to bid states for users, we'll use the adapter pattern to allow different components to work together without modifying their existing code.&lt;br /&gt;
&lt;br /&gt;
== Files Modified/Added ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-1.-controllers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
review_bids_controller.rb&lt;br /&gt;
&lt;br /&gt;
signup_sheet_controller.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-2.-models&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
review_bid.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-3.-helpers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
&lt;br /&gt;
authorization_helper.rb&lt;br /&gt;
&lt;br /&gt;
Review_bids_helper.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;viii.-test-plan&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
The Excel spreadsheet below details our test plan for testing the code we changed or created to ensure code coverage. The process involved reviewing the code and determining all the possible scenarios that needed to be covered based on what the method was doing, then creating test cases accordingly.&lt;br /&gt;
&lt;br /&gt;
* [https://docs.google.com/spreadsheets/d/1iHzOdSwlTtxOtf5VsNdwOaCP8NzYMmiJ/edit?usp=drive_link Open Test Matrix Spreadsheet]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/review_bids_controller_spec.rb ReviewBidsController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/sign_up_sheet_controller_spec.rb SignUpSheetController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/models/review_bid_spec.rb ReviewBid model updated test cases]&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-1.-mentor&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Ed Gehringer&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-2.-members&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Members ===&lt;br /&gt;
&lt;br /&gt;
* Gavin Teague&lt;br /&gt;
* Janice Uwujaren&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xi.-links&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
Previous Wiki: [https://wiki.expertiza.ncsu.edu/index.php/CSC/ECE_517_Fall_2021_-_E2151._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;Link&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xii.-references&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/document/d/1LHVacylQmB15I6VcYUA5nuC7GRRU9szHzZhjlPcE-PA/edit?tab=t.0#heading=h.u0oycoj6e0ev &amp;lt;u&amp;gt;Project Instructions&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;appendix-a-uml-class-diagram-plantuml-code-for-review-bids&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Juwujar</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160953</id>
		<title>CSC/ECE 517 Fall 2024 - E2485. Allow reviewers to bid on what to review</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160953"/>
		<updated>2024-12-10T15:18:10Z</updated>

		<summary type="html">&lt;p&gt;Juwujar: /* Test Plan */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
This project will fix the defects and improve the functionality in Expertiza of the Reviewer Bidding feature, a process where reviewers can bid for assignments they want to review. It will make the functionality smoother, more reliable, and align with Expertiza's collaborative learning objectives.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iii.-problem-statements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Problem Statements ==&lt;br /&gt;
&lt;br /&gt;
Assigning reviews to users on Expertiza involves a complex bidding algorithm that requires optimization before production deployment. Below are key areas we plan to address as per the project requirements:&lt;br /&gt;
* Code Refactoring and Best Practices&lt;br /&gt;
** '''Utilize Authentication Utilities''': Modify the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; function in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to use existing authentication utilities, enhancing code reusability and adhering to the Single Responsibility Principle (SRP) and Separation of Concerns (SoC).&lt;br /&gt;
** '''Eliminate Code Duplication''': Refactor &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; to comply with the Don't Repeat Yourself (DRY) principle.&lt;br /&gt;
** '''Rename Functions for Clarity''': Change &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; to better reflect its purpose.&lt;br /&gt;
** '''Refactor Methods in '''&amp;lt;code&amp;gt;review_bid.rb&amp;lt;/code&amp;gt;''':''': Convert class methods to instance methods or relocate them to helper classes for improved code organization.&lt;br /&gt;
* Functionality Enhancements&lt;br /&gt;
** '''Handle Missing Bids Gracefully''': Adjust the bidding algorithm to accommodate students who do not bid, allowing them to select from the remaining options.&lt;br /&gt;
** '''Support Both Individual and Team Assignments''': Ensure the code functions correctly for participant and team-based assignments when signing up for topics.&lt;br /&gt;
* User Interface Improvements&lt;br /&gt;
** '''Increase Transparency in the Bidding Process''': Display messages indicating how many students are eligible to bid, how many have submitted bids, and the deadline.&lt;br /&gt;
** '''Accurate Bid Status Indicators''': Ensure topic colors change correctly based on the number of outstanding bids, providing clear visual feedback to users.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Exhaustive Edge Case Testing''': Conduct thorough testing for edge cases not previously covered, adding additional test cases as needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iv.-design-goals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Design Goals ==&lt;br /&gt;
&lt;br /&gt;
While addressing the existing defects and enhancing the Reviewer Bidding feature, we plan to adhere to the following design guidelines:&lt;br /&gt;
&lt;br /&gt;
* Code Quality and Maintainability&lt;br /&gt;
** '''Improve Code Structure: '''Implement refactoring strategies to enhance code readability and maintainability, ensuring adherence to best practices like the DRY principle.&lt;br /&gt;
** '''Standardize Authentication Logic: '''Utilize a centralized authentication utility to promote consistency and reusability across the application.&lt;br /&gt;
* System Stability and Flexibility&lt;br /&gt;
** '''Enhance Algorithm Robustness: '''Modify the bidding algorithm to handle scenarios where students do not bid, ensuring the system remains stable under all conditions.&lt;br /&gt;
** '''Ensure Assignment Versatility: '''Design the system to seamlessly support both individual and team assignments without additional configuration.&lt;br /&gt;
* User Experience Enhancements&lt;br /&gt;
** '''Increase Bidding Transparency: '''Develop user interface elements that clearly display bidding status, deadlines, and participation metrics.&lt;br /&gt;
** '''Implement Dynamic Visual Feedback: '''Use real-time visual cues, such as color changes, to inform users about the bidding status instantly.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Develop Comprehensive Test Suites: '''Create extensive automated tests covering edge cases and critical functionalities to guarantee reliable system behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;v.-uml-diagram&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== UML Diagram ==&lt;br /&gt;
&lt;br /&gt;
Below is a visual representation of the models associated with the review bids functionality, depicting the data structures and their relationships.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml class diagram2.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi.-implementation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
'''ReviewBid: '''The &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; manages the bidding process, where participants indicate their preferences for reviewing different topics or assignments. This helps distribute review tasks fairly based on participants' interests.&lt;br /&gt;
&lt;br /&gt;
'''Assignment: '''The &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model represents team or individual assignments in Expertiza. It stores all the essential information required to keep the assignment process organized, like managing participants, setting due dates, forming teams, and overseeing the review process.&lt;br /&gt;
&lt;br /&gt;
'''SignUpTopic: '''The &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt; model identifies specific topics or areas within an assignment for which participants can sign up. It manages the availability of these topics, tracks bids, coordinates team sign-ups, and assigns topics based on both bids and participant availability. &lt;br /&gt;
&lt;br /&gt;
'''Participant: '''The &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model is about the individual users who participate in an assignment. This includes students, instructors, teaching assistants, or other relevant roles. This model is responsible for managing key user information, monitoring interactions with the assignment, keeping track of team memberships, and recording things like badges and review grades.&lt;br /&gt;
&lt;br /&gt;
== Implementation ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1.-decoupling-authorization-logic-from-reviewbidscontroller&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Decoupling Authorization Logic from &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| '''&amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; needs to use the authentication utilities'''&lt;br /&gt;
&lt;br /&gt;
The function &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; should use the available authentication utilities to support the Single Responsibility Principle and Separation of Concerns and to support code reusability.&lt;br /&gt;
&lt;br /&gt;
| '''Authentication Consistency:''' Remove authentication logic in the controller and call logic in the authentication utility class instead to ensure consistent use of the same logic across the application, foster reusability, and eliminate DRY.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
==== Authorization Flow Before Refactoring ====&lt;br /&gt;
The sequence diagram depicts the authorization process within the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;. When a user invokes an action, the controller checks if the action is allowed by verifying the user's role through the &amp;lt;code&amp;gt;current_role_name()&amp;lt;/code&amp;gt; method in the &amp;lt;code&amp;gt;ApplicationController&amp;lt;/code&amp;gt;. Depending on the action and the user's role, the controller either authorizes the user to proceed or denies access, ultimately executing the action logic if authorized and responding to the user.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 1.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We simplified the &amp;lt;code&amp;gt;action_allowed?&amp;lt;/code&amp;gt; method by decoupling authorization logic from the controller to use helper methods in the &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt; module. The &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; should not handle authentication, which is a violation of SRP. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;vertical-align:top;&amp;quot; | Original Code !! style=&amp;quot;vertical-align:top;&amp;quot; | Refactored Code&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index'&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator',&lt;br /&gt;
     'Student'].include? current_role_name and&lt;br /&gt;
        ((%w[list].include? action_name) ? are_needed_authorizations_present?(params[:id], &amp;quot;participant&amp;quot;, &amp;quot;reader&amp;quot;, &amp;quot;submitter&amp;quot;, &amp;quot;reviewer&amp;quot;) : true)&lt;br /&gt;
  else&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator'].include? current_role_name&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index', 'list'&lt;br /&gt;
    current_user_has_student_privileges? &amp;amp;&amp;amp; list_authorization_check&lt;br /&gt;
  else&lt;br /&gt;
    current_user_has_ta_privileges?&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; action ===&lt;br /&gt;
The refactor of the &amp;lt;code&amp;gt;ReviewBidsController's index&amp;lt;/code&amp;gt; action simplifies and streamlines the codebase, ensuring the action simply handles controller activities. Previously, the controller was overloaded with responsibilities—it handled everything from locating and validating assignments to fetching review mappings and counting completed reviews. This all-in-one approach made the controller action messy. &lt;br /&gt;
&lt;br /&gt;
Now we have moved the business logic out of the controller action and into dedicated service objects. With this change, we introduced two key service objects: &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; focuses on retrieving the participant along with their assignments, while the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; takes on the task of managing review mappings and calculating review-related stats, like how many reviews have been completed. &lt;br /&gt;
&lt;br /&gt;
This clear separation of responsibilities aligns with the Single Responsibility Principle, which helps each part of our code be more focused and easier to understand. With these new service objects, the code has become more reusable. The logic for fetching assignments, retrieving review mappings, and counting reviews is now more flexible and reusable throughout the application. For example, we used methods in the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; in both the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; and the &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;, helping to avoid code duplication.&lt;br /&gt;
&lt;br /&gt;
After moving the business logic to service objects, we can write unit tests to ensure each part of the code behaves as expected and prevents bugs. We also moved complex calculations, like those for completed reviews, into the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;, freeing the controller from dealing with the implementation details and promoting a cleaner design. The article https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial provides more information on when and how to use service objects in Rails.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
  return unless current_user_id?(@participant.user_id)&lt;br /&gt;
  @assignment = @participant.assignment&lt;br /&gt;
  @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
  # Finding how many reviews have been completed&lt;br /&gt;
  @num_reviews_completed = 0&lt;br /&gt;
  @review_mappings.each do |map|&lt;br /&gt;
    @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # render view for completing reviews after review bidding has been completed&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @assignment = participant_service.assignment&lt;br /&gt;
  unless @assignment.is_a?(Assignment)&lt;br /&gt;
    flash[:error] = 'Assignment not found.'&lt;br /&gt;
    redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  @review_mappings = review_service.review_mappings&lt;br /&gt;
  @num_reviews_completed = review_service.review_counts[:completed]&lt;br /&gt;
&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Optimize &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Action functionality ===&lt;br /&gt;
In the earlier version of the set_priority action in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, the server only needed a couple of key pieces of information: an array of topic IDs (params[:topic]) and the participant ID (params[:id}). From there, it would figure out the assignment_id by using the first topic as a reference. While this worked in simple cases, it could lead to problems if any data was missing or didn’t line up correctly.&lt;br /&gt;
&lt;br /&gt;
With the new implementation, the action now expects clear and structured parameters, including participant_id, an array of topic IDs, and assignment_id. This change ensures that the server always receives complete and reliable information, preventing issues caused by missing or inconsistent data. It also removes the need for the server to perform unnecessary lookups, allowing the controller to focus on its main job: validating inputs (like making sure the topics in params[:topic] belong to the same assignment as the participant), updating the database, handling errors properly, and sending the correct response back to the view.&lt;br /&gt;
&lt;br /&gt;
While these updates have cleaned up the set_priority action, they still contain a lot of business logic, like validations and priority updates. To make the code even more maintainable, there’s room for further refactoring—perhaps moving some of this logic into a service class or model method to streamline the controller even more.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | Old Code&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    if params[:topic].nil?&lt;br /&gt;
      ReviewBid.where(participant_id: params[:id]).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      participant = AssignmentParticipant.find_by(id: params[:id])&lt;br /&gt;
      assignment_id = SignUpTopic.find(params[:topic].first).assignment.id&lt;br /&gt;
      @bids = ReviewBid.where(participant_id: params[:id])&lt;br /&gt;
      signed_up_topics = ReviewBid.where(participant_id: params[:id]).map(&amp;amp;:signuptopic_id)&lt;br /&gt;
      signed_up_topics -= params[:topic].map(&amp;amp;:to_i)&lt;br /&gt;
      signed_up_topics.each do |topic|&lt;br /&gt;
        ReviewBid.where(signuptopic_id: topic, participant_id: params[:id]).destroy_all&lt;br /&gt;
      end&lt;br /&gt;
      params[:topic].each_with_index do |topic_id, index|&lt;br /&gt;
        bid_existence = ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id])&lt;br /&gt;
        if bid_existence.empty?&lt;br /&gt;
          ReviewBid.create(priority: index + 1, signuptopic_id: topic_id, participant_id: params[:id], assignment_id: assignment_id)&lt;br /&gt;
        else&lt;br /&gt;
          ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id]).update_all(priority: index + 1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to action: 'show', assignment_id: params[:assignment_id], id: params[:id]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    participant_id = params[:participant_id].to_i&lt;br /&gt;
    selected_topic_ids = params[:topic]&amp;amp;.map(&amp;amp;:to_i) || []&lt;br /&gt;
    assignment_id = params[:assignment_id].to_i&lt;br /&gt;
&lt;br /&gt;
    unless current_user_can_modify_participant?(participant_id)&lt;br /&gt;
      flash[:error] = 'You are not authorized to modify these bids.'&lt;br /&gt;
      respond_to do |format|&lt;br /&gt;
        format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
        format.json { render json: { status: 'unauthorized' }, status: :unauthorized }&lt;br /&gt;
      end&lt;br /&gt;
      return&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    if selected_topic_ids.empty?&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      assignment = Assignment.find_by(id: assignment_id)&lt;br /&gt;
      if assignment.nil?&lt;br /&gt;
        flash[:error] = &amp;quot;Invalid assignment.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_assignment' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      unless SignUpTopic.where(id: selected_topic_ids, assignment_id: assignment_id).count == selected_topic_ids.size&lt;br /&gt;
        flash[:error] = &amp;quot;One or more selected topics are invalid.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_topics' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).where.not(signuptopic_id: selected_topic_ids).destroy_all&lt;br /&gt;
&lt;br /&gt;
      selected_topic_ids.each_with_index do |topic_id, index|&lt;br /&gt;
        bid = ReviewBid.find_or_initialize_by(signuptopic_id: topic_id, participant_id: participant_id)&lt;br /&gt;
        bid.priority = index + 1&lt;br /&gt;
        bid.assignment_id = assignment_id&lt;br /&gt;
        bid.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_to action: 'show', assignment_id: assignment_id, id: participant_id, notice: 'Review bids updated successfully.' }&lt;br /&gt;
      format.json { render json: { status: 'success' }, status: :ok }&lt;br /&gt;
    end&lt;br /&gt;
  rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
    flash[:error] = &amp;quot;Failed to update priorities: #{e.message}&amp;quot;&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
      format.json { render json: { status: 'error', message: e.message }, status: :unprocessable_entity }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Client Side Changes to Support &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Changes ====&lt;br /&gt;
After tidying up the server-side logic, we needed to refactor the client-side code to ensure it passed the correct information from the view to the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action. On the client side, we now grab the topicIDs and send them as &amp;lt;code&amp;gt;selected_topic_ids&amp;lt;/code&amp;gt;, allowing the controller to figure out which topics were selected and in what order. It’s a cleaner and more reliable way to keep everything working together! &lt;br /&gt;
&lt;br /&gt;
We are also extracting the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, making it a lot easier to send data with AJAX and keeping things flexible. AJAX is awesome because it lets us update the server without reloading the page, making the user experience smooth and responsive. When we pull the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, we can grab it along with other data, like the list of topics, and include it in the request. This way, we don’t have to rely on the URL to know which participant we’re working with, which makes the code cleaner and more adaptable.&lt;br /&gt;
&lt;br /&gt;
If we were to rely on the route for the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt;, every AJAX request would need to be tied to a specific URL structure. That’s limiting, especially if the route doesn’t naturally include the participant ID, as can be the case with collection routes versus member routes in Rails. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery -&amp;gt;&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: -&amp;gt;&lt;br /&gt;
      $.post($(this).data('update-url'), $(this).sortable('serialize'))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery(document).ready ($) -&amp;gt;&lt;br /&gt;
  sendUpdatedOrder = -&amp;gt;&lt;br /&gt;
    sortedIDs = $(this).sortable('toArray')&lt;br /&gt;
&lt;br /&gt;
    topicIDs = sortedIDs.map (id) -&amp;gt;&lt;br /&gt;
      id.replace 'topic_', ''&lt;br /&gt;
&lt;br /&gt;
    participant_id = $(this).data('participant-id')&lt;br /&gt;
&lt;br /&gt;
    assignment_id = new URL($(this).data('update-url'), window.location.origin).searchParams.get('assignment_id')&lt;br /&gt;
&lt;br /&gt;
    $.ajax&lt;br /&gt;
      type: 'POST'&lt;br /&gt;
      url: $(this).data('update-url')&lt;br /&gt;
      data:&lt;br /&gt;
        topic: topicIDs&lt;br /&gt;
        participant_id: participant_id&lt;br /&gt;
        assignment_id: assignment_id&lt;br /&gt;
      success: (response) -&amp;gt;&lt;br /&gt;
        console.log 'Selections updated successfully.'&lt;br /&gt;
      error: (xhr, status, error) -&amp;gt;&lt;br /&gt;
        alert 'An error occurred while saving your selections. Please try again.'&lt;br /&gt;
        console.error error&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: sendUpdatedOrder&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-1.-signupsheetcontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; Updates ===&lt;br /&gt;
&lt;br /&gt;
The ReviewBidsController's index method renders the review_bids_others_work action in &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt;. However, the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; does not include the controller action for this view. We need to find the old implementation of the controller action, add it back, and review and update it for any DRY violations.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # provides variables for reviewing page located at views/review_bids/others_work.html.erb&lt;br /&gt;
  def index&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
    return unless current_user_id?(@participant.user_id)&lt;br /&gt;
&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
    # Finding how many reviews have been completed&lt;br /&gt;
    @num_reviews_completed = 0&lt;br /&gt;
    @review_mappings.each do |map|&lt;br /&gt;
      @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # render view for completing reviews after review bidding has been completed&lt;br /&gt;
    render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
  end  &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-2.-reviewbidsotherswork-view-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;review_bids_others_work.html.erb&amp;lt;/code&amp;gt; View Updates ====&lt;br /&gt;
&lt;br /&gt;
The code only contains a view with this name and no associated controller action. Reviewing the code in the view, we noticed some SRP violations where there is much logic in the presentation layer. Set up helper methods in the controller to handle some of that logic or see if the model can handle some of that logic.&lt;br /&gt;
&lt;br /&gt;
Note: The view is technically part of the SignUpSheetController, but it's being rendered by the ReviewBidsController, which initially threw us off. After chatting with the professor, we decided not to change this view. He indicated that the plan is to reuse these views for both assignment bidding and review bidding.&lt;br /&gt;
&lt;br /&gt;
We would suggest building the assignment bidding and review bidding features separately first to make sure each one works on its own. Once that’s done, we can go back and clean things up by combining any shared functionality. This could include creating shared views or even introducing an abstract model to handle both types of bidding in a cleaner way. It could also mean replacing the ReviewBidsController with something more generic, like a BidsController, to keep things reusable and easier to maintain down the line.&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Rename &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; method ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; should be &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt;&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:''' Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The primary activity here is to refactor the method name &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; and fix the hardcoded URL. However, the match topic bidding algorithm no longer works since Heroku is no longer free, per the professor. The reference link below has detailed information about the web service that we can leverage to gain that understanding.&lt;br /&gt;
&lt;br /&gt;
Reference Link for Webservice Details: [https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Updated Implementation ====&lt;br /&gt;
The original requirement was to rename the run_bidding_algorithm method to assign_reviewers, but after digging into the code, we realized that renaming it didn’t make sense. The method runs the bidding algorithm to match topics, and there’s already a separate method to handle assigning review topics.&lt;br /&gt;
&lt;br /&gt;
However, we improved the logic in the assign_bidding method (which is called by the run_bidding_algorithm) by introducing a separate BiddingAlgorithmService object. This keeps the business logic out of the controller action, which makes things cleaner and more maintainable.&lt;br /&gt;
&lt;br /&gt;
==== review_bids_controller ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def run_bidding_algorithm&lt;br /&gt;
    review_bid = ReviewBid.new&lt;br /&gt;
    bidding_data = review_bid.bidding_data&lt;br /&gt;
    matched_topics = BiddingAlgorithmService.new(bidding_data).run&lt;br /&gt;
    raise ArgumentError, 'Failed to assign reviewers. Please try again later.' unless matched_topics&lt;br /&gt;
&lt;br /&gt;
    matched_topics&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== bidding_algorithm_service ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# frozen_string_literal: true&lt;br /&gt;
&lt;br /&gt;
# The `BiddingAlgorithmService` run the bid assignment algorithm&lt;br /&gt;
# Sends student IDs, topic IDs, student preferences, and timestamps to the web service&lt;br /&gt;
# The web service returns the matched assignments in the JSON response body&lt;br /&gt;
class BiddingAlgorithmService&lt;br /&gt;
  SERVICE_URL = 'http://app-csc517.herokuapp.com/match_topics'.freeze&lt;br /&gt;
&lt;br /&gt;
  # MOCK_DATA is based on the documentation detailing how the webservice behaves.&lt;br /&gt;
  # Reference: https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review#Webservice&lt;br /&gt;
  MOCK_DATA = {&lt;br /&gt;
    36239 =&amp;gt; [3970, 3972, 3975],&lt;br /&gt;
    36240 =&amp;gt; [3973, 3974, 3972],&lt;br /&gt;
    36241 =&amp;gt; [3969, 3971, 3972],&lt;br /&gt;
    36242 =&amp;gt; [3969, 3971, 3973],&lt;br /&gt;
    36243 =&amp;gt; [3969, 3970, 3971]&lt;br /&gt;
  }.freeze&lt;br /&gt;
&lt;br /&gt;
  def initialize(bidding_data)&lt;br /&gt;
    @bidding_data = bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def run&lt;br /&gt;
    return MOCK_DATA if Rails.application.config.use_mock_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
    perform_request&lt;br /&gt;
  rescue RestClient::ExceptionWithResponse, JSON::ParserError&lt;br /&gt;
    false&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def perform_request&lt;br /&gt;
    response = RestClient.post(&lt;br /&gt;
      SERVICE_URL,&lt;br /&gt;
      @bidding_data.to_json,&lt;br /&gt;
      content_type: :json,&lt;br /&gt;
      accept: :json&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    validate_response(response)&lt;br /&gt;
    JSON.parse(response.body)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def validate_response(response)&lt;br /&gt;
    raise 'Invalid response format' unless response.headers[:content_type] &amp;amp;&amp;amp; response.headers[:content_type].include?('application/json')&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Resolve Functionality Issues for Non Bidders ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Currently it doesn't work if some student does not bid. In this case, algorithm needs to be fixed to ignore anyone who didn’t bid, and let them choose from what’s left over.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to do several things within this controller. We will set a default state for non-bidders that we can point to to give them options to select topics to review from the leftovers. We will consolidate &amp;lt;code&amp;gt;@sign_up_topics&amp;lt;/code&amp;gt; to resolve the DRY violation. Lastly, we will remove business logic from the show method and push it to a new helper to conform to the SRP.&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # Additional method&lt;br /&gt;
&lt;br /&gt;
  # Provides variables for review bidding page&lt;br /&gt;
  def show&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id].to_i)&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @sign_up_topics = SignUpTopic.where(assignment_id: @assignment.id, private_to: nil)&lt;br /&gt;
    my_topic = SignedUpTeam.topic_id(@participant.parent_id, @participant.user_id)&lt;br /&gt;
    @sign_up_topics -= SignUpTopic.where(assignment_id: @assignment.id, id: my_topic)&lt;br /&gt;
    @num_participants = AssignmentParticipant.where(parent_id: @assignment.id).count&lt;br /&gt;
    @selected_topics = nil # This is used to list the topics assigned to review (i.e., select == assigned)&lt;br /&gt;
    @bids = ReviewBid.where(participant_id: @participant, assignment_id: @assignment.id)&lt;br /&gt;
    signed_up_topics = []&lt;br /&gt;
    &lt;br /&gt;
    @bids.each do |bid|&lt;br /&gt;
      sign_up_topic = SignUpTopic.find_by(id: bid.signuptopic_id)&lt;br /&gt;
      signed_up_topics &amp;lt;&amp;lt; sign_up_topic if sign_up_topic&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    signed_up_topics &amp;amp;= @sign_up_topics&lt;br /&gt;
    @sign_up_topics -= signed_up_topics&lt;br /&gt;
    @bids = signed_up_topics&lt;br /&gt;
    @num_of_topics = @sign_up_topics.size&lt;br /&gt;
    @assigned_review_maps = []&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: @assignment.id, reviewer_id: @participant.id).each do |review_map|&lt;br /&gt;
      @assigned_review_maps &amp;lt;&amp;lt; review_map&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Explicitly render view since it's in the sign up sheet views&lt;br /&gt;
    render 'sign_up_sheet/review_bids_show'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Additional methods&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-2.-reviewbidshelper-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
 # Assigns bidding topics to reviewers&lt;br /&gt;
  def assign_bidding&lt;br /&gt;
      assignment = validate_assignment(params[:assignment_id])&lt;br /&gt;
&lt;br /&gt;
      reviewers = validate_reviewers(assignment.id)&lt;br /&gt;
      reviewer_ids = reviewers.map(&amp;amp;:id)&lt;br /&gt;
&lt;br /&gt;
      matched_topics = run_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
      if matched_topics.blank?&lt;br /&gt;
        flash[:alert] = 'Topic or assignment is missing'&lt;br /&gt;
        redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      leftover_topics = find_leftover_topics(assignment.id, matched_topics)&lt;br /&gt;
      assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.new.assign_review_topics(matched_topics)&lt;br /&gt;
      assignment.update!(can_choose_topic_to_review: false)&lt;br /&gt;
&lt;br /&gt;
      flash[:notice] = 'Reviewers were successfully assigned to topics.'&lt;br /&gt;
      redirect_back fallback_location: root_path&lt;br /&gt;
    rescue ArgumentError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ArgumentError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: e.message&lt;br /&gt;
    rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::RecordInvalid: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue ActiveRecord::ActiveRecordError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::ActiveRecordError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue StandardError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;StandardError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers. Please try again later.'&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  #service methods here ...&lt;br /&gt;
&lt;br /&gt;
  def find_leftover_topics(assignment_id, matched_topics)&lt;br /&gt;
    all_topic_ids = SignUpTopic.where(assignment_id: assignment_id).pluck(:id)&lt;br /&gt;
    assigned_topic_ids = matched_topics.map { |match| match[:topic_id] }&lt;br /&gt;
&lt;br /&gt;
    # Calculate leftover topics&lt;br /&gt;
    all_topic_ids - assigned_topic_ids&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
    return if leftover_topics.blank?&lt;br /&gt;
&lt;br /&gt;
    # Find non-bidders by excluding already matched reviewers&lt;br /&gt;
    non_bidders = reviewer_ids - matched_topics.keys&lt;br /&gt;
&lt;br /&gt;
    # Assign leftover topics to non-bidders in a round-robin fashion&lt;br /&gt;
    non_bidders.each_with_index do |reviewer_id, index|&lt;br /&gt;
      topic_id = leftover_topics[index % leftover_topics.length]&lt;br /&gt;
      ReviewBid.create(priority: 1, signuptopic_id: topic_id, participant_id: reviewer_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirm Functionality for Single-person Teams ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Make sure your code works on individual assignments, i.e., assignments where participants sign up for topics instead of teams. In this case, a team is created for each participant when they sign up. So the code should work for assignments to which either individuals or teams submit.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to make an update to focus on topic rather than team or have it where teams are updated based on topic id.We will probably utilize sign up topic or a similar object to accomplish this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-5-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bid Status Messaging ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| To make the functionality more intuitive, include a message to say how many students are eligible to submit bids, how many have submitted their bids, when the deadline for submitting bids is.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
The logic for these messages will go into the helper file. It is effectively the same reasoning as our migration of other business/functionality code from the controller to the helper to conform to the SRP. It will go hand in hand with the SignUpTopic portion of the code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-6-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Why are the methods in review_bid.rb class methods? Can we change them to instance methods or move it to helpers?&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-1.-reviewbid-model-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model Updates ====&lt;br /&gt;
Since the current class methods are tied to &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model instances, we will change them to instance methods. There is no need to move them to helpers.&lt;br /&gt;
&lt;br /&gt;
===== Review Bid Model Flow Before Refactoring =====&lt;br /&gt;
This sequence diagram describes how the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; interacts with the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model in assigning review topics to reviewers according to their bids. Once the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; action is initiated by the user, the controller gathers the IDs of reviewers and calls &amp;lt;code&amp;gt;ReviewBid.bidding_data&amp;lt;/code&amp;gt; to gather bidding information. That is sent out to an external bidding algorithm, and the matched topics that come back are used by &amp;lt;code&amp;gt;ReviewBid.assign_review_topics&amp;lt;/code&amp;gt; to create &amp;lt;code&amp;gt;ReviewResponseMap&amp;lt;/code&amp;gt; entries, assigning topics to reviewers for the assignment.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
  belongs_to :assignment, class_name: 'Assignment'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  def self.bidding_data(assignment_id, reviewer_ids)&lt;br /&gt;
    # create basic hash and set basic hash data&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'users' =&amp;gt; {}, 'max_accepted_proposals' =&amp;gt; [] }&lt;br /&gt;
    bidding_data['tid'] = SignUpTopic.where(assignment_id: assignment_id).ids&lt;br /&gt;
    bidding_data['max_accepted_proposals'] = Assignment.where(id: assignment_id).pluck(:num_reviews_allowed).first&lt;br /&gt;
&lt;br /&gt;
    # loop through reviewer_ids to get reviewer specific bidding data&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      bidding_data['users'][reviewer_id] = reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # assigns topics to reviews as matched by the webservice algorithm&lt;br /&gt;
  def self.assign_review_topics(assignment_id, reviewer_ids, matched_topics, _min_num_reviews = 2)&lt;br /&gt;
    # if review response map already created, delete it&lt;br /&gt;
    if ReviewResponseMap.where(reviewed_object_id: assignment_id)&lt;br /&gt;
      ReviewResponseMap.where(reviewed_object_id: assignment_id).destroy_all&lt;br /&gt;
    end&lt;br /&gt;
    # loop through reviewer_ids to assign reviews to each reviewer&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      topics_to_assign = matched_topics[reviewer_id.to_s]&lt;br /&gt;
      topics_to_assign.each do |topic|&lt;br /&gt;
        assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method to assign a single topic to a reviewer&lt;br /&gt;
  def self.assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic).pluck(:team_id).first&lt;br /&gt;
    team_to_review.nil? ? [] : ReviewResponseMap.create(reviewed_object_id: assignment_id, reviewer_id: reviewer_id, reviewee_id: team_to_review, type: 'ReviewResponseMap')&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method for getting individual reviewer_ids bidding data&lt;br /&gt;
  # returns user's bidding data hash&lt;br /&gt;
  def self.reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    reviewer_user_id = AssignmentParticipant.find(reviewer_id).user_id&lt;br /&gt;
    self_topic = SignedUpTeam.topic_id(assignment_id, reviewer_user_id)&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'otid' =&amp;gt; self_topic, 'priority' =&amp;gt; [], 'time' =&amp;gt; [] }&lt;br /&gt;
    bids = ReviewBid.where(participant_id: reviewer_id)&lt;br /&gt;
&lt;br /&gt;
    # loop through each bid for a topic to get specific data&lt;br /&gt;
    bids.each do |bid|&lt;br /&gt;
      bidding_data['tid'] &amp;lt;&amp;lt; bid.signuptopic_id&lt;br /&gt;
      bidding_data['priority'] &amp;lt;&amp;lt; bid.priority&lt;br /&gt;
      bidding_data['time'] &amp;lt;&amp;lt; bid.updated_at&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  # Instance method to get bidding data for this review bid&lt;br /&gt;
  def bidding_data&lt;br /&gt;
    {&lt;br /&gt;
      'tid' =&amp;gt; topic_ids,&lt;br /&gt;
      'otid' =&amp;gt; self_topic,&lt;br /&gt;
      'priority' =&amp;gt; priorities,&lt;br /&gt;
      'time' =&amp;gt; updated_times&lt;br /&gt;
    }&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # bid object to limit db touches for similar data acquisitions&lt;br /&gt;
  def bids&lt;br /&gt;
    @bids ||= ReviewBid.where(participant_id: participant_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def topic_ids&lt;br /&gt;
    bids.pluck(:signuptopic_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def priorities&lt;br /&gt;
    bids.pluck(:priority)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def updated_times&lt;br /&gt;
    bids.pluck(:updated_at)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self_topic&lt;br /&gt;
    return nil unless topic &amp;amp;&amp;amp; topic.assignment_id&lt;br /&gt;
&lt;br /&gt;
    SignedUpTeam.topic_id(topic.assignment_id, participant.user_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign topics to reviews&lt;br /&gt;
  def assign_review_topics(matched_topics)&lt;br /&gt;
    validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: topic.assignment_id).destroy_all&lt;br /&gt;
&lt;br /&gt;
    matched_topics.each do |reviewer_id, topics|&lt;br /&gt;
      Array(topics).each { |topic_id| assign_topic_to_reviewer(reviewer_id, topic_id) }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign a single topic to a reviewer&lt;br /&gt;
  def assign_topic_to_reviewer(reviewer_id, topic_id)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic_id).pluck(:team_id).first&lt;br /&gt;
    return if team_to_review.nil?&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.create(&lt;br /&gt;
      reviewed_object_id: topic.assignment_id,&lt;br /&gt;
      reviewer_id: reviewer_id,&lt;br /&gt;
      reviewee_id: team_to_review,&lt;br /&gt;
      type: 'ReviewResponseMap'&lt;br /&gt;
    )&lt;br /&gt;
    # error handling&lt;br /&gt;
  rescue StandardError =&amp;gt; e&lt;br /&gt;
    Rails.logger.error(&amp;quot;Failed to assign topic #{topic_id} to reviewer #{reviewer_id}: #{e.message}&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
    raise ArgumentError, 'Topic or assignment is missing' if topic.nil? || topic.assignment_id.nil?&lt;br /&gt;
    raise ArgumentError, 'Matched topics must be a Hash' unless matched_topics.is_a?(Hash)&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
Once we change them to instance methods, we’ll need to modify their use in the code to instantiate the object first.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;Placeholder for implementation screenshots (before and after)&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-3.-schema.db-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;Schema.db&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We also need to remove the foreign key reference to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model since the &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model already has access to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;, and that is the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; we should be referencing. This will also involve modifying the database to remove the foreign key reference to the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Steps required:&lt;br /&gt;
&lt;br /&gt;
# Review the records in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table in the database to see if there are any records and if those records have values for &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;&lt;br /&gt;
# Create a migration file using the command: &amp;lt;code&amp;gt;rails generate migration RemoveAssignmentFromReviewBids&amp;lt;/code&amp;gt;&lt;br /&gt;
# In the generated file, indicate the change that needs to be made using the change method and migration columns &amp;lt;code&amp;gt;remove_foreign_key&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;remove_column&amp;lt;/code&amp;gt;. Migration should be straightforward since there is no cascading behavior (i.e., &amp;lt;code&amp;gt;dependent:destroy&amp;lt;/code&amp;gt;) to worry about.&lt;br /&gt;
# Once comfortable with the updates to the generated file, run the migration using the &amp;lt;code&amp;gt;rails db:migrate&amp;lt;/code&amp;gt; command.&lt;br /&gt;
# Run a query in the DB to ensure no orphaned records are in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table after the updates.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;Placeholder for implementation screenshots (before and after)&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-4.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implement Edge Case Testing ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| In the previous implementation wiki, there are edge cases which are not exhaustively tested. Should test those edge cases thoroughly and add more edge case testing - link&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span class=&amp;quot;mark&amp;quot;&amp;gt;TODO&amp;lt;/span&amp;gt;:We just got initial test results back. We still need to figure out why tests are failing and what may be slipping through the cracks.[[File:Test expertiza.png|411x459px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-8-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design Patterns and Principles ===&lt;br /&gt;
Below are the design patterns and principles we plan to use to address the problem statements outlined in the project requirements:&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
** Controller Responsibility&lt;br /&gt;
*** To address the issue of authentication logic residing in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, we will refactor the code to ensure the controller solely handles request and response flows.&lt;br /&gt;
*** Authentication logic will be moved to dedicated methods in &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt;, making the codebase easier to maintain and test.&lt;br /&gt;
** View Simplification&lt;br /&gt;
*** Currently, the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; view contains excessive business logic, violating SRP.&lt;br /&gt;
*** We will move this logic into helper methods or models, ensuring each component has a single responsibility and improving code readability.&lt;br /&gt;
&lt;br /&gt;
* Separation of Concerns (SoC)&lt;br /&gt;
** By relocating authorization logic out of the controller, we separate authentication from request handling.&lt;br /&gt;
** Adhering to SoC enhances readability and maintainability by allowing each part of the application to focus on its specific role.&lt;br /&gt;
* Don't Repeat Yourself (DRY)&lt;br /&gt;
** We will inspect the existing implementation for code duplication, particularly in the controller actions.&lt;br /&gt;
** Any repetitive code will be refactored or removed to reduce redundancy and improve clarity.&lt;br /&gt;
&lt;br /&gt;
* Model-View-Controller (MVC) Adherence&lt;br /&gt;
** Moving business logic from the view aligns with MVC best practices.&lt;br /&gt;
** By ensuring the view only presents data without processing it, we maintain clear boundaries between the model, view, and controller layers.&lt;br /&gt;
&lt;br /&gt;
* Dependency Inversion Principle (DIP)&lt;br /&gt;
** We plan to update class methods to instance methods, allowing the controller to depend on abstractions rather than concrete implementations.&lt;br /&gt;
** This change leverages Ruby's duck typing, providing flexibility and making the code easier to maintain and test.&lt;br /&gt;
&lt;br /&gt;
* Factory Pattern&lt;br /&gt;
** We will implement a factory pattern for the common processing of &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpBid&amp;lt;/code&amp;gt;, encapsulating object creation and promoting code reuse.&lt;br /&gt;
&lt;br /&gt;
* Law of Demeter (Principle of Least Knowledge)&lt;br /&gt;
** We'll delegate interactions appropriately to prevent tightly coupled code, especially when dealing with topics and teams.&lt;br /&gt;
** Clear and intentional naming during refactoring will enhance code clarity and maintainability.&lt;br /&gt;
&lt;br /&gt;
* Adapter Pattern&lt;br /&gt;
** To add messaging to bid states for users, we'll use the adapter pattern to allow different components to work together without modifying their existing code.&lt;br /&gt;
&lt;br /&gt;
== Files Modified/Added ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-1.-controllers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
review_bids_controller.rb&lt;br /&gt;
&lt;br /&gt;
signup_sheet_controller.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-2.-models&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
review_bid.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-3.-helpers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
&lt;br /&gt;
authorization_helper.rb&lt;br /&gt;
&lt;br /&gt;
Review_bids_helper.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;viii.-test-plan&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
The Excel spreadsheet below details our test plan for testing the code we changed or created to ensure code coverage. The process involved reviewing the code and determining all the possible scenarios that needed to be covered based on what the method was doing, then creating test cases accordingly.&lt;br /&gt;
&lt;br /&gt;
* [https://docs.google.com/spreadsheets/d/1iHzOdSwlTtxOtf5VsNdwOaCP8NzYMmiJ/edit?usp=drive_link Open Test Matrix Spreadsheet]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/review_bids_controller_spec.rb ReviewBidsController updated test cases]&lt;br /&gt;
* [https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/sign_up_sheet_controller_spec.rb SignUpSheetController updated test cases]&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-1.-mentor&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Ed Gehringer&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-2.-members&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Members ===&lt;br /&gt;
&lt;br /&gt;
* Gavin Teague&lt;br /&gt;
* Janice Uwujaren&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xi.-links&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
Previous Wiki: [https://wiki.expertiza.ncsu.edu/index.php/CSC/ECE_517_Fall_2021_-_E2151._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;Link&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xii.-references&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/document/d/1LHVacylQmB15I6VcYUA5nuC7GRRU9szHzZhjlPcE-PA/edit?tab=t.0#heading=h.u0oycoj6e0ev &amp;lt;u&amp;gt;Project Instructions&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;appendix-a-uml-class-diagram-plantuml-code-for-review-bids&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Juwujar</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160952</id>
		<title>CSC/ECE 517 Fall 2024 - E2485. Allow reviewers to bid on what to review</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160952"/>
		<updated>2024-12-10T15:16:33Z</updated>

		<summary type="html">&lt;p&gt;Juwujar: /* Test Plan */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
This project will fix the defects and improve the functionality in Expertiza of the Reviewer Bidding feature, a process where reviewers can bid for assignments they want to review. It will make the functionality smoother, more reliable, and align with Expertiza's collaborative learning objectives.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iii.-problem-statements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Problem Statements ==&lt;br /&gt;
&lt;br /&gt;
Assigning reviews to users on Expertiza involves a complex bidding algorithm that requires optimization before production deployment. Below are key areas we plan to address as per the project requirements:&lt;br /&gt;
* Code Refactoring and Best Practices&lt;br /&gt;
** '''Utilize Authentication Utilities''': Modify the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; function in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to use existing authentication utilities, enhancing code reusability and adhering to the Single Responsibility Principle (SRP) and Separation of Concerns (SoC).&lt;br /&gt;
** '''Eliminate Code Duplication''': Refactor &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; to comply with the Don't Repeat Yourself (DRY) principle.&lt;br /&gt;
** '''Rename Functions for Clarity''': Change &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; to better reflect its purpose.&lt;br /&gt;
** '''Refactor Methods in '''&amp;lt;code&amp;gt;review_bid.rb&amp;lt;/code&amp;gt;''':''': Convert class methods to instance methods or relocate them to helper classes for improved code organization.&lt;br /&gt;
* Functionality Enhancements&lt;br /&gt;
** '''Handle Missing Bids Gracefully''': Adjust the bidding algorithm to accommodate students who do not bid, allowing them to select from the remaining options.&lt;br /&gt;
** '''Support Both Individual and Team Assignments''': Ensure the code functions correctly for participant and team-based assignments when signing up for topics.&lt;br /&gt;
* User Interface Improvements&lt;br /&gt;
** '''Increase Transparency in the Bidding Process''': Display messages indicating how many students are eligible to bid, how many have submitted bids, and the deadline.&lt;br /&gt;
** '''Accurate Bid Status Indicators''': Ensure topic colors change correctly based on the number of outstanding bids, providing clear visual feedback to users.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Exhaustive Edge Case Testing''': Conduct thorough testing for edge cases not previously covered, adding additional test cases as needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iv.-design-goals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Design Goals ==&lt;br /&gt;
&lt;br /&gt;
While addressing the existing defects and enhancing the Reviewer Bidding feature, we plan to adhere to the following design guidelines:&lt;br /&gt;
&lt;br /&gt;
* Code Quality and Maintainability&lt;br /&gt;
** '''Improve Code Structure: '''Implement refactoring strategies to enhance code readability and maintainability, ensuring adherence to best practices like the DRY principle.&lt;br /&gt;
** '''Standardize Authentication Logic: '''Utilize a centralized authentication utility to promote consistency and reusability across the application.&lt;br /&gt;
* System Stability and Flexibility&lt;br /&gt;
** '''Enhance Algorithm Robustness: '''Modify the bidding algorithm to handle scenarios where students do not bid, ensuring the system remains stable under all conditions.&lt;br /&gt;
** '''Ensure Assignment Versatility: '''Design the system to seamlessly support both individual and team assignments without additional configuration.&lt;br /&gt;
* User Experience Enhancements&lt;br /&gt;
** '''Increase Bidding Transparency: '''Develop user interface elements that clearly display bidding status, deadlines, and participation metrics.&lt;br /&gt;
** '''Implement Dynamic Visual Feedback: '''Use real-time visual cues, such as color changes, to inform users about the bidding status instantly.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Develop Comprehensive Test Suites: '''Create extensive automated tests covering edge cases and critical functionalities to guarantee reliable system behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;v.-uml-diagram&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== UML Diagram ==&lt;br /&gt;
&lt;br /&gt;
Below is a visual representation of the models associated with the review bids functionality, depicting the data structures and their relationships.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml class diagram2.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi.-implementation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
'''ReviewBid: '''The &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; manages the bidding process, where participants indicate their preferences for reviewing different topics or assignments. This helps distribute review tasks fairly based on participants' interests.&lt;br /&gt;
&lt;br /&gt;
'''Assignment: '''The &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model represents team or individual assignments in Expertiza. It stores all the essential information required to keep the assignment process organized, like managing participants, setting due dates, forming teams, and overseeing the review process.&lt;br /&gt;
&lt;br /&gt;
'''SignUpTopic: '''The &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt; model identifies specific topics or areas within an assignment for which participants can sign up. It manages the availability of these topics, tracks bids, coordinates team sign-ups, and assigns topics based on both bids and participant availability. &lt;br /&gt;
&lt;br /&gt;
'''Participant: '''The &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model is about the individual users who participate in an assignment. This includes students, instructors, teaching assistants, or other relevant roles. This model is responsible for managing key user information, monitoring interactions with the assignment, keeping track of team memberships, and recording things like badges and review grades.&lt;br /&gt;
&lt;br /&gt;
== Implementation ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1.-decoupling-authorization-logic-from-reviewbidscontroller&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Decoupling Authorization Logic from &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| '''&amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; needs to use the authentication utilities'''&lt;br /&gt;
&lt;br /&gt;
The function &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; should use the available authentication utilities to support the Single Responsibility Principle and Separation of Concerns and to support code reusability.&lt;br /&gt;
&lt;br /&gt;
| '''Authentication Consistency:''' Remove authentication logic in the controller and call logic in the authentication utility class instead to ensure consistent use of the same logic across the application, foster reusability, and eliminate DRY.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
==== Authorization Flow Before Refactoring ====&lt;br /&gt;
The sequence diagram depicts the authorization process within the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;. When a user invokes an action, the controller checks if the action is allowed by verifying the user's role through the &amp;lt;code&amp;gt;current_role_name()&amp;lt;/code&amp;gt; method in the &amp;lt;code&amp;gt;ApplicationController&amp;lt;/code&amp;gt;. Depending on the action and the user's role, the controller either authorizes the user to proceed or denies access, ultimately executing the action logic if authorized and responding to the user.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 1.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We simplified the &amp;lt;code&amp;gt;action_allowed?&amp;lt;/code&amp;gt; method by decoupling authorization logic from the controller to use helper methods in the &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt; module. The &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; should not handle authentication, which is a violation of SRP. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;vertical-align:top;&amp;quot; | Original Code !! style=&amp;quot;vertical-align:top;&amp;quot; | Refactored Code&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index'&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator',&lt;br /&gt;
     'Student'].include? current_role_name and&lt;br /&gt;
        ((%w[list].include? action_name) ? are_needed_authorizations_present?(params[:id], &amp;quot;participant&amp;quot;, &amp;quot;reader&amp;quot;, &amp;quot;submitter&amp;quot;, &amp;quot;reviewer&amp;quot;) : true)&lt;br /&gt;
  else&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator'].include? current_role_name&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index', 'list'&lt;br /&gt;
    current_user_has_student_privileges? &amp;amp;&amp;amp; list_authorization_check&lt;br /&gt;
  else&lt;br /&gt;
    current_user_has_ta_privileges?&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; action ===&lt;br /&gt;
The refactor of the &amp;lt;code&amp;gt;ReviewBidsController's index&amp;lt;/code&amp;gt; action simplifies and streamlines the codebase, ensuring the action simply handles controller activities. Previously, the controller was overloaded with responsibilities—it handled everything from locating and validating assignments to fetching review mappings and counting completed reviews. This all-in-one approach made the controller action messy. &lt;br /&gt;
&lt;br /&gt;
Now we have moved the business logic out of the controller action and into dedicated service objects. With this change, we introduced two key service objects: &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; focuses on retrieving the participant along with their assignments, while the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; takes on the task of managing review mappings and calculating review-related stats, like how many reviews have been completed. &lt;br /&gt;
&lt;br /&gt;
This clear separation of responsibilities aligns with the Single Responsibility Principle, which helps each part of our code be more focused and easier to understand. With these new service objects, the code has become more reusable. The logic for fetching assignments, retrieving review mappings, and counting reviews is now more flexible and reusable throughout the application. For example, we used methods in the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; in both the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; and the &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;, helping to avoid code duplication.&lt;br /&gt;
&lt;br /&gt;
After moving the business logic to service objects, we can write unit tests to ensure each part of the code behaves as expected and prevents bugs. We also moved complex calculations, like those for completed reviews, into the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;, freeing the controller from dealing with the implementation details and promoting a cleaner design. The article https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial provides more information on when and how to use service objects in Rails.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
  return unless current_user_id?(@participant.user_id)&lt;br /&gt;
  @assignment = @participant.assignment&lt;br /&gt;
  @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
  # Finding how many reviews have been completed&lt;br /&gt;
  @num_reviews_completed = 0&lt;br /&gt;
  @review_mappings.each do |map|&lt;br /&gt;
    @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # render view for completing reviews after review bidding has been completed&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @assignment = participant_service.assignment&lt;br /&gt;
  unless @assignment.is_a?(Assignment)&lt;br /&gt;
    flash[:error] = 'Assignment not found.'&lt;br /&gt;
    redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  @review_mappings = review_service.review_mappings&lt;br /&gt;
  @num_reviews_completed = review_service.review_counts[:completed]&lt;br /&gt;
&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Optimize &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Action functionality ===&lt;br /&gt;
In the earlier version of the set_priority action in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, the server only needed a couple of key pieces of information: an array of topic IDs (params[:topic]) and the participant ID (params[:id}). From there, it would figure out the assignment_id by using the first topic as a reference. While this worked in simple cases, it could lead to problems if any data was missing or didn’t line up correctly.&lt;br /&gt;
&lt;br /&gt;
With the new implementation, the action now expects clear and structured parameters, including participant_id, an array of topic IDs, and assignment_id. This change ensures that the server always receives complete and reliable information, preventing issues caused by missing or inconsistent data. It also removes the need for the server to perform unnecessary lookups, allowing the controller to focus on its main job: validating inputs (like making sure the topics in params[:topic] belong to the same assignment as the participant), updating the database, handling errors properly, and sending the correct response back to the view.&lt;br /&gt;
&lt;br /&gt;
While these updates have cleaned up the set_priority action, they still contain a lot of business logic, like validations and priority updates. To make the code even more maintainable, there’s room for further refactoring—perhaps moving some of this logic into a service class or model method to streamline the controller even more.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | Old Code&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    if params[:topic].nil?&lt;br /&gt;
      ReviewBid.where(participant_id: params[:id]).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      participant = AssignmentParticipant.find_by(id: params[:id])&lt;br /&gt;
      assignment_id = SignUpTopic.find(params[:topic].first).assignment.id&lt;br /&gt;
      @bids = ReviewBid.where(participant_id: params[:id])&lt;br /&gt;
      signed_up_topics = ReviewBid.where(participant_id: params[:id]).map(&amp;amp;:signuptopic_id)&lt;br /&gt;
      signed_up_topics -= params[:topic].map(&amp;amp;:to_i)&lt;br /&gt;
      signed_up_topics.each do |topic|&lt;br /&gt;
        ReviewBid.where(signuptopic_id: topic, participant_id: params[:id]).destroy_all&lt;br /&gt;
      end&lt;br /&gt;
      params[:topic].each_with_index do |topic_id, index|&lt;br /&gt;
        bid_existence = ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id])&lt;br /&gt;
        if bid_existence.empty?&lt;br /&gt;
          ReviewBid.create(priority: index + 1, signuptopic_id: topic_id, participant_id: params[:id], assignment_id: assignment_id)&lt;br /&gt;
        else&lt;br /&gt;
          ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id]).update_all(priority: index + 1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to action: 'show', assignment_id: params[:assignment_id], id: params[:id]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    participant_id = params[:participant_id].to_i&lt;br /&gt;
    selected_topic_ids = params[:topic]&amp;amp;.map(&amp;amp;:to_i) || []&lt;br /&gt;
    assignment_id = params[:assignment_id].to_i&lt;br /&gt;
&lt;br /&gt;
    unless current_user_can_modify_participant?(participant_id)&lt;br /&gt;
      flash[:error] = 'You are not authorized to modify these bids.'&lt;br /&gt;
      respond_to do |format|&lt;br /&gt;
        format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
        format.json { render json: { status: 'unauthorized' }, status: :unauthorized }&lt;br /&gt;
      end&lt;br /&gt;
      return&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    if selected_topic_ids.empty?&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      assignment = Assignment.find_by(id: assignment_id)&lt;br /&gt;
      if assignment.nil?&lt;br /&gt;
        flash[:error] = &amp;quot;Invalid assignment.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_assignment' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      unless SignUpTopic.where(id: selected_topic_ids, assignment_id: assignment_id).count == selected_topic_ids.size&lt;br /&gt;
        flash[:error] = &amp;quot;One or more selected topics are invalid.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_topics' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).where.not(signuptopic_id: selected_topic_ids).destroy_all&lt;br /&gt;
&lt;br /&gt;
      selected_topic_ids.each_with_index do |topic_id, index|&lt;br /&gt;
        bid = ReviewBid.find_or_initialize_by(signuptopic_id: topic_id, participant_id: participant_id)&lt;br /&gt;
        bid.priority = index + 1&lt;br /&gt;
        bid.assignment_id = assignment_id&lt;br /&gt;
        bid.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_to action: 'show', assignment_id: assignment_id, id: participant_id, notice: 'Review bids updated successfully.' }&lt;br /&gt;
      format.json { render json: { status: 'success' }, status: :ok }&lt;br /&gt;
    end&lt;br /&gt;
  rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
    flash[:error] = &amp;quot;Failed to update priorities: #{e.message}&amp;quot;&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
      format.json { render json: { status: 'error', message: e.message }, status: :unprocessable_entity }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Client Side Changes to Support &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Changes ====&lt;br /&gt;
After tidying up the server-side logic, we needed to refactor the client-side code to ensure it passed the correct information from the view to the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action. On the client side, we now grab the topicIDs and send them as &amp;lt;code&amp;gt;selected_topic_ids&amp;lt;/code&amp;gt;, allowing the controller to figure out which topics were selected and in what order. It’s a cleaner and more reliable way to keep everything working together! &lt;br /&gt;
&lt;br /&gt;
We are also extracting the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, making it a lot easier to send data with AJAX and keeping things flexible. AJAX is awesome because it lets us update the server without reloading the page, making the user experience smooth and responsive. When we pull the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, we can grab it along with other data, like the list of topics, and include it in the request. This way, we don’t have to rely on the URL to know which participant we’re working with, which makes the code cleaner and more adaptable.&lt;br /&gt;
&lt;br /&gt;
If we were to rely on the route for the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt;, every AJAX request would need to be tied to a specific URL structure. That’s limiting, especially if the route doesn’t naturally include the participant ID, as can be the case with collection routes versus member routes in Rails. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery -&amp;gt;&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: -&amp;gt;&lt;br /&gt;
      $.post($(this).data('update-url'), $(this).sortable('serialize'))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery(document).ready ($) -&amp;gt;&lt;br /&gt;
  sendUpdatedOrder = -&amp;gt;&lt;br /&gt;
    sortedIDs = $(this).sortable('toArray')&lt;br /&gt;
&lt;br /&gt;
    topicIDs = sortedIDs.map (id) -&amp;gt;&lt;br /&gt;
      id.replace 'topic_', ''&lt;br /&gt;
&lt;br /&gt;
    participant_id = $(this).data('participant-id')&lt;br /&gt;
&lt;br /&gt;
    assignment_id = new URL($(this).data('update-url'), window.location.origin).searchParams.get('assignment_id')&lt;br /&gt;
&lt;br /&gt;
    $.ajax&lt;br /&gt;
      type: 'POST'&lt;br /&gt;
      url: $(this).data('update-url')&lt;br /&gt;
      data:&lt;br /&gt;
        topic: topicIDs&lt;br /&gt;
        participant_id: participant_id&lt;br /&gt;
        assignment_id: assignment_id&lt;br /&gt;
      success: (response) -&amp;gt;&lt;br /&gt;
        console.log 'Selections updated successfully.'&lt;br /&gt;
      error: (xhr, status, error) -&amp;gt;&lt;br /&gt;
        alert 'An error occurred while saving your selections. Please try again.'&lt;br /&gt;
        console.error error&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: sendUpdatedOrder&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-1.-signupsheetcontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; Updates ===&lt;br /&gt;
&lt;br /&gt;
The ReviewBidsController's index method renders the review_bids_others_work action in &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt;. However, the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; does not include the controller action for this view. We need to find the old implementation of the controller action, add it back, and review and update it for any DRY violations.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # provides variables for reviewing page located at views/review_bids/others_work.html.erb&lt;br /&gt;
  def index&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
    return unless current_user_id?(@participant.user_id)&lt;br /&gt;
&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
    # Finding how many reviews have been completed&lt;br /&gt;
    @num_reviews_completed = 0&lt;br /&gt;
    @review_mappings.each do |map|&lt;br /&gt;
      @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # render view for completing reviews after review bidding has been completed&lt;br /&gt;
    render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
  end  &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-2.-reviewbidsotherswork-view-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;review_bids_others_work.html.erb&amp;lt;/code&amp;gt; View Updates ====&lt;br /&gt;
&lt;br /&gt;
The code only contains a view with this name and no associated controller action. Reviewing the code in the view, we noticed some SRP violations where there is much logic in the presentation layer. Set up helper methods in the controller to handle some of that logic or see if the model can handle some of that logic.&lt;br /&gt;
&lt;br /&gt;
Note: The view is technically part of the SignUpSheetController, but it's being rendered by the ReviewBidsController, which initially threw us off. After chatting with the professor, we decided not to change this view. He indicated that the plan is to reuse these views for both assignment bidding and review bidding.&lt;br /&gt;
&lt;br /&gt;
We would suggest building the assignment bidding and review bidding features separately first to make sure each one works on its own. Once that’s done, we can go back and clean things up by combining any shared functionality. This could include creating shared views or even introducing an abstract model to handle both types of bidding in a cleaner way. It could also mean replacing the ReviewBidsController with something more generic, like a BidsController, to keep things reusable and easier to maintain down the line.&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Rename &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; method ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; should be &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt;&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:''' Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The primary activity here is to refactor the method name &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; and fix the hardcoded URL. However, the match topic bidding algorithm no longer works since Heroku is no longer free, per the professor. The reference link below has detailed information about the web service that we can leverage to gain that understanding.&lt;br /&gt;
&lt;br /&gt;
Reference Link for Webservice Details: [https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Updated Implementation ====&lt;br /&gt;
The original requirement was to rename the run_bidding_algorithm method to assign_reviewers, but after digging into the code, we realized that renaming it didn’t make sense. The method runs the bidding algorithm to match topics, and there’s already a separate method to handle assigning review topics.&lt;br /&gt;
&lt;br /&gt;
However, we improved the logic in the assign_bidding method (which is called by the run_bidding_algorithm) by introducing a separate BiddingAlgorithmService object. This keeps the business logic out of the controller action, which makes things cleaner and more maintainable.&lt;br /&gt;
&lt;br /&gt;
==== review_bids_controller ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def run_bidding_algorithm&lt;br /&gt;
    review_bid = ReviewBid.new&lt;br /&gt;
    bidding_data = review_bid.bidding_data&lt;br /&gt;
    matched_topics = BiddingAlgorithmService.new(bidding_data).run&lt;br /&gt;
    raise ArgumentError, 'Failed to assign reviewers. Please try again later.' unless matched_topics&lt;br /&gt;
&lt;br /&gt;
    matched_topics&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== bidding_algorithm_service ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# frozen_string_literal: true&lt;br /&gt;
&lt;br /&gt;
# The `BiddingAlgorithmService` run the bid assignment algorithm&lt;br /&gt;
# Sends student IDs, topic IDs, student preferences, and timestamps to the web service&lt;br /&gt;
# The web service returns the matched assignments in the JSON response body&lt;br /&gt;
class BiddingAlgorithmService&lt;br /&gt;
  SERVICE_URL = 'http://app-csc517.herokuapp.com/match_topics'.freeze&lt;br /&gt;
&lt;br /&gt;
  # MOCK_DATA is based on the documentation detailing how the webservice behaves.&lt;br /&gt;
  # Reference: https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review#Webservice&lt;br /&gt;
  MOCK_DATA = {&lt;br /&gt;
    36239 =&amp;gt; [3970, 3972, 3975],&lt;br /&gt;
    36240 =&amp;gt; [3973, 3974, 3972],&lt;br /&gt;
    36241 =&amp;gt; [3969, 3971, 3972],&lt;br /&gt;
    36242 =&amp;gt; [3969, 3971, 3973],&lt;br /&gt;
    36243 =&amp;gt; [3969, 3970, 3971]&lt;br /&gt;
  }.freeze&lt;br /&gt;
&lt;br /&gt;
  def initialize(bidding_data)&lt;br /&gt;
    @bidding_data = bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def run&lt;br /&gt;
    return MOCK_DATA if Rails.application.config.use_mock_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
    perform_request&lt;br /&gt;
  rescue RestClient::ExceptionWithResponse, JSON::ParserError&lt;br /&gt;
    false&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def perform_request&lt;br /&gt;
    response = RestClient.post(&lt;br /&gt;
      SERVICE_URL,&lt;br /&gt;
      @bidding_data.to_json,&lt;br /&gt;
      content_type: :json,&lt;br /&gt;
      accept: :json&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    validate_response(response)&lt;br /&gt;
    JSON.parse(response.body)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def validate_response(response)&lt;br /&gt;
    raise 'Invalid response format' unless response.headers[:content_type] &amp;amp;&amp;amp; response.headers[:content_type].include?('application/json')&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Resolve Functionality Issues for Non Bidders ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Currently it doesn't work if some student does not bid. In this case, algorithm needs to be fixed to ignore anyone who didn’t bid, and let them choose from what’s left over.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to do several things within this controller. We will set a default state for non-bidders that we can point to to give them options to select topics to review from the leftovers. We will consolidate &amp;lt;code&amp;gt;@sign_up_topics&amp;lt;/code&amp;gt; to resolve the DRY violation. Lastly, we will remove business logic from the show method and push it to a new helper to conform to the SRP.&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # Additional method&lt;br /&gt;
&lt;br /&gt;
  # Provides variables for review bidding page&lt;br /&gt;
  def show&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id].to_i)&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @sign_up_topics = SignUpTopic.where(assignment_id: @assignment.id, private_to: nil)&lt;br /&gt;
    my_topic = SignedUpTeam.topic_id(@participant.parent_id, @participant.user_id)&lt;br /&gt;
    @sign_up_topics -= SignUpTopic.where(assignment_id: @assignment.id, id: my_topic)&lt;br /&gt;
    @num_participants = AssignmentParticipant.where(parent_id: @assignment.id).count&lt;br /&gt;
    @selected_topics = nil # This is used to list the topics assigned to review (i.e., select == assigned)&lt;br /&gt;
    @bids = ReviewBid.where(participant_id: @participant, assignment_id: @assignment.id)&lt;br /&gt;
    signed_up_topics = []&lt;br /&gt;
    &lt;br /&gt;
    @bids.each do |bid|&lt;br /&gt;
      sign_up_topic = SignUpTopic.find_by(id: bid.signuptopic_id)&lt;br /&gt;
      signed_up_topics &amp;lt;&amp;lt; sign_up_topic if sign_up_topic&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    signed_up_topics &amp;amp;= @sign_up_topics&lt;br /&gt;
    @sign_up_topics -= signed_up_topics&lt;br /&gt;
    @bids = signed_up_topics&lt;br /&gt;
    @num_of_topics = @sign_up_topics.size&lt;br /&gt;
    @assigned_review_maps = []&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: @assignment.id, reviewer_id: @participant.id).each do |review_map|&lt;br /&gt;
      @assigned_review_maps &amp;lt;&amp;lt; review_map&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Explicitly render view since it's in the sign up sheet views&lt;br /&gt;
    render 'sign_up_sheet/review_bids_show'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Additional methods&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-2.-reviewbidshelper-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
 # Assigns bidding topics to reviewers&lt;br /&gt;
  def assign_bidding&lt;br /&gt;
      assignment = validate_assignment(params[:assignment_id])&lt;br /&gt;
&lt;br /&gt;
      reviewers = validate_reviewers(assignment.id)&lt;br /&gt;
      reviewer_ids = reviewers.map(&amp;amp;:id)&lt;br /&gt;
&lt;br /&gt;
      matched_topics = run_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
      if matched_topics.blank?&lt;br /&gt;
        flash[:alert] = 'Topic or assignment is missing'&lt;br /&gt;
        redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      leftover_topics = find_leftover_topics(assignment.id, matched_topics)&lt;br /&gt;
      assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.new.assign_review_topics(matched_topics)&lt;br /&gt;
      assignment.update!(can_choose_topic_to_review: false)&lt;br /&gt;
&lt;br /&gt;
      flash[:notice] = 'Reviewers were successfully assigned to topics.'&lt;br /&gt;
      redirect_back fallback_location: root_path&lt;br /&gt;
    rescue ArgumentError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ArgumentError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: e.message&lt;br /&gt;
    rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::RecordInvalid: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue ActiveRecord::ActiveRecordError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::ActiveRecordError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue StandardError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;StandardError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers. Please try again later.'&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  #service methods here ...&lt;br /&gt;
&lt;br /&gt;
  def find_leftover_topics(assignment_id, matched_topics)&lt;br /&gt;
    all_topic_ids = SignUpTopic.where(assignment_id: assignment_id).pluck(:id)&lt;br /&gt;
    assigned_topic_ids = matched_topics.map { |match| match[:topic_id] }&lt;br /&gt;
&lt;br /&gt;
    # Calculate leftover topics&lt;br /&gt;
    all_topic_ids - assigned_topic_ids&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
    return if leftover_topics.blank?&lt;br /&gt;
&lt;br /&gt;
    # Find non-bidders by excluding already matched reviewers&lt;br /&gt;
    non_bidders = reviewer_ids - matched_topics.keys&lt;br /&gt;
&lt;br /&gt;
    # Assign leftover topics to non-bidders in a round-robin fashion&lt;br /&gt;
    non_bidders.each_with_index do |reviewer_id, index|&lt;br /&gt;
      topic_id = leftover_topics[index % leftover_topics.length]&lt;br /&gt;
      ReviewBid.create(priority: 1, signuptopic_id: topic_id, participant_id: reviewer_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirm Functionality for Single-person Teams ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Make sure your code works on individual assignments, i.e., assignments where participants sign up for topics instead of teams. In this case, a team is created for each participant when they sign up. So the code should work for assignments to which either individuals or teams submit.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to make an update to focus on topic rather than team or have it where teams are updated based on topic id.We will probably utilize sign up topic or a similar object to accomplish this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-5-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bid Status Messaging ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| To make the functionality more intuitive, include a message to say how many students are eligible to submit bids, how many have submitted their bids, when the deadline for submitting bids is.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
The logic for these messages will go into the helper file. It is effectively the same reasoning as our migration of other business/functionality code from the controller to the helper to conform to the SRP. It will go hand in hand with the SignUpTopic portion of the code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-6-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Why are the methods in review_bid.rb class methods? Can we change them to instance methods or move it to helpers?&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-1.-reviewbid-model-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model Updates ====&lt;br /&gt;
Since the current class methods are tied to &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model instances, we will change them to instance methods. There is no need to move them to helpers.&lt;br /&gt;
&lt;br /&gt;
===== Review Bid Model Flow Before Refactoring =====&lt;br /&gt;
This sequence diagram describes how the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; interacts with the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model in assigning review topics to reviewers according to their bids. Once the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; action is initiated by the user, the controller gathers the IDs of reviewers and calls &amp;lt;code&amp;gt;ReviewBid.bidding_data&amp;lt;/code&amp;gt; to gather bidding information. That is sent out to an external bidding algorithm, and the matched topics that come back are used by &amp;lt;code&amp;gt;ReviewBid.assign_review_topics&amp;lt;/code&amp;gt; to create &amp;lt;code&amp;gt;ReviewResponseMap&amp;lt;/code&amp;gt; entries, assigning topics to reviewers for the assignment.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
  belongs_to :assignment, class_name: 'Assignment'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  def self.bidding_data(assignment_id, reviewer_ids)&lt;br /&gt;
    # create basic hash and set basic hash data&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'users' =&amp;gt; {}, 'max_accepted_proposals' =&amp;gt; [] }&lt;br /&gt;
    bidding_data['tid'] = SignUpTopic.where(assignment_id: assignment_id).ids&lt;br /&gt;
    bidding_data['max_accepted_proposals'] = Assignment.where(id: assignment_id).pluck(:num_reviews_allowed).first&lt;br /&gt;
&lt;br /&gt;
    # loop through reviewer_ids to get reviewer specific bidding data&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      bidding_data['users'][reviewer_id] = reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # assigns topics to reviews as matched by the webservice algorithm&lt;br /&gt;
  def self.assign_review_topics(assignment_id, reviewer_ids, matched_topics, _min_num_reviews = 2)&lt;br /&gt;
    # if review response map already created, delete it&lt;br /&gt;
    if ReviewResponseMap.where(reviewed_object_id: assignment_id)&lt;br /&gt;
      ReviewResponseMap.where(reviewed_object_id: assignment_id).destroy_all&lt;br /&gt;
    end&lt;br /&gt;
    # loop through reviewer_ids to assign reviews to each reviewer&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      topics_to_assign = matched_topics[reviewer_id.to_s]&lt;br /&gt;
      topics_to_assign.each do |topic|&lt;br /&gt;
        assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method to assign a single topic to a reviewer&lt;br /&gt;
  def self.assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic).pluck(:team_id).first&lt;br /&gt;
    team_to_review.nil? ? [] : ReviewResponseMap.create(reviewed_object_id: assignment_id, reviewer_id: reviewer_id, reviewee_id: team_to_review, type: 'ReviewResponseMap')&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method for getting individual reviewer_ids bidding data&lt;br /&gt;
  # returns user's bidding data hash&lt;br /&gt;
  def self.reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    reviewer_user_id = AssignmentParticipant.find(reviewer_id).user_id&lt;br /&gt;
    self_topic = SignedUpTeam.topic_id(assignment_id, reviewer_user_id)&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'otid' =&amp;gt; self_topic, 'priority' =&amp;gt; [], 'time' =&amp;gt; [] }&lt;br /&gt;
    bids = ReviewBid.where(participant_id: reviewer_id)&lt;br /&gt;
&lt;br /&gt;
    # loop through each bid for a topic to get specific data&lt;br /&gt;
    bids.each do |bid|&lt;br /&gt;
      bidding_data['tid'] &amp;lt;&amp;lt; bid.signuptopic_id&lt;br /&gt;
      bidding_data['priority'] &amp;lt;&amp;lt; bid.priority&lt;br /&gt;
      bidding_data['time'] &amp;lt;&amp;lt; bid.updated_at&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  # Instance method to get bidding data for this review bid&lt;br /&gt;
  def bidding_data&lt;br /&gt;
    {&lt;br /&gt;
      'tid' =&amp;gt; topic_ids,&lt;br /&gt;
      'otid' =&amp;gt; self_topic,&lt;br /&gt;
      'priority' =&amp;gt; priorities,&lt;br /&gt;
      'time' =&amp;gt; updated_times&lt;br /&gt;
    }&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # bid object to limit db touches for similar data acquisitions&lt;br /&gt;
  def bids&lt;br /&gt;
    @bids ||= ReviewBid.where(participant_id: participant_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def topic_ids&lt;br /&gt;
    bids.pluck(:signuptopic_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def priorities&lt;br /&gt;
    bids.pluck(:priority)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def updated_times&lt;br /&gt;
    bids.pluck(:updated_at)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self_topic&lt;br /&gt;
    return nil unless topic &amp;amp;&amp;amp; topic.assignment_id&lt;br /&gt;
&lt;br /&gt;
    SignedUpTeam.topic_id(topic.assignment_id, participant.user_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign topics to reviews&lt;br /&gt;
  def assign_review_topics(matched_topics)&lt;br /&gt;
    validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: topic.assignment_id).destroy_all&lt;br /&gt;
&lt;br /&gt;
    matched_topics.each do |reviewer_id, topics|&lt;br /&gt;
      Array(topics).each { |topic_id| assign_topic_to_reviewer(reviewer_id, topic_id) }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign a single topic to a reviewer&lt;br /&gt;
  def assign_topic_to_reviewer(reviewer_id, topic_id)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic_id).pluck(:team_id).first&lt;br /&gt;
    return if team_to_review.nil?&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.create(&lt;br /&gt;
      reviewed_object_id: topic.assignment_id,&lt;br /&gt;
      reviewer_id: reviewer_id,&lt;br /&gt;
      reviewee_id: team_to_review,&lt;br /&gt;
      type: 'ReviewResponseMap'&lt;br /&gt;
    )&lt;br /&gt;
    # error handling&lt;br /&gt;
  rescue StandardError =&amp;gt; e&lt;br /&gt;
    Rails.logger.error(&amp;quot;Failed to assign topic #{topic_id} to reviewer #{reviewer_id}: #{e.message}&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
    raise ArgumentError, 'Topic or assignment is missing' if topic.nil? || topic.assignment_id.nil?&lt;br /&gt;
    raise ArgumentError, 'Matched topics must be a Hash' unless matched_topics.is_a?(Hash)&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
Once we change them to instance methods, we’ll need to modify their use in the code to instantiate the object first.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;Placeholder for implementation screenshots (before and after)&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-3.-schema.db-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;Schema.db&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We also need to remove the foreign key reference to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model since the &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model already has access to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;, and that is the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; we should be referencing. This will also involve modifying the database to remove the foreign key reference to the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Steps required:&lt;br /&gt;
&lt;br /&gt;
# Review the records in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table in the database to see if there are any records and if those records have values for &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;&lt;br /&gt;
# Create a migration file using the command: &amp;lt;code&amp;gt;rails generate migration RemoveAssignmentFromReviewBids&amp;lt;/code&amp;gt;&lt;br /&gt;
# In the generated file, indicate the change that needs to be made using the change method and migration columns &amp;lt;code&amp;gt;remove_foreign_key&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;remove_column&amp;lt;/code&amp;gt;. Migration should be straightforward since there is no cascading behavior (i.e., &amp;lt;code&amp;gt;dependent:destroy&amp;lt;/code&amp;gt;) to worry about.&lt;br /&gt;
# Once comfortable with the updates to the generated file, run the migration using the &amp;lt;code&amp;gt;rails db:migrate&amp;lt;/code&amp;gt; command.&lt;br /&gt;
# Run a query in the DB to ensure no orphaned records are in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table after the updates.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;Placeholder for implementation screenshots (before and after)&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-4.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implement Edge Case Testing ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| In the previous implementation wiki, there are edge cases which are not exhaustively tested. Should test those edge cases thoroughly and add more edge case testing - link&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span class=&amp;quot;mark&amp;quot;&amp;gt;TODO&amp;lt;/span&amp;gt;:We just got initial test results back. We still need to figure out why tests are failing and what may be slipping through the cracks.[[File:Test expertiza.png|411x459px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-8-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design Patterns and Principles ===&lt;br /&gt;
Below are the design patterns and principles we plan to use to address the problem statements outlined in the project requirements:&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
** Controller Responsibility&lt;br /&gt;
*** To address the issue of authentication logic residing in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, we will refactor the code to ensure the controller solely handles request and response flows.&lt;br /&gt;
*** Authentication logic will be moved to dedicated methods in &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt;, making the codebase easier to maintain and test.&lt;br /&gt;
** View Simplification&lt;br /&gt;
*** Currently, the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; view contains excessive business logic, violating SRP.&lt;br /&gt;
*** We will move this logic into helper methods or models, ensuring each component has a single responsibility and improving code readability.&lt;br /&gt;
&lt;br /&gt;
* Separation of Concerns (SoC)&lt;br /&gt;
** By relocating authorization logic out of the controller, we separate authentication from request handling.&lt;br /&gt;
** Adhering to SoC enhances readability and maintainability by allowing each part of the application to focus on its specific role.&lt;br /&gt;
* Don't Repeat Yourself (DRY)&lt;br /&gt;
** We will inspect the existing implementation for code duplication, particularly in the controller actions.&lt;br /&gt;
** Any repetitive code will be refactored or removed to reduce redundancy and improve clarity.&lt;br /&gt;
&lt;br /&gt;
* Model-View-Controller (MVC) Adherence&lt;br /&gt;
** Moving business logic from the view aligns with MVC best practices.&lt;br /&gt;
** By ensuring the view only presents data without processing it, we maintain clear boundaries between the model, view, and controller layers.&lt;br /&gt;
&lt;br /&gt;
* Dependency Inversion Principle (DIP)&lt;br /&gt;
** We plan to update class methods to instance methods, allowing the controller to depend on abstractions rather than concrete implementations.&lt;br /&gt;
** This change leverages Ruby's duck typing, providing flexibility and making the code easier to maintain and test.&lt;br /&gt;
&lt;br /&gt;
* Factory Pattern&lt;br /&gt;
** We will implement a factory pattern for the common processing of &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpBid&amp;lt;/code&amp;gt;, encapsulating object creation and promoting code reuse.&lt;br /&gt;
&lt;br /&gt;
* Law of Demeter (Principle of Least Knowledge)&lt;br /&gt;
** We'll delegate interactions appropriately to prevent tightly coupled code, especially when dealing with topics and teams.&lt;br /&gt;
** Clear and intentional naming during refactoring will enhance code clarity and maintainability.&lt;br /&gt;
&lt;br /&gt;
* Adapter Pattern&lt;br /&gt;
** To add messaging to bid states for users, we'll use the adapter pattern to allow different components to work together without modifying their existing code.&lt;br /&gt;
&lt;br /&gt;
== Files Modified/Added ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-1.-controllers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
review_bids_controller.rb&lt;br /&gt;
&lt;br /&gt;
signup_sheet_controller.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-2.-models&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
review_bid.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-3.-helpers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
&lt;br /&gt;
authorization_helper.rb&lt;br /&gt;
&lt;br /&gt;
Review_bids_helper.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;viii.-test-plan&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
The Excel spreadsheet below details our test plan for testing the code we changed or created to ensure code coverage. The process involved reviewing the code and determining all the possible scenarios that needed to be covered based on what the method was doing, then creating test cases accordingly.&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/spreadsheets/d/1iHzOdSwlTtxOtf5VsNdwOaCP8NzYMmiJ/edit?usp=drive_link Open Test Matrix Spreadsheet]&lt;br /&gt;
[https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/review_bids_controller_spec.rb ReviewBidsController updated test cases]&lt;br /&gt;
[https://github.com/expertiza/expertiza/blob/84a1270930a22dcce952e3527d23cd2c7cd0cf23/spec/controllers/sign_up_sheet_controller_spec.rb SignUpSheetController updated test cases]&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-1.-mentor&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Ed Gehringer&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-2.-members&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Members ===&lt;br /&gt;
&lt;br /&gt;
* Gavin Teague&lt;br /&gt;
* Janice Uwujaren&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xi.-links&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
Previous Wiki: [https://wiki.expertiza.ncsu.edu/index.php/CSC/ECE_517_Fall_2021_-_E2151._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;Link&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xii.-references&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/document/d/1LHVacylQmB15I6VcYUA5nuC7GRRU9szHzZhjlPcE-PA/edit?tab=t.0#heading=h.u0oycoj6e0ev &amp;lt;u&amp;gt;Project Instructions&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;appendix-a-uml-class-diagram-plantuml-code-for-review-bids&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Juwujar</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160951</id>
		<title>CSC/ECE 517 Fall 2024 - E2485. Allow reviewers to bid on what to review</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160951"/>
		<updated>2024-12-10T15:07:59Z</updated>

		<summary type="html">&lt;p&gt;Juwujar: /* Test Coverage */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
This project will fix the defects and improve the functionality in Expertiza of the Reviewer Bidding feature, a process where reviewers can bid for assignments they want to review. It will make the functionality smoother, more reliable, and align with Expertiza's collaborative learning objectives.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iii.-problem-statements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Problem Statements ==&lt;br /&gt;
&lt;br /&gt;
Assigning reviews to users on Expertiza involves a complex bidding algorithm that requires optimization before production deployment. Below are key areas we plan to address as per the project requirements:&lt;br /&gt;
* Code Refactoring and Best Practices&lt;br /&gt;
** '''Utilize Authentication Utilities''': Modify the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; function in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to use existing authentication utilities, enhancing code reusability and adhering to the Single Responsibility Principle (SRP) and Separation of Concerns (SoC).&lt;br /&gt;
** '''Eliminate Code Duplication''': Refactor &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; to comply with the Don't Repeat Yourself (DRY) principle.&lt;br /&gt;
** '''Rename Functions for Clarity''': Change &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; to better reflect its purpose.&lt;br /&gt;
** '''Refactor Methods in '''&amp;lt;code&amp;gt;review_bid.rb&amp;lt;/code&amp;gt;''':''': Convert class methods to instance methods or relocate them to helper classes for improved code organization.&lt;br /&gt;
* Functionality Enhancements&lt;br /&gt;
** '''Handle Missing Bids Gracefully''': Adjust the bidding algorithm to accommodate students who do not bid, allowing them to select from the remaining options.&lt;br /&gt;
** '''Support Both Individual and Team Assignments''': Ensure the code functions correctly for participant and team-based assignments when signing up for topics.&lt;br /&gt;
* User Interface Improvements&lt;br /&gt;
** '''Increase Transparency in the Bidding Process''': Display messages indicating how many students are eligible to bid, how many have submitted bids, and the deadline.&lt;br /&gt;
** '''Accurate Bid Status Indicators''': Ensure topic colors change correctly based on the number of outstanding bids, providing clear visual feedback to users.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Exhaustive Edge Case Testing''': Conduct thorough testing for edge cases not previously covered, adding additional test cases as needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iv.-design-goals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Design Goals ==&lt;br /&gt;
&lt;br /&gt;
While addressing the existing defects and enhancing the Reviewer Bidding feature, we plan to adhere to the following design guidelines:&lt;br /&gt;
&lt;br /&gt;
* Code Quality and Maintainability&lt;br /&gt;
** '''Improve Code Structure: '''Implement refactoring strategies to enhance code readability and maintainability, ensuring adherence to best practices like the DRY principle.&lt;br /&gt;
** '''Standardize Authentication Logic: '''Utilize a centralized authentication utility to promote consistency and reusability across the application.&lt;br /&gt;
* System Stability and Flexibility&lt;br /&gt;
** '''Enhance Algorithm Robustness: '''Modify the bidding algorithm to handle scenarios where students do not bid, ensuring the system remains stable under all conditions.&lt;br /&gt;
** '''Ensure Assignment Versatility: '''Design the system to seamlessly support both individual and team assignments without additional configuration.&lt;br /&gt;
* User Experience Enhancements&lt;br /&gt;
** '''Increase Bidding Transparency: '''Develop user interface elements that clearly display bidding status, deadlines, and participation metrics.&lt;br /&gt;
** '''Implement Dynamic Visual Feedback: '''Use real-time visual cues, such as color changes, to inform users about the bidding status instantly.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Develop Comprehensive Test Suites: '''Create extensive automated tests covering edge cases and critical functionalities to guarantee reliable system behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;v.-uml-diagram&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== UML Diagram ==&lt;br /&gt;
&lt;br /&gt;
Below is a visual representation of the models associated with the review bids functionality, depicting the data structures and their relationships.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml class diagram2.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi.-implementation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
'''ReviewBid: '''The &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; manages the bidding process, where participants indicate their preferences for reviewing different topics or assignments. This helps distribute review tasks fairly based on participants' interests.&lt;br /&gt;
&lt;br /&gt;
'''Assignment: '''The &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model represents team or individual assignments in Expertiza. It stores all the essential information required to keep the assignment process organized, like managing participants, setting due dates, forming teams, and overseeing the review process.&lt;br /&gt;
&lt;br /&gt;
'''SignUpTopic: '''The &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt; model identifies specific topics or areas within an assignment for which participants can sign up. It manages the availability of these topics, tracks bids, coordinates team sign-ups, and assigns topics based on both bids and participant availability. &lt;br /&gt;
&lt;br /&gt;
'''Participant: '''The &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model is about the individual users who participate in an assignment. This includes students, instructors, teaching assistants, or other relevant roles. This model is responsible for managing key user information, monitoring interactions with the assignment, keeping track of team memberships, and recording things like badges and review grades.&lt;br /&gt;
&lt;br /&gt;
== Implementation ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1.-decoupling-authorization-logic-from-reviewbidscontroller&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Decoupling Authorization Logic from &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| '''&amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; needs to use the authentication utilities'''&lt;br /&gt;
&lt;br /&gt;
The function &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; should use the available authentication utilities to support the Single Responsibility Principle and Separation of Concerns and to support code reusability.&lt;br /&gt;
&lt;br /&gt;
| '''Authentication Consistency:''' Remove authentication logic in the controller and call logic in the authentication utility class instead to ensure consistent use of the same logic across the application, foster reusability, and eliminate DRY.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
==== Authorization Flow Before Refactoring ====&lt;br /&gt;
The sequence diagram depicts the authorization process within the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;. When a user invokes an action, the controller checks if the action is allowed by verifying the user's role through the &amp;lt;code&amp;gt;current_role_name()&amp;lt;/code&amp;gt; method in the &amp;lt;code&amp;gt;ApplicationController&amp;lt;/code&amp;gt;. Depending on the action and the user's role, the controller either authorizes the user to proceed or denies access, ultimately executing the action logic if authorized and responding to the user.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 1.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We simplified the &amp;lt;code&amp;gt;action_allowed?&amp;lt;/code&amp;gt; method by decoupling authorization logic from the controller to use helper methods in the &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt; module. The &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; should not handle authentication, which is a violation of SRP. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;vertical-align:top;&amp;quot; | Original Code !! style=&amp;quot;vertical-align:top;&amp;quot; | Refactored Code&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index'&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator',&lt;br /&gt;
     'Student'].include? current_role_name and&lt;br /&gt;
        ((%w[list].include? action_name) ? are_needed_authorizations_present?(params[:id], &amp;quot;participant&amp;quot;, &amp;quot;reader&amp;quot;, &amp;quot;submitter&amp;quot;, &amp;quot;reviewer&amp;quot;) : true)&lt;br /&gt;
  else&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator'].include? current_role_name&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index', 'list'&lt;br /&gt;
    current_user_has_student_privileges? &amp;amp;&amp;amp; list_authorization_check&lt;br /&gt;
  else&lt;br /&gt;
    current_user_has_ta_privileges?&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; action ===&lt;br /&gt;
The refactor of the &amp;lt;code&amp;gt;ReviewBidsController's index&amp;lt;/code&amp;gt; action simplifies and streamlines the codebase, ensuring the action simply handles controller activities. Previously, the controller was overloaded with responsibilities—it handled everything from locating and validating assignments to fetching review mappings and counting completed reviews. This all-in-one approach made the controller action messy. &lt;br /&gt;
&lt;br /&gt;
Now we have moved the business logic out of the controller action and into dedicated service objects. With this change, we introduced two key service objects: &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; focuses on retrieving the participant along with their assignments, while the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; takes on the task of managing review mappings and calculating review-related stats, like how many reviews have been completed. &lt;br /&gt;
&lt;br /&gt;
This clear separation of responsibilities aligns with the Single Responsibility Principle, which helps each part of our code be more focused and easier to understand. With these new service objects, the code has become more reusable. The logic for fetching assignments, retrieving review mappings, and counting reviews is now more flexible and reusable throughout the application. For example, we used methods in the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; in both the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; and the &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;, helping to avoid code duplication.&lt;br /&gt;
&lt;br /&gt;
After moving the business logic to service objects, we can write unit tests to ensure each part of the code behaves as expected and prevents bugs. We also moved complex calculations, like those for completed reviews, into the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;, freeing the controller from dealing with the implementation details and promoting a cleaner design. The article https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial provides more information on when and how to use service objects in Rails.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
  return unless current_user_id?(@participant.user_id)&lt;br /&gt;
  @assignment = @participant.assignment&lt;br /&gt;
  @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
  # Finding how many reviews have been completed&lt;br /&gt;
  @num_reviews_completed = 0&lt;br /&gt;
  @review_mappings.each do |map|&lt;br /&gt;
    @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # render view for completing reviews after review bidding has been completed&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @assignment = participant_service.assignment&lt;br /&gt;
  unless @assignment.is_a?(Assignment)&lt;br /&gt;
    flash[:error] = 'Assignment not found.'&lt;br /&gt;
    redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  @review_mappings = review_service.review_mappings&lt;br /&gt;
  @num_reviews_completed = review_service.review_counts[:completed]&lt;br /&gt;
&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Optimize &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Action functionality ===&lt;br /&gt;
In the earlier version of the set_priority action in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, the server only needed a couple of key pieces of information: an array of topic IDs (params[:topic]) and the participant ID (params[:id}). From there, it would figure out the assignment_id by using the first topic as a reference. While this worked in simple cases, it could lead to problems if any data was missing or didn’t line up correctly.&lt;br /&gt;
&lt;br /&gt;
With the new implementation, the action now expects clear and structured parameters, including participant_id, an array of topic IDs, and assignment_id. This change ensures that the server always receives complete and reliable information, preventing issues caused by missing or inconsistent data. It also removes the need for the server to perform unnecessary lookups, allowing the controller to focus on its main job: validating inputs (like making sure the topics in params[:topic] belong to the same assignment as the participant), updating the database, handling errors properly, and sending the correct response back to the view.&lt;br /&gt;
&lt;br /&gt;
While these updates have cleaned up the set_priority action, they still contain a lot of business logic, like validations and priority updates. To make the code even more maintainable, there’s room for further refactoring—perhaps moving some of this logic into a service class or model method to streamline the controller even more.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | Old Code&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    if params[:topic].nil?&lt;br /&gt;
      ReviewBid.where(participant_id: params[:id]).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      participant = AssignmentParticipant.find_by(id: params[:id])&lt;br /&gt;
      assignment_id = SignUpTopic.find(params[:topic].first).assignment.id&lt;br /&gt;
      @bids = ReviewBid.where(participant_id: params[:id])&lt;br /&gt;
      signed_up_topics = ReviewBid.where(participant_id: params[:id]).map(&amp;amp;:signuptopic_id)&lt;br /&gt;
      signed_up_topics -= params[:topic].map(&amp;amp;:to_i)&lt;br /&gt;
      signed_up_topics.each do |topic|&lt;br /&gt;
        ReviewBid.where(signuptopic_id: topic, participant_id: params[:id]).destroy_all&lt;br /&gt;
      end&lt;br /&gt;
      params[:topic].each_with_index do |topic_id, index|&lt;br /&gt;
        bid_existence = ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id])&lt;br /&gt;
        if bid_existence.empty?&lt;br /&gt;
          ReviewBid.create(priority: index + 1, signuptopic_id: topic_id, participant_id: params[:id], assignment_id: assignment_id)&lt;br /&gt;
        else&lt;br /&gt;
          ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id]).update_all(priority: index + 1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to action: 'show', assignment_id: params[:assignment_id], id: params[:id]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    participant_id = params[:participant_id].to_i&lt;br /&gt;
    selected_topic_ids = params[:topic]&amp;amp;.map(&amp;amp;:to_i) || []&lt;br /&gt;
    assignment_id = params[:assignment_id].to_i&lt;br /&gt;
&lt;br /&gt;
    unless current_user_can_modify_participant?(participant_id)&lt;br /&gt;
      flash[:error] = 'You are not authorized to modify these bids.'&lt;br /&gt;
      respond_to do |format|&lt;br /&gt;
        format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
        format.json { render json: { status: 'unauthorized' }, status: :unauthorized }&lt;br /&gt;
      end&lt;br /&gt;
      return&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    if selected_topic_ids.empty?&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      assignment = Assignment.find_by(id: assignment_id)&lt;br /&gt;
      if assignment.nil?&lt;br /&gt;
        flash[:error] = &amp;quot;Invalid assignment.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_assignment' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      unless SignUpTopic.where(id: selected_topic_ids, assignment_id: assignment_id).count == selected_topic_ids.size&lt;br /&gt;
        flash[:error] = &amp;quot;One or more selected topics are invalid.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_topics' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).where.not(signuptopic_id: selected_topic_ids).destroy_all&lt;br /&gt;
&lt;br /&gt;
      selected_topic_ids.each_with_index do |topic_id, index|&lt;br /&gt;
        bid = ReviewBid.find_or_initialize_by(signuptopic_id: topic_id, participant_id: participant_id)&lt;br /&gt;
        bid.priority = index + 1&lt;br /&gt;
        bid.assignment_id = assignment_id&lt;br /&gt;
        bid.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_to action: 'show', assignment_id: assignment_id, id: participant_id, notice: 'Review bids updated successfully.' }&lt;br /&gt;
      format.json { render json: { status: 'success' }, status: :ok }&lt;br /&gt;
    end&lt;br /&gt;
  rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
    flash[:error] = &amp;quot;Failed to update priorities: #{e.message}&amp;quot;&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
      format.json { render json: { status: 'error', message: e.message }, status: :unprocessable_entity }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Client Side Changes to Support &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Changes ====&lt;br /&gt;
After tidying up the server-side logic, we needed to refactor the client-side code to ensure it passed the correct information from the view to the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action. On the client side, we now grab the topicIDs and send them as &amp;lt;code&amp;gt;selected_topic_ids&amp;lt;/code&amp;gt;, allowing the controller to figure out which topics were selected and in what order. It’s a cleaner and more reliable way to keep everything working together! &lt;br /&gt;
&lt;br /&gt;
We are also extracting the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, making it a lot easier to send data with AJAX and keeping things flexible. AJAX is awesome because it lets us update the server without reloading the page, making the user experience smooth and responsive. When we pull the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, we can grab it along with other data, like the list of topics, and include it in the request. This way, we don’t have to rely on the URL to know which participant we’re working with, which makes the code cleaner and more adaptable.&lt;br /&gt;
&lt;br /&gt;
If we were to rely on the route for the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt;, every AJAX request would need to be tied to a specific URL structure. That’s limiting, especially if the route doesn’t naturally include the participant ID, as can be the case with collection routes versus member routes in Rails. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery -&amp;gt;&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: -&amp;gt;&lt;br /&gt;
      $.post($(this).data('update-url'), $(this).sortable('serialize'))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery(document).ready ($) -&amp;gt;&lt;br /&gt;
  sendUpdatedOrder = -&amp;gt;&lt;br /&gt;
    sortedIDs = $(this).sortable('toArray')&lt;br /&gt;
&lt;br /&gt;
    topicIDs = sortedIDs.map (id) -&amp;gt;&lt;br /&gt;
      id.replace 'topic_', ''&lt;br /&gt;
&lt;br /&gt;
    participant_id = $(this).data('participant-id')&lt;br /&gt;
&lt;br /&gt;
    assignment_id = new URL($(this).data('update-url'), window.location.origin).searchParams.get('assignment_id')&lt;br /&gt;
&lt;br /&gt;
    $.ajax&lt;br /&gt;
      type: 'POST'&lt;br /&gt;
      url: $(this).data('update-url')&lt;br /&gt;
      data:&lt;br /&gt;
        topic: topicIDs&lt;br /&gt;
        participant_id: participant_id&lt;br /&gt;
        assignment_id: assignment_id&lt;br /&gt;
      success: (response) -&amp;gt;&lt;br /&gt;
        console.log 'Selections updated successfully.'&lt;br /&gt;
      error: (xhr, status, error) -&amp;gt;&lt;br /&gt;
        alert 'An error occurred while saving your selections. Please try again.'&lt;br /&gt;
        console.error error&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: sendUpdatedOrder&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-1.-signupsheetcontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; Updates ===&lt;br /&gt;
&lt;br /&gt;
The ReviewBidsController's index method renders the review_bids_others_work action in &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt;. However, the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; does not include the controller action for this view. We need to find the old implementation of the controller action, add it back, and review and update it for any DRY violations.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # provides variables for reviewing page located at views/review_bids/others_work.html.erb&lt;br /&gt;
  def index&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
    return unless current_user_id?(@participant.user_id)&lt;br /&gt;
&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
    # Finding how many reviews have been completed&lt;br /&gt;
    @num_reviews_completed = 0&lt;br /&gt;
    @review_mappings.each do |map|&lt;br /&gt;
      @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # render view for completing reviews after review bidding has been completed&lt;br /&gt;
    render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
  end  &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-2.-reviewbidsotherswork-view-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;review_bids_others_work.html.erb&amp;lt;/code&amp;gt; View Updates ====&lt;br /&gt;
&lt;br /&gt;
The code only contains a view with this name and no associated controller action. Reviewing the code in the view, we noticed some SRP violations where there is much logic in the presentation layer. Set up helper methods in the controller to handle some of that logic or see if the model can handle some of that logic.&lt;br /&gt;
&lt;br /&gt;
Note: The view is technically part of the SignUpSheetController, but it's being rendered by the ReviewBidsController, which initially threw us off. After chatting with the professor, we decided not to change this view. He indicated that the plan is to reuse these views for both assignment bidding and review bidding.&lt;br /&gt;
&lt;br /&gt;
We would suggest building the assignment bidding and review bidding features separately first to make sure each one works on its own. Once that’s done, we can go back and clean things up by combining any shared functionality. This could include creating shared views or even introducing an abstract model to handle both types of bidding in a cleaner way. It could also mean replacing the ReviewBidsController with something more generic, like a BidsController, to keep things reusable and easier to maintain down the line.&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Rename &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; method ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; should be &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt;&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:''' Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The primary activity here is to refactor the method name &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; and fix the hardcoded URL. However, the match topic bidding algorithm no longer works since Heroku is no longer free, per the professor. The reference link below has detailed information about the web service that we can leverage to gain that understanding.&lt;br /&gt;
&lt;br /&gt;
Reference Link for Webservice Details: [https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Updated Implementation ====&lt;br /&gt;
The original requirement was to rename the run_bidding_algorithm method to assign_reviewers, but after digging into the code, we realized that renaming it didn’t make sense. The method runs the bidding algorithm to match topics, and there’s already a separate method to handle assigning review topics.&lt;br /&gt;
&lt;br /&gt;
However, we improved the logic in the assign_bidding method (which is called by the run_bidding_algorithm) by introducing a separate BiddingAlgorithmService object. This keeps the business logic out of the controller action, which makes things cleaner and more maintainable.&lt;br /&gt;
&lt;br /&gt;
==== review_bids_controller ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def run_bidding_algorithm&lt;br /&gt;
    review_bid = ReviewBid.new&lt;br /&gt;
    bidding_data = review_bid.bidding_data&lt;br /&gt;
    matched_topics = BiddingAlgorithmService.new(bidding_data).run&lt;br /&gt;
    raise ArgumentError, 'Failed to assign reviewers. Please try again later.' unless matched_topics&lt;br /&gt;
&lt;br /&gt;
    matched_topics&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== bidding_algorithm_service ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# frozen_string_literal: true&lt;br /&gt;
&lt;br /&gt;
# The `BiddingAlgorithmService` run the bid assignment algorithm&lt;br /&gt;
# Sends student IDs, topic IDs, student preferences, and timestamps to the web service&lt;br /&gt;
# The web service returns the matched assignments in the JSON response body&lt;br /&gt;
class BiddingAlgorithmService&lt;br /&gt;
  SERVICE_URL = 'http://app-csc517.herokuapp.com/match_topics'.freeze&lt;br /&gt;
&lt;br /&gt;
  # MOCK_DATA is based on the documentation detailing how the webservice behaves.&lt;br /&gt;
  # Reference: https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review#Webservice&lt;br /&gt;
  MOCK_DATA = {&lt;br /&gt;
    36239 =&amp;gt; [3970, 3972, 3975],&lt;br /&gt;
    36240 =&amp;gt; [3973, 3974, 3972],&lt;br /&gt;
    36241 =&amp;gt; [3969, 3971, 3972],&lt;br /&gt;
    36242 =&amp;gt; [3969, 3971, 3973],&lt;br /&gt;
    36243 =&amp;gt; [3969, 3970, 3971]&lt;br /&gt;
  }.freeze&lt;br /&gt;
&lt;br /&gt;
  def initialize(bidding_data)&lt;br /&gt;
    @bidding_data = bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def run&lt;br /&gt;
    return MOCK_DATA if Rails.application.config.use_mock_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
    perform_request&lt;br /&gt;
  rescue RestClient::ExceptionWithResponse, JSON::ParserError&lt;br /&gt;
    false&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def perform_request&lt;br /&gt;
    response = RestClient.post(&lt;br /&gt;
      SERVICE_URL,&lt;br /&gt;
      @bidding_data.to_json,&lt;br /&gt;
      content_type: :json,&lt;br /&gt;
      accept: :json&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    validate_response(response)&lt;br /&gt;
    JSON.parse(response.body)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def validate_response(response)&lt;br /&gt;
    raise 'Invalid response format' unless response.headers[:content_type] &amp;amp;&amp;amp; response.headers[:content_type].include?('application/json')&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Resolve Functionality Issues for Non Bidders ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Currently it doesn't work if some student does not bid. In this case, algorithm needs to be fixed to ignore anyone who didn’t bid, and let them choose from what’s left over.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to do several things within this controller. We will set a default state for non-bidders that we can point to to give them options to select topics to review from the leftovers. We will consolidate &amp;lt;code&amp;gt;@sign_up_topics&amp;lt;/code&amp;gt; to resolve the DRY violation. Lastly, we will remove business logic from the show method and push it to a new helper to conform to the SRP.&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # Additional method&lt;br /&gt;
&lt;br /&gt;
  # Provides variables for review bidding page&lt;br /&gt;
  def show&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id].to_i)&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @sign_up_topics = SignUpTopic.where(assignment_id: @assignment.id, private_to: nil)&lt;br /&gt;
    my_topic = SignedUpTeam.topic_id(@participant.parent_id, @participant.user_id)&lt;br /&gt;
    @sign_up_topics -= SignUpTopic.where(assignment_id: @assignment.id, id: my_topic)&lt;br /&gt;
    @num_participants = AssignmentParticipant.where(parent_id: @assignment.id).count&lt;br /&gt;
    @selected_topics = nil # This is used to list the topics assigned to review (i.e., select == assigned)&lt;br /&gt;
    @bids = ReviewBid.where(participant_id: @participant, assignment_id: @assignment.id)&lt;br /&gt;
    signed_up_topics = []&lt;br /&gt;
    &lt;br /&gt;
    @bids.each do |bid|&lt;br /&gt;
      sign_up_topic = SignUpTopic.find_by(id: bid.signuptopic_id)&lt;br /&gt;
      signed_up_topics &amp;lt;&amp;lt; sign_up_topic if sign_up_topic&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    signed_up_topics &amp;amp;= @sign_up_topics&lt;br /&gt;
    @sign_up_topics -= signed_up_topics&lt;br /&gt;
    @bids = signed_up_topics&lt;br /&gt;
    @num_of_topics = @sign_up_topics.size&lt;br /&gt;
    @assigned_review_maps = []&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: @assignment.id, reviewer_id: @participant.id).each do |review_map|&lt;br /&gt;
      @assigned_review_maps &amp;lt;&amp;lt; review_map&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Explicitly render view since it's in the sign up sheet views&lt;br /&gt;
    render 'sign_up_sheet/review_bids_show'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Additional methods&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-2.-reviewbidshelper-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
 # Assigns bidding topics to reviewers&lt;br /&gt;
  def assign_bidding&lt;br /&gt;
      assignment = validate_assignment(params[:assignment_id])&lt;br /&gt;
&lt;br /&gt;
      reviewers = validate_reviewers(assignment.id)&lt;br /&gt;
      reviewer_ids = reviewers.map(&amp;amp;:id)&lt;br /&gt;
&lt;br /&gt;
      matched_topics = run_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
      if matched_topics.blank?&lt;br /&gt;
        flash[:alert] = 'Topic or assignment is missing'&lt;br /&gt;
        redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      leftover_topics = find_leftover_topics(assignment.id, matched_topics)&lt;br /&gt;
      assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.new.assign_review_topics(matched_topics)&lt;br /&gt;
      assignment.update!(can_choose_topic_to_review: false)&lt;br /&gt;
&lt;br /&gt;
      flash[:notice] = 'Reviewers were successfully assigned to topics.'&lt;br /&gt;
      redirect_back fallback_location: root_path&lt;br /&gt;
    rescue ArgumentError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ArgumentError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: e.message&lt;br /&gt;
    rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::RecordInvalid: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue ActiveRecord::ActiveRecordError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::ActiveRecordError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue StandardError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;StandardError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers. Please try again later.'&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  #service methods here ...&lt;br /&gt;
&lt;br /&gt;
  def find_leftover_topics(assignment_id, matched_topics)&lt;br /&gt;
    all_topic_ids = SignUpTopic.where(assignment_id: assignment_id).pluck(:id)&lt;br /&gt;
    assigned_topic_ids = matched_topics.map { |match| match[:topic_id] }&lt;br /&gt;
&lt;br /&gt;
    # Calculate leftover topics&lt;br /&gt;
    all_topic_ids - assigned_topic_ids&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
    return if leftover_topics.blank?&lt;br /&gt;
&lt;br /&gt;
    # Find non-bidders by excluding already matched reviewers&lt;br /&gt;
    non_bidders = reviewer_ids - matched_topics.keys&lt;br /&gt;
&lt;br /&gt;
    # Assign leftover topics to non-bidders in a round-robin fashion&lt;br /&gt;
    non_bidders.each_with_index do |reviewer_id, index|&lt;br /&gt;
      topic_id = leftover_topics[index % leftover_topics.length]&lt;br /&gt;
      ReviewBid.create(priority: 1, signuptopic_id: topic_id, participant_id: reviewer_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirm Functionality for Single-person Teams ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Make sure your code works on individual assignments, i.e., assignments where participants sign up for topics instead of teams. In this case, a team is created for each participant when they sign up. So the code should work for assignments to which either individuals or teams submit.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to make an update to focus on topic rather than team or have it where teams are updated based on topic id.We will probably utilize sign up topic or a similar object to accomplish this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-5-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bid Status Messaging ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| To make the functionality more intuitive, include a message to say how many students are eligible to submit bids, how many have submitted their bids, when the deadline for submitting bids is.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
The logic for these messages will go into the helper file. It is effectively the same reasoning as our migration of other business/functionality code from the controller to the helper to conform to the SRP. It will go hand in hand with the SignUpTopic portion of the code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-6-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Why are the methods in review_bid.rb class methods? Can we change them to instance methods or move it to helpers?&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-1.-reviewbid-model-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model Updates ====&lt;br /&gt;
Since the current class methods are tied to &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model instances, we will change them to instance methods. There is no need to move them to helpers.&lt;br /&gt;
&lt;br /&gt;
===== Review Bid Model Flow Before Refactoring =====&lt;br /&gt;
This sequence diagram describes how the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; interacts with the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model in assigning review topics to reviewers according to their bids. Once the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; action is initiated by the user, the controller gathers the IDs of reviewers and calls &amp;lt;code&amp;gt;ReviewBid.bidding_data&amp;lt;/code&amp;gt; to gather bidding information. That is sent out to an external bidding algorithm, and the matched topics that come back are used by &amp;lt;code&amp;gt;ReviewBid.assign_review_topics&amp;lt;/code&amp;gt; to create &amp;lt;code&amp;gt;ReviewResponseMap&amp;lt;/code&amp;gt; entries, assigning topics to reviewers for the assignment.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
  belongs_to :assignment, class_name: 'Assignment'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  def self.bidding_data(assignment_id, reviewer_ids)&lt;br /&gt;
    # create basic hash and set basic hash data&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'users' =&amp;gt; {}, 'max_accepted_proposals' =&amp;gt; [] }&lt;br /&gt;
    bidding_data['tid'] = SignUpTopic.where(assignment_id: assignment_id).ids&lt;br /&gt;
    bidding_data['max_accepted_proposals'] = Assignment.where(id: assignment_id).pluck(:num_reviews_allowed).first&lt;br /&gt;
&lt;br /&gt;
    # loop through reviewer_ids to get reviewer specific bidding data&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      bidding_data['users'][reviewer_id] = reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # assigns topics to reviews as matched by the webservice algorithm&lt;br /&gt;
  def self.assign_review_topics(assignment_id, reviewer_ids, matched_topics, _min_num_reviews = 2)&lt;br /&gt;
    # if review response map already created, delete it&lt;br /&gt;
    if ReviewResponseMap.where(reviewed_object_id: assignment_id)&lt;br /&gt;
      ReviewResponseMap.where(reviewed_object_id: assignment_id).destroy_all&lt;br /&gt;
    end&lt;br /&gt;
    # loop through reviewer_ids to assign reviews to each reviewer&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      topics_to_assign = matched_topics[reviewer_id.to_s]&lt;br /&gt;
      topics_to_assign.each do |topic|&lt;br /&gt;
        assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method to assign a single topic to a reviewer&lt;br /&gt;
  def self.assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic).pluck(:team_id).first&lt;br /&gt;
    team_to_review.nil? ? [] : ReviewResponseMap.create(reviewed_object_id: assignment_id, reviewer_id: reviewer_id, reviewee_id: team_to_review, type: 'ReviewResponseMap')&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method for getting individual reviewer_ids bidding data&lt;br /&gt;
  # returns user's bidding data hash&lt;br /&gt;
  def self.reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    reviewer_user_id = AssignmentParticipant.find(reviewer_id).user_id&lt;br /&gt;
    self_topic = SignedUpTeam.topic_id(assignment_id, reviewer_user_id)&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'otid' =&amp;gt; self_topic, 'priority' =&amp;gt; [], 'time' =&amp;gt; [] }&lt;br /&gt;
    bids = ReviewBid.where(participant_id: reviewer_id)&lt;br /&gt;
&lt;br /&gt;
    # loop through each bid for a topic to get specific data&lt;br /&gt;
    bids.each do |bid|&lt;br /&gt;
      bidding_data['tid'] &amp;lt;&amp;lt; bid.signuptopic_id&lt;br /&gt;
      bidding_data['priority'] &amp;lt;&amp;lt; bid.priority&lt;br /&gt;
      bidding_data['time'] &amp;lt;&amp;lt; bid.updated_at&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  # Instance method to get bidding data for this review bid&lt;br /&gt;
  def bidding_data&lt;br /&gt;
    {&lt;br /&gt;
      'tid' =&amp;gt; topic_ids,&lt;br /&gt;
      'otid' =&amp;gt; self_topic,&lt;br /&gt;
      'priority' =&amp;gt; priorities,&lt;br /&gt;
      'time' =&amp;gt; updated_times&lt;br /&gt;
    }&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # bid object to limit db touches for similar data acquisitions&lt;br /&gt;
  def bids&lt;br /&gt;
    @bids ||= ReviewBid.where(participant_id: participant_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def topic_ids&lt;br /&gt;
    bids.pluck(:signuptopic_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def priorities&lt;br /&gt;
    bids.pluck(:priority)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def updated_times&lt;br /&gt;
    bids.pluck(:updated_at)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self_topic&lt;br /&gt;
    return nil unless topic &amp;amp;&amp;amp; topic.assignment_id&lt;br /&gt;
&lt;br /&gt;
    SignedUpTeam.topic_id(topic.assignment_id, participant.user_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign topics to reviews&lt;br /&gt;
  def assign_review_topics(matched_topics)&lt;br /&gt;
    validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: topic.assignment_id).destroy_all&lt;br /&gt;
&lt;br /&gt;
    matched_topics.each do |reviewer_id, topics|&lt;br /&gt;
      Array(topics).each { |topic_id| assign_topic_to_reviewer(reviewer_id, topic_id) }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign a single topic to a reviewer&lt;br /&gt;
  def assign_topic_to_reviewer(reviewer_id, topic_id)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic_id).pluck(:team_id).first&lt;br /&gt;
    return if team_to_review.nil?&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.create(&lt;br /&gt;
      reviewed_object_id: topic.assignment_id,&lt;br /&gt;
      reviewer_id: reviewer_id,&lt;br /&gt;
      reviewee_id: team_to_review,&lt;br /&gt;
      type: 'ReviewResponseMap'&lt;br /&gt;
    )&lt;br /&gt;
    # error handling&lt;br /&gt;
  rescue StandardError =&amp;gt; e&lt;br /&gt;
    Rails.logger.error(&amp;quot;Failed to assign topic #{topic_id} to reviewer #{reviewer_id}: #{e.message}&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
    raise ArgumentError, 'Topic or assignment is missing' if topic.nil? || topic.assignment_id.nil?&lt;br /&gt;
    raise ArgumentError, 'Matched topics must be a Hash' unless matched_topics.is_a?(Hash)&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
Once we change them to instance methods, we’ll need to modify their use in the code to instantiate the object first.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;Placeholder for implementation screenshots (before and after)&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-3.-schema.db-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;Schema.db&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We also need to remove the foreign key reference to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model since the &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model already has access to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;, and that is the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; we should be referencing. This will also involve modifying the database to remove the foreign key reference to the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Steps required:&lt;br /&gt;
&lt;br /&gt;
# Review the records in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table in the database to see if there are any records and if those records have values for &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;&lt;br /&gt;
# Create a migration file using the command: &amp;lt;code&amp;gt;rails generate migration RemoveAssignmentFromReviewBids&amp;lt;/code&amp;gt;&lt;br /&gt;
# In the generated file, indicate the change that needs to be made using the change method and migration columns &amp;lt;code&amp;gt;remove_foreign_key&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;remove_column&amp;lt;/code&amp;gt;. Migration should be straightforward since there is no cascading behavior (i.e., &amp;lt;code&amp;gt;dependent:destroy&amp;lt;/code&amp;gt;) to worry about.&lt;br /&gt;
# Once comfortable with the updates to the generated file, run the migration using the &amp;lt;code&amp;gt;rails db:migrate&amp;lt;/code&amp;gt; command.&lt;br /&gt;
# Run a query in the DB to ensure no orphaned records are in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table after the updates.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;Placeholder for implementation screenshots (before and after)&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-4.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implement Edge Case Testing ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| In the previous implementation wiki, there are edge cases which are not exhaustively tested. Should test those edge cases thoroughly and add more edge case testing - link&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span class=&amp;quot;mark&amp;quot;&amp;gt;TODO&amp;lt;/span&amp;gt;:We just got initial test results back. We still need to figure out why tests are failing and what may be slipping through the cracks.[[File:Test expertiza.png|411x459px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-8-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design Patterns and Principles ===&lt;br /&gt;
Below are the design patterns and principles we plan to use to address the problem statements outlined in the project requirements:&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
** Controller Responsibility&lt;br /&gt;
*** To address the issue of authentication logic residing in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, we will refactor the code to ensure the controller solely handles request and response flows.&lt;br /&gt;
*** Authentication logic will be moved to dedicated methods in &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt;, making the codebase easier to maintain and test.&lt;br /&gt;
** View Simplification&lt;br /&gt;
*** Currently, the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; view contains excessive business logic, violating SRP.&lt;br /&gt;
*** We will move this logic into helper methods or models, ensuring each component has a single responsibility and improving code readability.&lt;br /&gt;
&lt;br /&gt;
* Separation of Concerns (SoC)&lt;br /&gt;
** By relocating authorization logic out of the controller, we separate authentication from request handling.&lt;br /&gt;
** Adhering to SoC enhances readability and maintainability by allowing each part of the application to focus on its specific role.&lt;br /&gt;
* Don't Repeat Yourself (DRY)&lt;br /&gt;
** We will inspect the existing implementation for code duplication, particularly in the controller actions.&lt;br /&gt;
** Any repetitive code will be refactored or removed to reduce redundancy and improve clarity.&lt;br /&gt;
&lt;br /&gt;
* Model-View-Controller (MVC) Adherence&lt;br /&gt;
** Moving business logic from the view aligns with MVC best practices.&lt;br /&gt;
** By ensuring the view only presents data without processing it, we maintain clear boundaries between the model, view, and controller layers.&lt;br /&gt;
&lt;br /&gt;
* Dependency Inversion Principle (DIP)&lt;br /&gt;
** We plan to update class methods to instance methods, allowing the controller to depend on abstractions rather than concrete implementations.&lt;br /&gt;
** This change leverages Ruby's duck typing, providing flexibility and making the code easier to maintain and test.&lt;br /&gt;
&lt;br /&gt;
* Factory Pattern&lt;br /&gt;
** We will implement a factory pattern for the common processing of &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpBid&amp;lt;/code&amp;gt;, encapsulating object creation and promoting code reuse.&lt;br /&gt;
&lt;br /&gt;
* Law of Demeter (Principle of Least Knowledge)&lt;br /&gt;
** We'll delegate interactions appropriately to prevent tightly coupled code, especially when dealing with topics and teams.&lt;br /&gt;
** Clear and intentional naming during refactoring will enhance code clarity and maintainability.&lt;br /&gt;
&lt;br /&gt;
* Adapter Pattern&lt;br /&gt;
** To add messaging to bid states for users, we'll use the adapter pattern to allow different components to work together without modifying their existing code.&lt;br /&gt;
&lt;br /&gt;
== Files Modified/Added ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-1.-controllers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
review_bids_controller.rb&lt;br /&gt;
&lt;br /&gt;
signup_sheet_controller.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-2.-models&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
review_bid.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-3.-helpers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
&lt;br /&gt;
authorization_helper.rb&lt;br /&gt;
&lt;br /&gt;
Review_bids_helper.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;viii.-test-plan&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
The Excel spreadsheet below details our test plan for testing the code we changed or created to ensure code coverage. The process involved reviewing the code and determining all the possible scenarios that needed to be covered based on what the method was doing, then creating test cases accordingly.&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/spreadsheets/d/1iHzOdSwlTtxOtf5VsNdwOaCP8NzYMmiJ/edit?usp=drive_link Open Test Matrix Spreadsheet]&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-1.-mentor&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Ed Gehringer&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-2.-members&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Members ===&lt;br /&gt;
&lt;br /&gt;
* Gavin Teague&lt;br /&gt;
* Janice Uwujaren&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xi.-links&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
Previous Wiki: [https://wiki.expertiza.ncsu.edu/index.php/CSC/ECE_517_Fall_2021_-_E2151._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;Link&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xii.-references&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/document/d/1LHVacylQmB15I6VcYUA5nuC7GRRU9szHzZhjlPcE-PA/edit?tab=t.0#heading=h.u0oycoj6e0ev &amp;lt;u&amp;gt;Project Instructions&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;appendix-a-uml-class-diagram-plantuml-code-for-review-bids&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Juwujar</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160950</id>
		<title>CSC/ECE 517 Fall 2024 - E2485. Allow reviewers to bid on what to review</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160950"/>
		<updated>2024-12-10T15:06:05Z</updated>

		<summary type="html">&lt;p&gt;Juwujar: /* Updated Implementation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
This project will fix the defects and improve the functionality in Expertiza of the Reviewer Bidding feature, a process where reviewers can bid for assignments they want to review. It will make the functionality smoother, more reliable, and align with Expertiza's collaborative learning objectives.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iii.-problem-statements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Problem Statements ==&lt;br /&gt;
&lt;br /&gt;
Assigning reviews to users on Expertiza involves a complex bidding algorithm that requires optimization before production deployment. Below are key areas we plan to address as per the project requirements:&lt;br /&gt;
* Code Refactoring and Best Practices&lt;br /&gt;
** '''Utilize Authentication Utilities''': Modify the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; function in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to use existing authentication utilities, enhancing code reusability and adhering to the Single Responsibility Principle (SRP) and Separation of Concerns (SoC).&lt;br /&gt;
** '''Eliminate Code Duplication''': Refactor &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; to comply with the Don't Repeat Yourself (DRY) principle.&lt;br /&gt;
** '''Rename Functions for Clarity''': Change &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; to better reflect its purpose.&lt;br /&gt;
** '''Refactor Methods in '''&amp;lt;code&amp;gt;review_bid.rb&amp;lt;/code&amp;gt;''':''': Convert class methods to instance methods or relocate them to helper classes for improved code organization.&lt;br /&gt;
* Functionality Enhancements&lt;br /&gt;
** '''Handle Missing Bids Gracefully''': Adjust the bidding algorithm to accommodate students who do not bid, allowing them to select from the remaining options.&lt;br /&gt;
** '''Support Both Individual and Team Assignments''': Ensure the code functions correctly for participant and team-based assignments when signing up for topics.&lt;br /&gt;
* User Interface Improvements&lt;br /&gt;
** '''Increase Transparency in the Bidding Process''': Display messages indicating how many students are eligible to bid, how many have submitted bids, and the deadline.&lt;br /&gt;
** '''Accurate Bid Status Indicators''': Ensure topic colors change correctly based on the number of outstanding bids, providing clear visual feedback to users.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Exhaustive Edge Case Testing''': Conduct thorough testing for edge cases not previously covered, adding additional test cases as needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iv.-design-goals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Design Goals ==&lt;br /&gt;
&lt;br /&gt;
While addressing the existing defects and enhancing the Reviewer Bidding feature, we plan to adhere to the following design guidelines:&lt;br /&gt;
&lt;br /&gt;
* Code Quality and Maintainability&lt;br /&gt;
** '''Improve Code Structure: '''Implement refactoring strategies to enhance code readability and maintainability, ensuring adherence to best practices like the DRY principle.&lt;br /&gt;
** '''Standardize Authentication Logic: '''Utilize a centralized authentication utility to promote consistency and reusability across the application.&lt;br /&gt;
* System Stability and Flexibility&lt;br /&gt;
** '''Enhance Algorithm Robustness: '''Modify the bidding algorithm to handle scenarios where students do not bid, ensuring the system remains stable under all conditions.&lt;br /&gt;
** '''Ensure Assignment Versatility: '''Design the system to seamlessly support both individual and team assignments without additional configuration.&lt;br /&gt;
* User Experience Enhancements&lt;br /&gt;
** '''Increase Bidding Transparency: '''Develop user interface elements that clearly display bidding status, deadlines, and participation metrics.&lt;br /&gt;
** '''Implement Dynamic Visual Feedback: '''Use real-time visual cues, such as color changes, to inform users about the bidding status instantly.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Develop Comprehensive Test Suites: '''Create extensive automated tests covering edge cases and critical functionalities to guarantee reliable system behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;v.-uml-diagram&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== UML Diagram ==&lt;br /&gt;
&lt;br /&gt;
Below is a visual representation of the models associated with the review bids functionality, depicting the data structures and their relationships.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml class diagram2.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi.-implementation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
'''ReviewBid: '''The &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; manages the bidding process, where participants indicate their preferences for reviewing different topics or assignments. This helps distribute review tasks fairly based on participants' interests.&lt;br /&gt;
&lt;br /&gt;
'''Assignment: '''The &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model represents team or individual assignments in Expertiza. It stores all the essential information required to keep the assignment process organized, like managing participants, setting due dates, forming teams, and overseeing the review process.&lt;br /&gt;
&lt;br /&gt;
'''SignUpTopic: '''The &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt; model identifies specific topics or areas within an assignment for which participants can sign up. It manages the availability of these topics, tracks bids, coordinates team sign-ups, and assigns topics based on both bids and participant availability. &lt;br /&gt;
&lt;br /&gt;
'''Participant: '''The &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model is about the individual users who participate in an assignment. This includes students, instructors, teaching assistants, or other relevant roles. This model is responsible for managing key user information, monitoring interactions with the assignment, keeping track of team memberships, and recording things like badges and review grades.&lt;br /&gt;
&lt;br /&gt;
== Implementation ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1.-decoupling-authorization-logic-from-reviewbidscontroller&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Decoupling Authorization Logic from &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| '''&amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; needs to use the authentication utilities'''&lt;br /&gt;
&lt;br /&gt;
The function &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; should use the available authentication utilities to support the Single Responsibility Principle and Separation of Concerns and to support code reusability.&lt;br /&gt;
&lt;br /&gt;
| '''Authentication Consistency:''' Remove authentication logic in the controller and call logic in the authentication utility class instead to ensure consistent use of the same logic across the application, foster reusability, and eliminate DRY.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
==== Authorization Flow Before Refactoring ====&lt;br /&gt;
The sequence diagram depicts the authorization process within the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;. When a user invokes an action, the controller checks if the action is allowed by verifying the user's role through the &amp;lt;code&amp;gt;current_role_name()&amp;lt;/code&amp;gt; method in the &amp;lt;code&amp;gt;ApplicationController&amp;lt;/code&amp;gt;. Depending on the action and the user's role, the controller either authorizes the user to proceed or denies access, ultimately executing the action logic if authorized and responding to the user.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 1.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We simplified the &amp;lt;code&amp;gt;action_allowed?&amp;lt;/code&amp;gt; method by decoupling authorization logic from the controller to use helper methods in the &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt; module. The &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; should not handle authentication, which is a violation of SRP. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;vertical-align:top;&amp;quot; | Original Code !! style=&amp;quot;vertical-align:top;&amp;quot; | Refactored Code&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index'&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator',&lt;br /&gt;
     'Student'].include? current_role_name and&lt;br /&gt;
        ((%w[list].include? action_name) ? are_needed_authorizations_present?(params[:id], &amp;quot;participant&amp;quot;, &amp;quot;reader&amp;quot;, &amp;quot;submitter&amp;quot;, &amp;quot;reviewer&amp;quot;) : true)&lt;br /&gt;
  else&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator'].include? current_role_name&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index', 'list'&lt;br /&gt;
    current_user_has_student_privileges? &amp;amp;&amp;amp; list_authorization_check&lt;br /&gt;
  else&lt;br /&gt;
    current_user_has_ta_privileges?&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; action ===&lt;br /&gt;
The refactor of the &amp;lt;code&amp;gt;ReviewBidsController's index&amp;lt;/code&amp;gt; action simplifies and streamlines the codebase, ensuring the action simply handles controller activities. Previously, the controller was overloaded with responsibilities—it handled everything from locating and validating assignments to fetching review mappings and counting completed reviews. This all-in-one approach made the controller action messy. &lt;br /&gt;
&lt;br /&gt;
Now we have moved the business logic out of the controller action and into dedicated service objects. With this change, we introduced two key service objects: &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; focuses on retrieving the participant along with their assignments, while the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; takes on the task of managing review mappings and calculating review-related stats, like how many reviews have been completed. &lt;br /&gt;
&lt;br /&gt;
This clear separation of responsibilities aligns with the Single Responsibility Principle, which helps each part of our code be more focused and easier to understand. With these new service objects, the code has become more reusable. The logic for fetching assignments, retrieving review mappings, and counting reviews is now more flexible and reusable throughout the application. For example, we used methods in the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; in both the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; and the &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;, helping to avoid code duplication.&lt;br /&gt;
&lt;br /&gt;
After moving the business logic to service objects, we can write unit tests to ensure each part of the code behaves as expected and prevents bugs. We also moved complex calculations, like those for completed reviews, into the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;, freeing the controller from dealing with the implementation details and promoting a cleaner design. The article https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial provides more information on when and how to use service objects in Rails.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
  return unless current_user_id?(@participant.user_id)&lt;br /&gt;
  @assignment = @participant.assignment&lt;br /&gt;
  @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
  # Finding how many reviews have been completed&lt;br /&gt;
  @num_reviews_completed = 0&lt;br /&gt;
  @review_mappings.each do |map|&lt;br /&gt;
    @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # render view for completing reviews after review bidding has been completed&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @assignment = participant_service.assignment&lt;br /&gt;
  unless @assignment.is_a?(Assignment)&lt;br /&gt;
    flash[:error] = 'Assignment not found.'&lt;br /&gt;
    redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  @review_mappings = review_service.review_mappings&lt;br /&gt;
  @num_reviews_completed = review_service.review_counts[:completed]&lt;br /&gt;
&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Optimize &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Action functionality ===&lt;br /&gt;
In the earlier version of the set_priority action in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, the server only needed a couple of key pieces of information: an array of topic IDs (params[:topic]) and the participant ID (params[:id}). From there, it would figure out the assignment_id by using the first topic as a reference. While this worked in simple cases, it could lead to problems if any data was missing or didn’t line up correctly.&lt;br /&gt;
&lt;br /&gt;
With the new implementation, the action now expects clear and structured parameters, including participant_id, an array of topic IDs, and assignment_id. This change ensures that the server always receives complete and reliable information, preventing issues caused by missing or inconsistent data. It also removes the need for the server to perform unnecessary lookups, allowing the controller to focus on its main job: validating inputs (like making sure the topics in params[:topic] belong to the same assignment as the participant), updating the database, handling errors properly, and sending the correct response back to the view.&lt;br /&gt;
&lt;br /&gt;
While these updates have cleaned up the set_priority action, they still contain a lot of business logic, like validations and priority updates. To make the code even more maintainable, there’s room for further refactoring—perhaps moving some of this logic into a service class or model method to streamline the controller even more.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | Old Code&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    if params[:topic].nil?&lt;br /&gt;
      ReviewBid.where(participant_id: params[:id]).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      participant = AssignmentParticipant.find_by(id: params[:id])&lt;br /&gt;
      assignment_id = SignUpTopic.find(params[:topic].first).assignment.id&lt;br /&gt;
      @bids = ReviewBid.where(participant_id: params[:id])&lt;br /&gt;
      signed_up_topics = ReviewBid.where(participant_id: params[:id]).map(&amp;amp;:signuptopic_id)&lt;br /&gt;
      signed_up_topics -= params[:topic].map(&amp;amp;:to_i)&lt;br /&gt;
      signed_up_topics.each do |topic|&lt;br /&gt;
        ReviewBid.where(signuptopic_id: topic, participant_id: params[:id]).destroy_all&lt;br /&gt;
      end&lt;br /&gt;
      params[:topic].each_with_index do |topic_id, index|&lt;br /&gt;
        bid_existence = ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id])&lt;br /&gt;
        if bid_existence.empty?&lt;br /&gt;
          ReviewBid.create(priority: index + 1, signuptopic_id: topic_id, participant_id: params[:id], assignment_id: assignment_id)&lt;br /&gt;
        else&lt;br /&gt;
          ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id]).update_all(priority: index + 1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to action: 'show', assignment_id: params[:assignment_id], id: params[:id]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    participant_id = params[:participant_id].to_i&lt;br /&gt;
    selected_topic_ids = params[:topic]&amp;amp;.map(&amp;amp;:to_i) || []&lt;br /&gt;
    assignment_id = params[:assignment_id].to_i&lt;br /&gt;
&lt;br /&gt;
    unless current_user_can_modify_participant?(participant_id)&lt;br /&gt;
      flash[:error] = 'You are not authorized to modify these bids.'&lt;br /&gt;
      respond_to do |format|&lt;br /&gt;
        format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
        format.json { render json: { status: 'unauthorized' }, status: :unauthorized }&lt;br /&gt;
      end&lt;br /&gt;
      return&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    if selected_topic_ids.empty?&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      assignment = Assignment.find_by(id: assignment_id)&lt;br /&gt;
      if assignment.nil?&lt;br /&gt;
        flash[:error] = &amp;quot;Invalid assignment.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_assignment' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      unless SignUpTopic.where(id: selected_topic_ids, assignment_id: assignment_id).count == selected_topic_ids.size&lt;br /&gt;
        flash[:error] = &amp;quot;One or more selected topics are invalid.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_topics' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).where.not(signuptopic_id: selected_topic_ids).destroy_all&lt;br /&gt;
&lt;br /&gt;
      selected_topic_ids.each_with_index do |topic_id, index|&lt;br /&gt;
        bid = ReviewBid.find_or_initialize_by(signuptopic_id: topic_id, participant_id: participant_id)&lt;br /&gt;
        bid.priority = index + 1&lt;br /&gt;
        bid.assignment_id = assignment_id&lt;br /&gt;
        bid.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_to action: 'show', assignment_id: assignment_id, id: participant_id, notice: 'Review bids updated successfully.' }&lt;br /&gt;
      format.json { render json: { status: 'success' }, status: :ok }&lt;br /&gt;
    end&lt;br /&gt;
  rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
    flash[:error] = &amp;quot;Failed to update priorities: #{e.message}&amp;quot;&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
      format.json { render json: { status: 'error', message: e.message }, status: :unprocessable_entity }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Client Side Changes to Support &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Changes ====&lt;br /&gt;
After tidying up the server-side logic, we needed to refactor the client-side code to ensure it passed the correct information from the view to the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action. On the client side, we now grab the topicIDs and send them as &amp;lt;code&amp;gt;selected_topic_ids&amp;lt;/code&amp;gt;, allowing the controller to figure out which topics were selected and in what order. It’s a cleaner and more reliable way to keep everything working together! &lt;br /&gt;
&lt;br /&gt;
We are also extracting the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, making it a lot easier to send data with AJAX and keeping things flexible. AJAX is awesome because it lets us update the server without reloading the page, making the user experience smooth and responsive. When we pull the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, we can grab it along with other data, like the list of topics, and include it in the request. This way, we don’t have to rely on the URL to know which participant we’re working with, which makes the code cleaner and more adaptable.&lt;br /&gt;
&lt;br /&gt;
If we were to rely on the route for the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt;, every AJAX request would need to be tied to a specific URL structure. That’s limiting, especially if the route doesn’t naturally include the participant ID, as can be the case with collection routes versus member routes in Rails. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery -&amp;gt;&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: -&amp;gt;&lt;br /&gt;
      $.post($(this).data('update-url'), $(this).sortable('serialize'))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery(document).ready ($) -&amp;gt;&lt;br /&gt;
  sendUpdatedOrder = -&amp;gt;&lt;br /&gt;
    sortedIDs = $(this).sortable('toArray')&lt;br /&gt;
&lt;br /&gt;
    topicIDs = sortedIDs.map (id) -&amp;gt;&lt;br /&gt;
      id.replace 'topic_', ''&lt;br /&gt;
&lt;br /&gt;
    participant_id = $(this).data('participant-id')&lt;br /&gt;
&lt;br /&gt;
    assignment_id = new URL($(this).data('update-url'), window.location.origin).searchParams.get('assignment_id')&lt;br /&gt;
&lt;br /&gt;
    $.ajax&lt;br /&gt;
      type: 'POST'&lt;br /&gt;
      url: $(this).data('update-url')&lt;br /&gt;
      data:&lt;br /&gt;
        topic: topicIDs&lt;br /&gt;
        participant_id: participant_id&lt;br /&gt;
        assignment_id: assignment_id&lt;br /&gt;
      success: (response) -&amp;gt;&lt;br /&gt;
        console.log 'Selections updated successfully.'&lt;br /&gt;
      error: (xhr, status, error) -&amp;gt;&lt;br /&gt;
        alert 'An error occurred while saving your selections. Please try again.'&lt;br /&gt;
        console.error error&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: sendUpdatedOrder&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-1.-signupsheetcontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; Updates ===&lt;br /&gt;
&lt;br /&gt;
The ReviewBidsController's index method renders the review_bids_others_work action in &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt;. However, the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; does not include the controller action for this view. We need to find the old implementation of the controller action, add it back, and review and update it for any DRY violations.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # provides variables for reviewing page located at views/review_bids/others_work.html.erb&lt;br /&gt;
  def index&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
    return unless current_user_id?(@participant.user_id)&lt;br /&gt;
&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
    # Finding how many reviews have been completed&lt;br /&gt;
    @num_reviews_completed = 0&lt;br /&gt;
    @review_mappings.each do |map|&lt;br /&gt;
      @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # render view for completing reviews after review bidding has been completed&lt;br /&gt;
    render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
  end  &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-2.-reviewbidsotherswork-view-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;review_bids_others_work.html.erb&amp;lt;/code&amp;gt; View Updates ====&lt;br /&gt;
&lt;br /&gt;
The code only contains a view with this name and no associated controller action. Reviewing the code in the view, we noticed some SRP violations where there is much logic in the presentation layer. Set up helper methods in the controller to handle some of that logic or see if the model can handle some of that logic.&lt;br /&gt;
&lt;br /&gt;
Note: The view is technically part of the SignUpSheetController, but it's being rendered by the ReviewBidsController, which initially threw us off. After chatting with the professor, we decided not to change this view. He indicated that the plan is to reuse these views for both assignment bidding and review bidding.&lt;br /&gt;
&lt;br /&gt;
We would suggest building the assignment bidding and review bidding features separately first to make sure each one works on its own. Once that’s done, we can go back and clean things up by combining any shared functionality. This could include creating shared views or even introducing an abstract model to handle both types of bidding in a cleaner way. It could also mean replacing the ReviewBidsController with something more generic, like a BidsController, to keep things reusable and easier to maintain down the line.&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Rename &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; method ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; should be &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt;&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:''' Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The primary activity here is to refactor the method name &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; and fix the hardcoded URL. However, the match topic bidding algorithm no longer works since Heroku is no longer free, per the professor. The reference link below has detailed information about the web service that we can leverage to gain that understanding.&lt;br /&gt;
&lt;br /&gt;
Reference Link for Webservice Details: [https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Updated Implementation ====&lt;br /&gt;
The original requirement was to rename the run_bidding_algorithm method to assign_reviewers, but after digging into the code, we realized that renaming it didn’t make sense. The method runs the bidding algorithm to match topics, and there’s already a separate method to handle assigning review topics.&lt;br /&gt;
&lt;br /&gt;
However, we improved the logic in the assign_bidding method (which is called by the run_bidding_algorithm) by introducing a separate BiddingAlgorithmService object. This keeps the business logic out of the controller action, which makes things cleaner and more maintainable.&lt;br /&gt;
&lt;br /&gt;
==== review_bids_controller ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def run_bidding_algorithm&lt;br /&gt;
    review_bid = ReviewBid.new&lt;br /&gt;
    bidding_data = review_bid.bidding_data&lt;br /&gt;
    matched_topics = BiddingAlgorithmService.new(bidding_data).run&lt;br /&gt;
    raise ArgumentError, 'Failed to assign reviewers. Please try again later.' unless matched_topics&lt;br /&gt;
&lt;br /&gt;
    matched_topics&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== bidding_algorithm_service ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# frozen_string_literal: true&lt;br /&gt;
&lt;br /&gt;
# The `BiddingAlgorithmService` run the bid assignment algorithm&lt;br /&gt;
# Sends student IDs, topic IDs, student preferences, and timestamps to the web service&lt;br /&gt;
# The web service returns the matched assignments in the JSON response body&lt;br /&gt;
class BiddingAlgorithmService&lt;br /&gt;
  SERVICE_URL = 'http://app-csc517.herokuapp.com/match_topics'.freeze&lt;br /&gt;
&lt;br /&gt;
  # MOCK_DATA is based on the documentation detailing how the webservice behaves.&lt;br /&gt;
  # Reference: https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review#Webservice&lt;br /&gt;
  MOCK_DATA = {&lt;br /&gt;
    36239 =&amp;gt; [3970, 3972, 3975],&lt;br /&gt;
    36240 =&amp;gt; [3973, 3974, 3972],&lt;br /&gt;
    36241 =&amp;gt; [3969, 3971, 3972],&lt;br /&gt;
    36242 =&amp;gt; [3969, 3971, 3973],&lt;br /&gt;
    36243 =&amp;gt; [3969, 3970, 3971]&lt;br /&gt;
  }.freeze&lt;br /&gt;
&lt;br /&gt;
  def initialize(bidding_data)&lt;br /&gt;
    @bidding_data = bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def run&lt;br /&gt;
    return MOCK_DATA if Rails.application.config.use_mock_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
    perform_request&lt;br /&gt;
  rescue RestClient::ExceptionWithResponse, JSON::ParserError&lt;br /&gt;
    false&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def perform_request&lt;br /&gt;
    response = RestClient.post(&lt;br /&gt;
      SERVICE_URL,&lt;br /&gt;
      @bidding_data.to_json,&lt;br /&gt;
      content_type: :json,&lt;br /&gt;
      accept: :json&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    validate_response(response)&lt;br /&gt;
    JSON.parse(response.body)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def validate_response(response)&lt;br /&gt;
    raise 'Invalid response format' unless response.headers[:content_type] &amp;amp;&amp;amp; response.headers[:content_type].include?('application/json')&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Resolve Functionality Issues for Non Bidders ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Currently it doesn't work if some student does not bid. In this case, algorithm needs to be fixed to ignore anyone who didn’t bid, and let them choose from what’s left over.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to do several things within this controller. We will set a default state for non-bidders that we can point to to give them options to select topics to review from the leftovers. We will consolidate &amp;lt;code&amp;gt;@sign_up_topics&amp;lt;/code&amp;gt; to resolve the DRY violation. Lastly, we will remove business logic from the show method and push it to a new helper to conform to the SRP.&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # Additional method&lt;br /&gt;
&lt;br /&gt;
  # Provides variables for review bidding page&lt;br /&gt;
  def show&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id].to_i)&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @sign_up_topics = SignUpTopic.where(assignment_id: @assignment.id, private_to: nil)&lt;br /&gt;
    my_topic = SignedUpTeam.topic_id(@participant.parent_id, @participant.user_id)&lt;br /&gt;
    @sign_up_topics -= SignUpTopic.where(assignment_id: @assignment.id, id: my_topic)&lt;br /&gt;
    @num_participants = AssignmentParticipant.where(parent_id: @assignment.id).count&lt;br /&gt;
    @selected_topics = nil # This is used to list the topics assigned to review (i.e., select == assigned)&lt;br /&gt;
    @bids = ReviewBid.where(participant_id: @participant, assignment_id: @assignment.id)&lt;br /&gt;
    signed_up_topics = []&lt;br /&gt;
    &lt;br /&gt;
    @bids.each do |bid|&lt;br /&gt;
      sign_up_topic = SignUpTopic.find_by(id: bid.signuptopic_id)&lt;br /&gt;
      signed_up_topics &amp;lt;&amp;lt; sign_up_topic if sign_up_topic&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    signed_up_topics &amp;amp;= @sign_up_topics&lt;br /&gt;
    @sign_up_topics -= signed_up_topics&lt;br /&gt;
    @bids = signed_up_topics&lt;br /&gt;
    @num_of_topics = @sign_up_topics.size&lt;br /&gt;
    @assigned_review_maps = []&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: @assignment.id, reviewer_id: @participant.id).each do |review_map|&lt;br /&gt;
      @assigned_review_maps &amp;lt;&amp;lt; review_map&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Explicitly render view since it's in the sign up sheet views&lt;br /&gt;
    render 'sign_up_sheet/review_bids_show'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Additional methods&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-2.-reviewbidshelper-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
 # Assigns bidding topics to reviewers&lt;br /&gt;
  def assign_bidding&lt;br /&gt;
      assignment = validate_assignment(params[:assignment_id])&lt;br /&gt;
&lt;br /&gt;
      reviewers = validate_reviewers(assignment.id)&lt;br /&gt;
      reviewer_ids = reviewers.map(&amp;amp;:id)&lt;br /&gt;
&lt;br /&gt;
      matched_topics = run_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
      if matched_topics.blank?&lt;br /&gt;
        flash[:alert] = 'Topic or assignment is missing'&lt;br /&gt;
        redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      leftover_topics = find_leftover_topics(assignment.id, matched_topics)&lt;br /&gt;
      assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.new.assign_review_topics(matched_topics)&lt;br /&gt;
      assignment.update!(can_choose_topic_to_review: false)&lt;br /&gt;
&lt;br /&gt;
      flash[:notice] = 'Reviewers were successfully assigned to topics.'&lt;br /&gt;
      redirect_back fallback_location: root_path&lt;br /&gt;
    rescue ArgumentError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ArgumentError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: e.message&lt;br /&gt;
    rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::RecordInvalid: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue ActiveRecord::ActiveRecordError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::ActiveRecordError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue StandardError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;StandardError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers. Please try again later.'&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  #service methods here ...&lt;br /&gt;
&lt;br /&gt;
  def find_leftover_topics(assignment_id, matched_topics)&lt;br /&gt;
    all_topic_ids = SignUpTopic.where(assignment_id: assignment_id).pluck(:id)&lt;br /&gt;
    assigned_topic_ids = matched_topics.map { |match| match[:topic_id] }&lt;br /&gt;
&lt;br /&gt;
    # Calculate leftover topics&lt;br /&gt;
    all_topic_ids - assigned_topic_ids&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
    return if leftover_topics.blank?&lt;br /&gt;
&lt;br /&gt;
    # Find non-bidders by excluding already matched reviewers&lt;br /&gt;
    non_bidders = reviewer_ids - matched_topics.keys&lt;br /&gt;
&lt;br /&gt;
    # Assign leftover topics to non-bidders in a round-robin fashion&lt;br /&gt;
    non_bidders.each_with_index do |reviewer_id, index|&lt;br /&gt;
      topic_id = leftover_topics[index % leftover_topics.length]&lt;br /&gt;
      ReviewBid.create(priority: 1, signuptopic_id: topic_id, participant_id: reviewer_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirm Functionality for Single-person Teams ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Make sure your code works on individual assignments, i.e., assignments where participants sign up for topics instead of teams. In this case, a team is created for each participant when they sign up. So the code should work for assignments to which either individuals or teams submit.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to make an update to focus on topic rather than team or have it where teams are updated based on topic id.We will probably utilize sign up topic or a similar object to accomplish this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-5-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bid Status Messaging ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| To make the functionality more intuitive, include a message to say how many students are eligible to submit bids, how many have submitted their bids, when the deadline for submitting bids is.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
The logic for these messages will go into the helper file. It is effectively the same reasoning as our migration of other business/functionality code from the controller to the helper to conform to the SRP. It will go hand in hand with the SignUpTopic portion of the code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-6-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Why are the methods in review_bid.rb class methods? Can we change them to instance methods or move it to helpers?&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-1.-reviewbid-model-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model Updates ====&lt;br /&gt;
Since the current class methods are tied to &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model instances, we will change them to instance methods. There is no need to move them to helpers.&lt;br /&gt;
&lt;br /&gt;
===== Review Bid Model Flow Before Refactoring =====&lt;br /&gt;
This sequence diagram describes how the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; interacts with the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model in assigning review topics to reviewers according to their bids. Once the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; action is initiated by the user, the controller gathers the IDs of reviewers and calls &amp;lt;code&amp;gt;ReviewBid.bidding_data&amp;lt;/code&amp;gt; to gather bidding information. That is sent out to an external bidding algorithm, and the matched topics that come back are used by &amp;lt;code&amp;gt;ReviewBid.assign_review_topics&amp;lt;/code&amp;gt; to create &amp;lt;code&amp;gt;ReviewResponseMap&amp;lt;/code&amp;gt; entries, assigning topics to reviewers for the assignment.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
  belongs_to :assignment, class_name: 'Assignment'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  def self.bidding_data(assignment_id, reviewer_ids)&lt;br /&gt;
    # create basic hash and set basic hash data&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'users' =&amp;gt; {}, 'max_accepted_proposals' =&amp;gt; [] }&lt;br /&gt;
    bidding_data['tid'] = SignUpTopic.where(assignment_id: assignment_id).ids&lt;br /&gt;
    bidding_data['max_accepted_proposals'] = Assignment.where(id: assignment_id).pluck(:num_reviews_allowed).first&lt;br /&gt;
&lt;br /&gt;
    # loop through reviewer_ids to get reviewer specific bidding data&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      bidding_data['users'][reviewer_id] = reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # assigns topics to reviews as matched by the webservice algorithm&lt;br /&gt;
  def self.assign_review_topics(assignment_id, reviewer_ids, matched_topics, _min_num_reviews = 2)&lt;br /&gt;
    # if review response map already created, delete it&lt;br /&gt;
    if ReviewResponseMap.where(reviewed_object_id: assignment_id)&lt;br /&gt;
      ReviewResponseMap.where(reviewed_object_id: assignment_id).destroy_all&lt;br /&gt;
    end&lt;br /&gt;
    # loop through reviewer_ids to assign reviews to each reviewer&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      topics_to_assign = matched_topics[reviewer_id.to_s]&lt;br /&gt;
      topics_to_assign.each do |topic|&lt;br /&gt;
        assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method to assign a single topic to a reviewer&lt;br /&gt;
  def self.assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic).pluck(:team_id).first&lt;br /&gt;
    team_to_review.nil? ? [] : ReviewResponseMap.create(reviewed_object_id: assignment_id, reviewer_id: reviewer_id, reviewee_id: team_to_review, type: 'ReviewResponseMap')&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method for getting individual reviewer_ids bidding data&lt;br /&gt;
  # returns user's bidding data hash&lt;br /&gt;
  def self.reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    reviewer_user_id = AssignmentParticipant.find(reviewer_id).user_id&lt;br /&gt;
    self_topic = SignedUpTeam.topic_id(assignment_id, reviewer_user_id)&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'otid' =&amp;gt; self_topic, 'priority' =&amp;gt; [], 'time' =&amp;gt; [] }&lt;br /&gt;
    bids = ReviewBid.where(participant_id: reviewer_id)&lt;br /&gt;
&lt;br /&gt;
    # loop through each bid for a topic to get specific data&lt;br /&gt;
    bids.each do |bid|&lt;br /&gt;
      bidding_data['tid'] &amp;lt;&amp;lt; bid.signuptopic_id&lt;br /&gt;
      bidding_data['priority'] &amp;lt;&amp;lt; bid.priority&lt;br /&gt;
      bidding_data['time'] &amp;lt;&amp;lt; bid.updated_at&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  # Instance method to get bidding data for this review bid&lt;br /&gt;
  def bidding_data&lt;br /&gt;
    {&lt;br /&gt;
      'tid' =&amp;gt; topic_ids,&lt;br /&gt;
      'otid' =&amp;gt; self_topic,&lt;br /&gt;
      'priority' =&amp;gt; priorities,&lt;br /&gt;
      'time' =&amp;gt; updated_times&lt;br /&gt;
    }&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # bid object to limit db touches for similar data acquisitions&lt;br /&gt;
  def bids&lt;br /&gt;
    @bids ||= ReviewBid.where(participant_id: participant_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def topic_ids&lt;br /&gt;
    bids.pluck(:signuptopic_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def priorities&lt;br /&gt;
    bids.pluck(:priority)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def updated_times&lt;br /&gt;
    bids.pluck(:updated_at)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self_topic&lt;br /&gt;
    return nil unless topic &amp;amp;&amp;amp; topic.assignment_id&lt;br /&gt;
&lt;br /&gt;
    SignedUpTeam.topic_id(topic.assignment_id, participant.user_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign topics to reviews&lt;br /&gt;
  def assign_review_topics(matched_topics)&lt;br /&gt;
    validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: topic.assignment_id).destroy_all&lt;br /&gt;
&lt;br /&gt;
    matched_topics.each do |reviewer_id, topics|&lt;br /&gt;
      Array(topics).each { |topic_id| assign_topic_to_reviewer(reviewer_id, topic_id) }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign a single topic to a reviewer&lt;br /&gt;
  def assign_topic_to_reviewer(reviewer_id, topic_id)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic_id).pluck(:team_id).first&lt;br /&gt;
    return if team_to_review.nil?&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.create(&lt;br /&gt;
      reviewed_object_id: topic.assignment_id,&lt;br /&gt;
      reviewer_id: reviewer_id,&lt;br /&gt;
      reviewee_id: team_to_review,&lt;br /&gt;
      type: 'ReviewResponseMap'&lt;br /&gt;
    )&lt;br /&gt;
    # error handling&lt;br /&gt;
  rescue StandardError =&amp;gt; e&lt;br /&gt;
    Rails.logger.error(&amp;quot;Failed to assign topic #{topic_id} to reviewer #{reviewer_id}: #{e.message}&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
    raise ArgumentError, 'Topic or assignment is missing' if topic.nil? || topic.assignment_id.nil?&lt;br /&gt;
    raise ArgumentError, 'Matched topics must be a Hash' unless matched_topics.is_a?(Hash)&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
Once we change them to instance methods, we’ll need to modify their use in the code to instantiate the object first.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;Placeholder for implementation screenshots (before and after)&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-3.-schema.db-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;Schema.db&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We also need to remove the foreign key reference to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model since the &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model already has access to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;, and that is the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; we should be referencing. This will also involve modifying the database to remove the foreign key reference to the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Steps required:&lt;br /&gt;
&lt;br /&gt;
# Review the records in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table in the database to see if there are any records and if those records have values for &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;&lt;br /&gt;
# Create a migration file using the command: &amp;lt;code&amp;gt;rails generate migration RemoveAssignmentFromReviewBids&amp;lt;/code&amp;gt;&lt;br /&gt;
# In the generated file, indicate the change that needs to be made using the change method and migration columns &amp;lt;code&amp;gt;remove_foreign_key&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;remove_column&amp;lt;/code&amp;gt;. Migration should be straightforward since there is no cascading behavior (i.e., &amp;lt;code&amp;gt;dependent:destroy&amp;lt;/code&amp;gt;) to worry about.&lt;br /&gt;
# Once comfortable with the updates to the generated file, run the migration using the &amp;lt;code&amp;gt;rails db:migrate&amp;lt;/code&amp;gt; command.&lt;br /&gt;
# Run a query in the DB to ensure no orphaned records are in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table after the updates.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;Placeholder for implementation screenshots (before and after)&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-4.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implement Edge Case Testing ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| In the previous implementation wiki, there are edge cases which are not exhaustively tested. Should test those edge cases thoroughly and add more edge case testing - link&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span class=&amp;quot;mark&amp;quot;&amp;gt;TODO&amp;lt;/span&amp;gt;:We just got initial test results back. We still need to figure out why tests are failing and what may be slipping through the cracks.[[File:Test expertiza.png|411x459px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-8-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design Patterns and Principles ===&lt;br /&gt;
Below are the design patterns and principles we plan to use to address the problem statements outlined in the project requirements:&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
** Controller Responsibility&lt;br /&gt;
*** To address the issue of authentication logic residing in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, we will refactor the code to ensure the controller solely handles request and response flows.&lt;br /&gt;
*** Authentication logic will be moved to dedicated methods in &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt;, making the codebase easier to maintain and test.&lt;br /&gt;
** View Simplification&lt;br /&gt;
*** Currently, the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; view contains excessive business logic, violating SRP.&lt;br /&gt;
*** We will move this logic into helper methods or models, ensuring each component has a single responsibility and improving code readability.&lt;br /&gt;
&lt;br /&gt;
* Separation of Concerns (SoC)&lt;br /&gt;
** By relocating authorization logic out of the controller, we separate authentication from request handling.&lt;br /&gt;
** Adhering to SoC enhances readability and maintainability by allowing each part of the application to focus on its specific role.&lt;br /&gt;
* Don't Repeat Yourself (DRY)&lt;br /&gt;
** We will inspect the existing implementation for code duplication, particularly in the controller actions.&lt;br /&gt;
** Any repetitive code will be refactored or removed to reduce redundancy and improve clarity.&lt;br /&gt;
&lt;br /&gt;
* Model-View-Controller (MVC) Adherence&lt;br /&gt;
** Moving business logic from the view aligns with MVC best practices.&lt;br /&gt;
** By ensuring the view only presents data without processing it, we maintain clear boundaries between the model, view, and controller layers.&lt;br /&gt;
&lt;br /&gt;
* Dependency Inversion Principle (DIP)&lt;br /&gt;
** We plan to update class methods to instance methods, allowing the controller to depend on abstractions rather than concrete implementations.&lt;br /&gt;
** This change leverages Ruby's duck typing, providing flexibility and making the code easier to maintain and test.&lt;br /&gt;
&lt;br /&gt;
* Factory Pattern&lt;br /&gt;
** We will implement a factory pattern for the common processing of &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpBid&amp;lt;/code&amp;gt;, encapsulating object creation and promoting code reuse.&lt;br /&gt;
&lt;br /&gt;
* Law of Demeter (Principle of Least Knowledge)&lt;br /&gt;
** We'll delegate interactions appropriately to prevent tightly coupled code, especially when dealing with topics and teams.&lt;br /&gt;
** Clear and intentional naming during refactoring will enhance code clarity and maintainability.&lt;br /&gt;
&lt;br /&gt;
* Adapter Pattern&lt;br /&gt;
** To add messaging to bid states for users, we'll use the adapter pattern to allow different components to work together without modifying their existing code.&lt;br /&gt;
&lt;br /&gt;
== Files Modified/Added ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-1.-controllers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
review_bids_controller.rb&lt;br /&gt;
&lt;br /&gt;
signup_sheet_controller.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-2.-models&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
review_bid.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-3.-helpers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
&lt;br /&gt;
authorization_helper.rb&lt;br /&gt;
&lt;br /&gt;
Review_bids_helper.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;viii.-test-plan&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
The Excel spreadsheet below details our test plan for testing the code we changed or created to ensure code coverage. The process involved reviewing the code and determining all the possible scenarios that needed to be covered based on what the method was doing, then creating test cases accordingly.&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/spreadsheets/d/1iHzOdSwlTtxOtf5VsNdwOaCP8NzYMmiJ/edit?usp=drive_link Open Test Matrix Spreadsheet]&lt;br /&gt;
&lt;br /&gt;
== Test Coverage ==&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;screenshot of SimpleCov coverage analysis&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x.-team&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-1.-mentor&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Ed Gehringer&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-2.-members&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Members ===&lt;br /&gt;
&lt;br /&gt;
* Gavin Teague&lt;br /&gt;
* Janice Uwujaren&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xi.-links&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
Previous Wiki: [https://wiki.expertiza.ncsu.edu/index.php/CSC/ECE_517_Fall_2021_-_E2151._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;Link&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xii.-references&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/document/d/1LHVacylQmB15I6VcYUA5nuC7GRRU9szHzZhjlPcE-PA/edit?tab=t.0#heading=h.u0oycoj6e0ev &amp;lt;u&amp;gt;Project Instructions&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;appendix-a-uml-class-diagram-plantuml-code-for-review-bids&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Juwujar</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160949</id>
		<title>CSC/ECE 517 Fall 2024 - E2485. Allow reviewers to bid on what to review</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160949"/>
		<updated>2024-12-10T15:04:50Z</updated>

		<summary type="html">&lt;p&gt;Juwujar: /* ReviewBidsController Updates */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
This project will fix the defects and improve the functionality in Expertiza of the Reviewer Bidding feature, a process where reviewers can bid for assignments they want to review. It will make the functionality smoother, more reliable, and align with Expertiza's collaborative learning objectives.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iii.-problem-statements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Problem Statements ==&lt;br /&gt;
&lt;br /&gt;
Assigning reviews to users on Expertiza involves a complex bidding algorithm that requires optimization before production deployment. Below are key areas we plan to address as per the project requirements:&lt;br /&gt;
* Code Refactoring and Best Practices&lt;br /&gt;
** '''Utilize Authentication Utilities''': Modify the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; function in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to use existing authentication utilities, enhancing code reusability and adhering to the Single Responsibility Principle (SRP) and Separation of Concerns (SoC).&lt;br /&gt;
** '''Eliminate Code Duplication''': Refactor &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; to comply with the Don't Repeat Yourself (DRY) principle.&lt;br /&gt;
** '''Rename Functions for Clarity''': Change &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; to better reflect its purpose.&lt;br /&gt;
** '''Refactor Methods in '''&amp;lt;code&amp;gt;review_bid.rb&amp;lt;/code&amp;gt;''':''': Convert class methods to instance methods or relocate them to helper classes for improved code organization.&lt;br /&gt;
* Functionality Enhancements&lt;br /&gt;
** '''Handle Missing Bids Gracefully''': Adjust the bidding algorithm to accommodate students who do not bid, allowing them to select from the remaining options.&lt;br /&gt;
** '''Support Both Individual and Team Assignments''': Ensure the code functions correctly for participant and team-based assignments when signing up for topics.&lt;br /&gt;
* User Interface Improvements&lt;br /&gt;
** '''Increase Transparency in the Bidding Process''': Display messages indicating how many students are eligible to bid, how many have submitted bids, and the deadline.&lt;br /&gt;
** '''Accurate Bid Status Indicators''': Ensure topic colors change correctly based on the number of outstanding bids, providing clear visual feedback to users.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Exhaustive Edge Case Testing''': Conduct thorough testing for edge cases not previously covered, adding additional test cases as needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iv.-design-goals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Design Goals ==&lt;br /&gt;
&lt;br /&gt;
While addressing the existing defects and enhancing the Reviewer Bidding feature, we plan to adhere to the following design guidelines:&lt;br /&gt;
&lt;br /&gt;
* Code Quality and Maintainability&lt;br /&gt;
** '''Improve Code Structure: '''Implement refactoring strategies to enhance code readability and maintainability, ensuring adherence to best practices like the DRY principle.&lt;br /&gt;
** '''Standardize Authentication Logic: '''Utilize a centralized authentication utility to promote consistency and reusability across the application.&lt;br /&gt;
* System Stability and Flexibility&lt;br /&gt;
** '''Enhance Algorithm Robustness: '''Modify the bidding algorithm to handle scenarios where students do not bid, ensuring the system remains stable under all conditions.&lt;br /&gt;
** '''Ensure Assignment Versatility: '''Design the system to seamlessly support both individual and team assignments without additional configuration.&lt;br /&gt;
* User Experience Enhancements&lt;br /&gt;
** '''Increase Bidding Transparency: '''Develop user interface elements that clearly display bidding status, deadlines, and participation metrics.&lt;br /&gt;
** '''Implement Dynamic Visual Feedback: '''Use real-time visual cues, such as color changes, to inform users about the bidding status instantly.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Develop Comprehensive Test Suites: '''Create extensive automated tests covering edge cases and critical functionalities to guarantee reliable system behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;v.-uml-diagram&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== UML Diagram ==&lt;br /&gt;
&lt;br /&gt;
Below is a visual representation of the models associated with the review bids functionality, depicting the data structures and their relationships.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml class diagram2.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi.-implementation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
'''ReviewBid: '''The &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; manages the bidding process, where participants indicate their preferences for reviewing different topics or assignments. This helps distribute review tasks fairly based on participants' interests.&lt;br /&gt;
&lt;br /&gt;
'''Assignment: '''The &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model represents team or individual assignments in Expertiza. It stores all the essential information required to keep the assignment process organized, like managing participants, setting due dates, forming teams, and overseeing the review process.&lt;br /&gt;
&lt;br /&gt;
'''SignUpTopic: '''The &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt; model identifies specific topics or areas within an assignment for which participants can sign up. It manages the availability of these topics, tracks bids, coordinates team sign-ups, and assigns topics based on both bids and participant availability. &lt;br /&gt;
&lt;br /&gt;
'''Participant: '''The &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model is about the individual users who participate in an assignment. This includes students, instructors, teaching assistants, or other relevant roles. This model is responsible for managing key user information, monitoring interactions with the assignment, keeping track of team memberships, and recording things like badges and review grades.&lt;br /&gt;
&lt;br /&gt;
== Implementation ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1.-decoupling-authorization-logic-from-reviewbidscontroller&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Decoupling Authorization Logic from &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| '''&amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; needs to use the authentication utilities'''&lt;br /&gt;
&lt;br /&gt;
The function &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; should use the available authentication utilities to support the Single Responsibility Principle and Separation of Concerns and to support code reusability.&lt;br /&gt;
&lt;br /&gt;
| '''Authentication Consistency:''' Remove authentication logic in the controller and call logic in the authentication utility class instead to ensure consistent use of the same logic across the application, foster reusability, and eliminate DRY.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
==== Authorization Flow Before Refactoring ====&lt;br /&gt;
The sequence diagram depicts the authorization process within the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;. When a user invokes an action, the controller checks if the action is allowed by verifying the user's role through the &amp;lt;code&amp;gt;current_role_name()&amp;lt;/code&amp;gt; method in the &amp;lt;code&amp;gt;ApplicationController&amp;lt;/code&amp;gt;. Depending on the action and the user's role, the controller either authorizes the user to proceed or denies access, ultimately executing the action logic if authorized and responding to the user.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 1.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We simplified the &amp;lt;code&amp;gt;action_allowed?&amp;lt;/code&amp;gt; method by decoupling authorization logic from the controller to use helper methods in the &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt; module. The &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; should not handle authentication, which is a violation of SRP. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;vertical-align:top;&amp;quot; | Original Code !! style=&amp;quot;vertical-align:top;&amp;quot; | Refactored Code&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index'&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator',&lt;br /&gt;
     'Student'].include? current_role_name and&lt;br /&gt;
        ((%w[list].include? action_name) ? are_needed_authorizations_present?(params[:id], &amp;quot;participant&amp;quot;, &amp;quot;reader&amp;quot;, &amp;quot;submitter&amp;quot;, &amp;quot;reviewer&amp;quot;) : true)&lt;br /&gt;
  else&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator'].include? current_role_name&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index', 'list'&lt;br /&gt;
    current_user_has_student_privileges? &amp;amp;&amp;amp; list_authorization_check&lt;br /&gt;
  else&lt;br /&gt;
    current_user_has_ta_privileges?&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; action ===&lt;br /&gt;
The refactor of the &amp;lt;code&amp;gt;ReviewBidsController's index&amp;lt;/code&amp;gt; action simplifies and streamlines the codebase, ensuring the action simply handles controller activities. Previously, the controller was overloaded with responsibilities—it handled everything from locating and validating assignments to fetching review mappings and counting completed reviews. This all-in-one approach made the controller action messy. &lt;br /&gt;
&lt;br /&gt;
Now we have moved the business logic out of the controller action and into dedicated service objects. With this change, we introduced two key service objects: &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; focuses on retrieving the participant along with their assignments, while the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; takes on the task of managing review mappings and calculating review-related stats, like how many reviews have been completed. &lt;br /&gt;
&lt;br /&gt;
This clear separation of responsibilities aligns with the Single Responsibility Principle, which helps each part of our code be more focused and easier to understand. With these new service objects, the code has become more reusable. The logic for fetching assignments, retrieving review mappings, and counting reviews is now more flexible and reusable throughout the application. For example, we used methods in the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; in both the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; and the &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;, helping to avoid code duplication.&lt;br /&gt;
&lt;br /&gt;
After moving the business logic to service objects, we can write unit tests to ensure each part of the code behaves as expected and prevents bugs. We also moved complex calculations, like those for completed reviews, into the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;, freeing the controller from dealing with the implementation details and promoting a cleaner design. The article https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial provides more information on when and how to use service objects in Rails.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
  return unless current_user_id?(@participant.user_id)&lt;br /&gt;
  @assignment = @participant.assignment&lt;br /&gt;
  @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
  # Finding how many reviews have been completed&lt;br /&gt;
  @num_reviews_completed = 0&lt;br /&gt;
  @review_mappings.each do |map|&lt;br /&gt;
    @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # render view for completing reviews after review bidding has been completed&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @assignment = participant_service.assignment&lt;br /&gt;
  unless @assignment.is_a?(Assignment)&lt;br /&gt;
    flash[:error] = 'Assignment not found.'&lt;br /&gt;
    redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  @review_mappings = review_service.review_mappings&lt;br /&gt;
  @num_reviews_completed = review_service.review_counts[:completed]&lt;br /&gt;
&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Optimize &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Action functionality ===&lt;br /&gt;
In the earlier version of the set_priority action in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, the server only needed a couple of key pieces of information: an array of topic IDs (params[:topic]) and the participant ID (params[:id}). From there, it would figure out the assignment_id by using the first topic as a reference. While this worked in simple cases, it could lead to problems if any data was missing or didn’t line up correctly.&lt;br /&gt;
&lt;br /&gt;
With the new implementation, the action now expects clear and structured parameters, including participant_id, an array of topic IDs, and assignment_id. This change ensures that the server always receives complete and reliable information, preventing issues caused by missing or inconsistent data. It also removes the need for the server to perform unnecessary lookups, allowing the controller to focus on its main job: validating inputs (like making sure the topics in params[:topic] belong to the same assignment as the participant), updating the database, handling errors properly, and sending the correct response back to the view.&lt;br /&gt;
&lt;br /&gt;
While these updates have cleaned up the set_priority action, they still contain a lot of business logic, like validations and priority updates. To make the code even more maintainable, there’s room for further refactoring—perhaps moving some of this logic into a service class or model method to streamline the controller even more.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | Old Code&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    if params[:topic].nil?&lt;br /&gt;
      ReviewBid.where(participant_id: params[:id]).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      participant = AssignmentParticipant.find_by(id: params[:id])&lt;br /&gt;
      assignment_id = SignUpTopic.find(params[:topic].first).assignment.id&lt;br /&gt;
      @bids = ReviewBid.where(participant_id: params[:id])&lt;br /&gt;
      signed_up_topics = ReviewBid.where(participant_id: params[:id]).map(&amp;amp;:signuptopic_id)&lt;br /&gt;
      signed_up_topics -= params[:topic].map(&amp;amp;:to_i)&lt;br /&gt;
      signed_up_topics.each do |topic|&lt;br /&gt;
        ReviewBid.where(signuptopic_id: topic, participant_id: params[:id]).destroy_all&lt;br /&gt;
      end&lt;br /&gt;
      params[:topic].each_with_index do |topic_id, index|&lt;br /&gt;
        bid_existence = ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id])&lt;br /&gt;
        if bid_existence.empty?&lt;br /&gt;
          ReviewBid.create(priority: index + 1, signuptopic_id: topic_id, participant_id: params[:id], assignment_id: assignment_id)&lt;br /&gt;
        else&lt;br /&gt;
          ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id]).update_all(priority: index + 1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to action: 'show', assignment_id: params[:assignment_id], id: params[:id]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    participant_id = params[:participant_id].to_i&lt;br /&gt;
    selected_topic_ids = params[:topic]&amp;amp;.map(&amp;amp;:to_i) || []&lt;br /&gt;
    assignment_id = params[:assignment_id].to_i&lt;br /&gt;
&lt;br /&gt;
    unless current_user_can_modify_participant?(participant_id)&lt;br /&gt;
      flash[:error] = 'You are not authorized to modify these bids.'&lt;br /&gt;
      respond_to do |format|&lt;br /&gt;
        format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
        format.json { render json: { status: 'unauthorized' }, status: :unauthorized }&lt;br /&gt;
      end&lt;br /&gt;
      return&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    if selected_topic_ids.empty?&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      assignment = Assignment.find_by(id: assignment_id)&lt;br /&gt;
      if assignment.nil?&lt;br /&gt;
        flash[:error] = &amp;quot;Invalid assignment.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_assignment' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      unless SignUpTopic.where(id: selected_topic_ids, assignment_id: assignment_id).count == selected_topic_ids.size&lt;br /&gt;
        flash[:error] = &amp;quot;One or more selected topics are invalid.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_topics' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).where.not(signuptopic_id: selected_topic_ids).destroy_all&lt;br /&gt;
&lt;br /&gt;
      selected_topic_ids.each_with_index do |topic_id, index|&lt;br /&gt;
        bid = ReviewBid.find_or_initialize_by(signuptopic_id: topic_id, participant_id: participant_id)&lt;br /&gt;
        bid.priority = index + 1&lt;br /&gt;
        bid.assignment_id = assignment_id&lt;br /&gt;
        bid.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_to action: 'show', assignment_id: assignment_id, id: participant_id, notice: 'Review bids updated successfully.' }&lt;br /&gt;
      format.json { render json: { status: 'success' }, status: :ok }&lt;br /&gt;
    end&lt;br /&gt;
  rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
    flash[:error] = &amp;quot;Failed to update priorities: #{e.message}&amp;quot;&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
      format.json { render json: { status: 'error', message: e.message }, status: :unprocessable_entity }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Client Side Changes to Support &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Changes ====&lt;br /&gt;
After tidying up the server-side logic, we needed to refactor the client-side code to ensure it passed the correct information from the view to the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action. On the client side, we now grab the topicIDs and send them as &amp;lt;code&amp;gt;selected_topic_ids&amp;lt;/code&amp;gt;, allowing the controller to figure out which topics were selected and in what order. It’s a cleaner and more reliable way to keep everything working together! &lt;br /&gt;
&lt;br /&gt;
We are also extracting the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, making it a lot easier to send data with AJAX and keeping things flexible. AJAX is awesome because it lets us update the server without reloading the page, making the user experience smooth and responsive. When we pull the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, we can grab it along with other data, like the list of topics, and include it in the request. This way, we don’t have to rely on the URL to know which participant we’re working with, which makes the code cleaner and more adaptable.&lt;br /&gt;
&lt;br /&gt;
If we were to rely on the route for the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt;, every AJAX request would need to be tied to a specific URL structure. That’s limiting, especially if the route doesn’t naturally include the participant ID, as can be the case with collection routes versus member routes in Rails. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery -&amp;gt;&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: -&amp;gt;&lt;br /&gt;
      $.post($(this).data('update-url'), $(this).sortable('serialize'))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery(document).ready ($) -&amp;gt;&lt;br /&gt;
  sendUpdatedOrder = -&amp;gt;&lt;br /&gt;
    sortedIDs = $(this).sortable('toArray')&lt;br /&gt;
&lt;br /&gt;
    topicIDs = sortedIDs.map (id) -&amp;gt;&lt;br /&gt;
      id.replace 'topic_', ''&lt;br /&gt;
&lt;br /&gt;
    participant_id = $(this).data('participant-id')&lt;br /&gt;
&lt;br /&gt;
    assignment_id = new URL($(this).data('update-url'), window.location.origin).searchParams.get('assignment_id')&lt;br /&gt;
&lt;br /&gt;
    $.ajax&lt;br /&gt;
      type: 'POST'&lt;br /&gt;
      url: $(this).data('update-url')&lt;br /&gt;
      data:&lt;br /&gt;
        topic: topicIDs&lt;br /&gt;
        participant_id: participant_id&lt;br /&gt;
        assignment_id: assignment_id&lt;br /&gt;
      success: (response) -&amp;gt;&lt;br /&gt;
        console.log 'Selections updated successfully.'&lt;br /&gt;
      error: (xhr, status, error) -&amp;gt;&lt;br /&gt;
        alert 'An error occurred while saving your selections. Please try again.'&lt;br /&gt;
        console.error error&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: sendUpdatedOrder&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-1.-signupsheetcontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; Updates ===&lt;br /&gt;
&lt;br /&gt;
The ReviewBidsController's index method renders the review_bids_others_work action in &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt;. However, the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; does not include the controller action for this view. We need to find the old implementation of the controller action, add it back, and review and update it for any DRY violations.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # provides variables for reviewing page located at views/review_bids/others_work.html.erb&lt;br /&gt;
  def index&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
    return unless current_user_id?(@participant.user_id)&lt;br /&gt;
&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
    # Finding how many reviews have been completed&lt;br /&gt;
    @num_reviews_completed = 0&lt;br /&gt;
    @review_mappings.each do |map|&lt;br /&gt;
      @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # render view for completing reviews after review bidding has been completed&lt;br /&gt;
    render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
  end  &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-2.-reviewbidsotherswork-view-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;review_bids_others_work.html.erb&amp;lt;/code&amp;gt; View Updates ====&lt;br /&gt;
&lt;br /&gt;
The code only contains a view with this name and no associated controller action. Reviewing the code in the view, we noticed some SRP violations where there is much logic in the presentation layer. Set up helper methods in the controller to handle some of that logic or see if the model can handle some of that logic.&lt;br /&gt;
&lt;br /&gt;
Note: The view is technically part of the SignUpSheetController, but it's being rendered by the ReviewBidsController, which initially threw us off. After chatting with the professor, we decided not to change this view. He indicated that the plan is to reuse these views for both assignment bidding and review bidding.&lt;br /&gt;
&lt;br /&gt;
We would suggest building the assignment bidding and review bidding features separately first to make sure each one works on its own. Once that’s done, we can go back and clean things up by combining any shared functionality. This could include creating shared views or even introducing an abstract model to handle both types of bidding in a cleaner way. It could also mean replacing the ReviewBidsController with something more generic, like a BidsController, to keep things reusable and easier to maintain down the line.&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Rename &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; method ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; should be &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt;&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:''' Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The primary activity here is to refactor the method name &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; and fix the hardcoded URL. However, the match topic bidding algorithm no longer works since Heroku is no longer free, per the professor. The reference link below has detailed information about the web service that we can leverage to gain that understanding.&lt;br /&gt;
&lt;br /&gt;
Reference Link for Webservice Details: [https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Updated Implementation ====&lt;br /&gt;
We implemented an update using mocked data and a service as placeholders until a new URL is in place. We did not end up refactoring to rename this method as we found that the name describes the functionality quite well.&lt;br /&gt;
&lt;br /&gt;
==== review_bids_controller ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def run_bidding_algorithm&lt;br /&gt;
    review_bid = ReviewBid.new&lt;br /&gt;
    bidding_data = review_bid.bidding_data&lt;br /&gt;
    matched_topics = BiddingAlgorithmService.new(bidding_data).run&lt;br /&gt;
    raise ArgumentError, 'Failed to assign reviewers. Please try again later.' unless matched_topics&lt;br /&gt;
&lt;br /&gt;
    matched_topics&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== bidding_algorithm_service ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# frozen_string_literal: true&lt;br /&gt;
&lt;br /&gt;
# The `BiddingAlgorithmService` run the bid assignment algorithm&lt;br /&gt;
# Sends student IDs, topic IDs, student preferences, and timestamps to the web service&lt;br /&gt;
# The web service returns the matched assignments in the JSON response body&lt;br /&gt;
class BiddingAlgorithmService&lt;br /&gt;
  SERVICE_URL = 'http://app-csc517.herokuapp.com/match_topics'.freeze&lt;br /&gt;
&lt;br /&gt;
  # MOCK_DATA is based on the documentation detailing how the webservice behaves.&lt;br /&gt;
  # Reference: https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review#Webservice&lt;br /&gt;
  MOCK_DATA = {&lt;br /&gt;
    36239 =&amp;gt; [3970, 3972, 3975],&lt;br /&gt;
    36240 =&amp;gt; [3973, 3974, 3972],&lt;br /&gt;
    36241 =&amp;gt; [3969, 3971, 3972],&lt;br /&gt;
    36242 =&amp;gt; [3969, 3971, 3973],&lt;br /&gt;
    36243 =&amp;gt; [3969, 3970, 3971]&lt;br /&gt;
  }.freeze&lt;br /&gt;
&lt;br /&gt;
  def initialize(bidding_data)&lt;br /&gt;
    @bidding_data = bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def run&lt;br /&gt;
    return MOCK_DATA if Rails.application.config.use_mock_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
    perform_request&lt;br /&gt;
  rescue RestClient::ExceptionWithResponse, JSON::ParserError&lt;br /&gt;
    false&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def perform_request&lt;br /&gt;
    response = RestClient.post(&lt;br /&gt;
      SERVICE_URL,&lt;br /&gt;
      @bidding_data.to_json,&lt;br /&gt;
      content_type: :json,&lt;br /&gt;
      accept: :json&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    validate_response(response)&lt;br /&gt;
    JSON.parse(response.body)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def validate_response(response)&lt;br /&gt;
    raise 'Invalid response format' unless response.headers[:content_type] &amp;amp;&amp;amp; response.headers[:content_type].include?('application/json')&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Resolve Functionality Issues for Non Bidders ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Currently it doesn't work if some student does not bid. In this case, algorithm needs to be fixed to ignore anyone who didn’t bid, and let them choose from what’s left over.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to do several things within this controller. We will set a default state for non-bidders that we can point to to give them options to select topics to review from the leftovers. We will consolidate &amp;lt;code&amp;gt;@sign_up_topics&amp;lt;/code&amp;gt; to resolve the DRY violation. Lastly, we will remove business logic from the show method and push it to a new helper to conform to the SRP.&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # Additional method&lt;br /&gt;
&lt;br /&gt;
  # Provides variables for review bidding page&lt;br /&gt;
  def show&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id].to_i)&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @sign_up_topics = SignUpTopic.where(assignment_id: @assignment.id, private_to: nil)&lt;br /&gt;
    my_topic = SignedUpTeam.topic_id(@participant.parent_id, @participant.user_id)&lt;br /&gt;
    @sign_up_topics -= SignUpTopic.where(assignment_id: @assignment.id, id: my_topic)&lt;br /&gt;
    @num_participants = AssignmentParticipant.where(parent_id: @assignment.id).count&lt;br /&gt;
    @selected_topics = nil # This is used to list the topics assigned to review (i.e., select == assigned)&lt;br /&gt;
    @bids = ReviewBid.where(participant_id: @participant, assignment_id: @assignment.id)&lt;br /&gt;
    signed_up_topics = []&lt;br /&gt;
    &lt;br /&gt;
    @bids.each do |bid|&lt;br /&gt;
      sign_up_topic = SignUpTopic.find_by(id: bid.signuptopic_id)&lt;br /&gt;
      signed_up_topics &amp;lt;&amp;lt; sign_up_topic if sign_up_topic&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    signed_up_topics &amp;amp;= @sign_up_topics&lt;br /&gt;
    @sign_up_topics -= signed_up_topics&lt;br /&gt;
    @bids = signed_up_topics&lt;br /&gt;
    @num_of_topics = @sign_up_topics.size&lt;br /&gt;
    @assigned_review_maps = []&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: @assignment.id, reviewer_id: @participant.id).each do |review_map|&lt;br /&gt;
      @assigned_review_maps &amp;lt;&amp;lt; review_map&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Explicitly render view since it's in the sign up sheet views&lt;br /&gt;
    render 'sign_up_sheet/review_bids_show'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Additional methods&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-2.-reviewbidshelper-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
 # Assigns bidding topics to reviewers&lt;br /&gt;
  def assign_bidding&lt;br /&gt;
      assignment = validate_assignment(params[:assignment_id])&lt;br /&gt;
&lt;br /&gt;
      reviewers = validate_reviewers(assignment.id)&lt;br /&gt;
      reviewer_ids = reviewers.map(&amp;amp;:id)&lt;br /&gt;
&lt;br /&gt;
      matched_topics = run_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
      if matched_topics.blank?&lt;br /&gt;
        flash[:alert] = 'Topic or assignment is missing'&lt;br /&gt;
        redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      leftover_topics = find_leftover_topics(assignment.id, matched_topics)&lt;br /&gt;
      assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.new.assign_review_topics(matched_topics)&lt;br /&gt;
      assignment.update!(can_choose_topic_to_review: false)&lt;br /&gt;
&lt;br /&gt;
      flash[:notice] = 'Reviewers were successfully assigned to topics.'&lt;br /&gt;
      redirect_back fallback_location: root_path&lt;br /&gt;
    rescue ArgumentError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ArgumentError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: e.message&lt;br /&gt;
    rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::RecordInvalid: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue ActiveRecord::ActiveRecordError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::ActiveRecordError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue StandardError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;StandardError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers. Please try again later.'&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  #service methods here ...&lt;br /&gt;
&lt;br /&gt;
  def find_leftover_topics(assignment_id, matched_topics)&lt;br /&gt;
    all_topic_ids = SignUpTopic.where(assignment_id: assignment_id).pluck(:id)&lt;br /&gt;
    assigned_topic_ids = matched_topics.map { |match| match[:topic_id] }&lt;br /&gt;
&lt;br /&gt;
    # Calculate leftover topics&lt;br /&gt;
    all_topic_ids - assigned_topic_ids&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
    return if leftover_topics.blank?&lt;br /&gt;
&lt;br /&gt;
    # Find non-bidders by excluding already matched reviewers&lt;br /&gt;
    non_bidders = reviewer_ids - matched_topics.keys&lt;br /&gt;
&lt;br /&gt;
    # Assign leftover topics to non-bidders in a round-robin fashion&lt;br /&gt;
    non_bidders.each_with_index do |reviewer_id, index|&lt;br /&gt;
      topic_id = leftover_topics[index % leftover_topics.length]&lt;br /&gt;
      ReviewBid.create(priority: 1, signuptopic_id: topic_id, participant_id: reviewer_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirm Functionality for Single-person Teams ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Make sure your code works on individual assignments, i.e., assignments where participants sign up for topics instead of teams. In this case, a team is created for each participant when they sign up. So the code should work for assignments to which either individuals or teams submit.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to make an update to focus on topic rather than team or have it where teams are updated based on topic id.We will probably utilize sign up topic or a similar object to accomplish this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-5-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bid Status Messaging ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| To make the functionality more intuitive, include a message to say how many students are eligible to submit bids, how many have submitted their bids, when the deadline for submitting bids is.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
The logic for these messages will go into the helper file. It is effectively the same reasoning as our migration of other business/functionality code from the controller to the helper to conform to the SRP. It will go hand in hand with the SignUpTopic portion of the code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-6-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Why are the methods in review_bid.rb class methods? Can we change them to instance methods or move it to helpers?&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-1.-reviewbid-model-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model Updates ====&lt;br /&gt;
Since the current class methods are tied to &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model instances, we will change them to instance methods. There is no need to move them to helpers.&lt;br /&gt;
&lt;br /&gt;
===== Review Bid Model Flow Before Refactoring =====&lt;br /&gt;
This sequence diagram describes how the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; interacts with the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model in assigning review topics to reviewers according to their bids. Once the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; action is initiated by the user, the controller gathers the IDs of reviewers and calls &amp;lt;code&amp;gt;ReviewBid.bidding_data&amp;lt;/code&amp;gt; to gather bidding information. That is sent out to an external bidding algorithm, and the matched topics that come back are used by &amp;lt;code&amp;gt;ReviewBid.assign_review_topics&amp;lt;/code&amp;gt; to create &amp;lt;code&amp;gt;ReviewResponseMap&amp;lt;/code&amp;gt; entries, assigning topics to reviewers for the assignment.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
  belongs_to :assignment, class_name: 'Assignment'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  def self.bidding_data(assignment_id, reviewer_ids)&lt;br /&gt;
    # create basic hash and set basic hash data&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'users' =&amp;gt; {}, 'max_accepted_proposals' =&amp;gt; [] }&lt;br /&gt;
    bidding_data['tid'] = SignUpTopic.where(assignment_id: assignment_id).ids&lt;br /&gt;
    bidding_data['max_accepted_proposals'] = Assignment.where(id: assignment_id).pluck(:num_reviews_allowed).first&lt;br /&gt;
&lt;br /&gt;
    # loop through reviewer_ids to get reviewer specific bidding data&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      bidding_data['users'][reviewer_id] = reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # assigns topics to reviews as matched by the webservice algorithm&lt;br /&gt;
  def self.assign_review_topics(assignment_id, reviewer_ids, matched_topics, _min_num_reviews = 2)&lt;br /&gt;
    # if review response map already created, delete it&lt;br /&gt;
    if ReviewResponseMap.where(reviewed_object_id: assignment_id)&lt;br /&gt;
      ReviewResponseMap.where(reviewed_object_id: assignment_id).destroy_all&lt;br /&gt;
    end&lt;br /&gt;
    # loop through reviewer_ids to assign reviews to each reviewer&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      topics_to_assign = matched_topics[reviewer_id.to_s]&lt;br /&gt;
      topics_to_assign.each do |topic|&lt;br /&gt;
        assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method to assign a single topic to a reviewer&lt;br /&gt;
  def self.assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic).pluck(:team_id).first&lt;br /&gt;
    team_to_review.nil? ? [] : ReviewResponseMap.create(reviewed_object_id: assignment_id, reviewer_id: reviewer_id, reviewee_id: team_to_review, type: 'ReviewResponseMap')&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method for getting individual reviewer_ids bidding data&lt;br /&gt;
  # returns user's bidding data hash&lt;br /&gt;
  def self.reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    reviewer_user_id = AssignmentParticipant.find(reviewer_id).user_id&lt;br /&gt;
    self_topic = SignedUpTeam.topic_id(assignment_id, reviewer_user_id)&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'otid' =&amp;gt; self_topic, 'priority' =&amp;gt; [], 'time' =&amp;gt; [] }&lt;br /&gt;
    bids = ReviewBid.where(participant_id: reviewer_id)&lt;br /&gt;
&lt;br /&gt;
    # loop through each bid for a topic to get specific data&lt;br /&gt;
    bids.each do |bid|&lt;br /&gt;
      bidding_data['tid'] &amp;lt;&amp;lt; bid.signuptopic_id&lt;br /&gt;
      bidding_data['priority'] &amp;lt;&amp;lt; bid.priority&lt;br /&gt;
      bidding_data['time'] &amp;lt;&amp;lt; bid.updated_at&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  # Instance method to get bidding data for this review bid&lt;br /&gt;
  def bidding_data&lt;br /&gt;
    {&lt;br /&gt;
      'tid' =&amp;gt; topic_ids,&lt;br /&gt;
      'otid' =&amp;gt; self_topic,&lt;br /&gt;
      'priority' =&amp;gt; priorities,&lt;br /&gt;
      'time' =&amp;gt; updated_times&lt;br /&gt;
    }&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # bid object to limit db touches for similar data acquisitions&lt;br /&gt;
  def bids&lt;br /&gt;
    @bids ||= ReviewBid.where(participant_id: participant_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def topic_ids&lt;br /&gt;
    bids.pluck(:signuptopic_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def priorities&lt;br /&gt;
    bids.pluck(:priority)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def updated_times&lt;br /&gt;
    bids.pluck(:updated_at)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self_topic&lt;br /&gt;
    return nil unless topic &amp;amp;&amp;amp; topic.assignment_id&lt;br /&gt;
&lt;br /&gt;
    SignedUpTeam.topic_id(topic.assignment_id, participant.user_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign topics to reviews&lt;br /&gt;
  def assign_review_topics(matched_topics)&lt;br /&gt;
    validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: topic.assignment_id).destroy_all&lt;br /&gt;
&lt;br /&gt;
    matched_topics.each do |reviewer_id, topics|&lt;br /&gt;
      Array(topics).each { |topic_id| assign_topic_to_reviewer(reviewer_id, topic_id) }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign a single topic to a reviewer&lt;br /&gt;
  def assign_topic_to_reviewer(reviewer_id, topic_id)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic_id).pluck(:team_id).first&lt;br /&gt;
    return if team_to_review.nil?&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.create(&lt;br /&gt;
      reviewed_object_id: topic.assignment_id,&lt;br /&gt;
      reviewer_id: reviewer_id,&lt;br /&gt;
      reviewee_id: team_to_review,&lt;br /&gt;
      type: 'ReviewResponseMap'&lt;br /&gt;
    )&lt;br /&gt;
    # error handling&lt;br /&gt;
  rescue StandardError =&amp;gt; e&lt;br /&gt;
    Rails.logger.error(&amp;quot;Failed to assign topic #{topic_id} to reviewer #{reviewer_id}: #{e.message}&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
    raise ArgumentError, 'Topic or assignment is missing' if topic.nil? || topic.assignment_id.nil?&lt;br /&gt;
    raise ArgumentError, 'Matched topics must be a Hash' unless matched_topics.is_a?(Hash)&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
Once we change them to instance methods, we’ll need to modify their use in the code to instantiate the object first.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;Placeholder for implementation screenshots (before and after)&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-3.-schema.db-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;Schema.db&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We also need to remove the foreign key reference to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model since the &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model already has access to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;, and that is the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; we should be referencing. This will also involve modifying the database to remove the foreign key reference to the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Steps required:&lt;br /&gt;
&lt;br /&gt;
# Review the records in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table in the database to see if there are any records and if those records have values for &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;&lt;br /&gt;
# Create a migration file using the command: &amp;lt;code&amp;gt;rails generate migration RemoveAssignmentFromReviewBids&amp;lt;/code&amp;gt;&lt;br /&gt;
# In the generated file, indicate the change that needs to be made using the change method and migration columns &amp;lt;code&amp;gt;remove_foreign_key&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;remove_column&amp;lt;/code&amp;gt;. Migration should be straightforward since there is no cascading behavior (i.e., &amp;lt;code&amp;gt;dependent:destroy&amp;lt;/code&amp;gt;) to worry about.&lt;br /&gt;
# Once comfortable with the updates to the generated file, run the migration using the &amp;lt;code&amp;gt;rails db:migrate&amp;lt;/code&amp;gt; command.&lt;br /&gt;
# Run a query in the DB to ensure no orphaned records are in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table after the updates.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;Placeholder for implementation screenshots (before and after)&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-4.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implement Edge Case Testing ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| In the previous implementation wiki, there are edge cases which are not exhaustively tested. Should test those edge cases thoroughly and add more edge case testing - link&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span class=&amp;quot;mark&amp;quot;&amp;gt;TODO&amp;lt;/span&amp;gt;:We just got initial test results back. We still need to figure out why tests are failing and what may be slipping through the cracks.[[File:Test expertiza.png|411x459px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-8-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design Patterns and Principles ===&lt;br /&gt;
Below are the design patterns and principles we plan to use to address the problem statements outlined in the project requirements:&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
** Controller Responsibility&lt;br /&gt;
*** To address the issue of authentication logic residing in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, we will refactor the code to ensure the controller solely handles request and response flows.&lt;br /&gt;
*** Authentication logic will be moved to dedicated methods in &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt;, making the codebase easier to maintain and test.&lt;br /&gt;
** View Simplification&lt;br /&gt;
*** Currently, the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; view contains excessive business logic, violating SRP.&lt;br /&gt;
*** We will move this logic into helper methods or models, ensuring each component has a single responsibility and improving code readability.&lt;br /&gt;
&lt;br /&gt;
* Separation of Concerns (SoC)&lt;br /&gt;
** By relocating authorization logic out of the controller, we separate authentication from request handling.&lt;br /&gt;
** Adhering to SoC enhances readability and maintainability by allowing each part of the application to focus on its specific role.&lt;br /&gt;
* Don't Repeat Yourself (DRY)&lt;br /&gt;
** We will inspect the existing implementation for code duplication, particularly in the controller actions.&lt;br /&gt;
** Any repetitive code will be refactored or removed to reduce redundancy and improve clarity.&lt;br /&gt;
&lt;br /&gt;
* Model-View-Controller (MVC) Adherence&lt;br /&gt;
** Moving business logic from the view aligns with MVC best practices.&lt;br /&gt;
** By ensuring the view only presents data without processing it, we maintain clear boundaries between the model, view, and controller layers.&lt;br /&gt;
&lt;br /&gt;
* Dependency Inversion Principle (DIP)&lt;br /&gt;
** We plan to update class methods to instance methods, allowing the controller to depend on abstractions rather than concrete implementations.&lt;br /&gt;
** This change leverages Ruby's duck typing, providing flexibility and making the code easier to maintain and test.&lt;br /&gt;
&lt;br /&gt;
* Factory Pattern&lt;br /&gt;
** We will implement a factory pattern for the common processing of &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpBid&amp;lt;/code&amp;gt;, encapsulating object creation and promoting code reuse.&lt;br /&gt;
&lt;br /&gt;
* Law of Demeter (Principle of Least Knowledge)&lt;br /&gt;
** We'll delegate interactions appropriately to prevent tightly coupled code, especially when dealing with topics and teams.&lt;br /&gt;
** Clear and intentional naming during refactoring will enhance code clarity and maintainability.&lt;br /&gt;
&lt;br /&gt;
* Adapter Pattern&lt;br /&gt;
** To add messaging to bid states for users, we'll use the adapter pattern to allow different components to work together without modifying their existing code.&lt;br /&gt;
&lt;br /&gt;
== Files Modified/Added ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-1.-controllers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
review_bids_controller.rb&lt;br /&gt;
&lt;br /&gt;
signup_sheet_controller.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-2.-models&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
review_bid.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-3.-helpers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
&lt;br /&gt;
authorization_helper.rb&lt;br /&gt;
&lt;br /&gt;
Review_bids_helper.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;viii.-test-plan&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
The Excel spreadsheet below details our test plan for testing the code we changed or created to ensure code coverage. The process involved reviewing the code and determining all the possible scenarios that needed to be covered based on what the method was doing, then creating test cases accordingly.&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/spreadsheets/d/1iHzOdSwlTtxOtf5VsNdwOaCP8NzYMmiJ/edit?usp=drive_link Open Test Matrix Spreadsheet]&lt;br /&gt;
&lt;br /&gt;
== Test Coverage ==&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;screenshot of SimpleCov coverage analysis&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x.-team&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-1.-mentor&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Ed Gehringer&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-2.-members&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Members ===&lt;br /&gt;
&lt;br /&gt;
* Gavin Teague&lt;br /&gt;
* Janice Uwujaren&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xi.-links&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
Previous Wiki: [https://wiki.expertiza.ncsu.edu/index.php/CSC/ECE_517_Fall_2021_-_E2151._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;Link&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xii.-references&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/document/d/1LHVacylQmB15I6VcYUA5nuC7GRRU9szHzZhjlPcE-PA/edit?tab=t.0#heading=h.u0oycoj6e0ev &amp;lt;u&amp;gt;Project Instructions&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;appendix-a-uml-class-diagram-plantuml-code-for-review-bids&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Juwujar</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160948</id>
		<title>CSC/ECE 517 Fall 2024 - E2485. Allow reviewers to bid on what to review</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160948"/>
		<updated>2024-12-10T15:03:55Z</updated>

		<summary type="html">&lt;p&gt;Juwujar: /* ReviewBidsController Updates */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
This project will fix the defects and improve the functionality in Expertiza of the Reviewer Bidding feature, a process where reviewers can bid for assignments they want to review. It will make the functionality smoother, more reliable, and align with Expertiza's collaborative learning objectives.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iii.-problem-statements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Problem Statements ==&lt;br /&gt;
&lt;br /&gt;
Assigning reviews to users on Expertiza involves a complex bidding algorithm that requires optimization before production deployment. Below are key areas we plan to address as per the project requirements:&lt;br /&gt;
* Code Refactoring and Best Practices&lt;br /&gt;
** '''Utilize Authentication Utilities''': Modify the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; function in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to use existing authentication utilities, enhancing code reusability and adhering to the Single Responsibility Principle (SRP) and Separation of Concerns (SoC).&lt;br /&gt;
** '''Eliminate Code Duplication''': Refactor &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; to comply with the Don't Repeat Yourself (DRY) principle.&lt;br /&gt;
** '''Rename Functions for Clarity''': Change &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; to better reflect its purpose.&lt;br /&gt;
** '''Refactor Methods in '''&amp;lt;code&amp;gt;review_bid.rb&amp;lt;/code&amp;gt;''':''': Convert class methods to instance methods or relocate them to helper classes for improved code organization.&lt;br /&gt;
* Functionality Enhancements&lt;br /&gt;
** '''Handle Missing Bids Gracefully''': Adjust the bidding algorithm to accommodate students who do not bid, allowing them to select from the remaining options.&lt;br /&gt;
** '''Support Both Individual and Team Assignments''': Ensure the code functions correctly for participant and team-based assignments when signing up for topics.&lt;br /&gt;
* User Interface Improvements&lt;br /&gt;
** '''Increase Transparency in the Bidding Process''': Display messages indicating how many students are eligible to bid, how many have submitted bids, and the deadline.&lt;br /&gt;
** '''Accurate Bid Status Indicators''': Ensure topic colors change correctly based on the number of outstanding bids, providing clear visual feedback to users.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Exhaustive Edge Case Testing''': Conduct thorough testing for edge cases not previously covered, adding additional test cases as needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iv.-design-goals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Design Goals ==&lt;br /&gt;
&lt;br /&gt;
While addressing the existing defects and enhancing the Reviewer Bidding feature, we plan to adhere to the following design guidelines:&lt;br /&gt;
&lt;br /&gt;
* Code Quality and Maintainability&lt;br /&gt;
** '''Improve Code Structure: '''Implement refactoring strategies to enhance code readability and maintainability, ensuring adherence to best practices like the DRY principle.&lt;br /&gt;
** '''Standardize Authentication Logic: '''Utilize a centralized authentication utility to promote consistency and reusability across the application.&lt;br /&gt;
* System Stability and Flexibility&lt;br /&gt;
** '''Enhance Algorithm Robustness: '''Modify the bidding algorithm to handle scenarios where students do not bid, ensuring the system remains stable under all conditions.&lt;br /&gt;
** '''Ensure Assignment Versatility: '''Design the system to seamlessly support both individual and team assignments without additional configuration.&lt;br /&gt;
* User Experience Enhancements&lt;br /&gt;
** '''Increase Bidding Transparency: '''Develop user interface elements that clearly display bidding status, deadlines, and participation metrics.&lt;br /&gt;
** '''Implement Dynamic Visual Feedback: '''Use real-time visual cues, such as color changes, to inform users about the bidding status instantly.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Develop Comprehensive Test Suites: '''Create extensive automated tests covering edge cases and critical functionalities to guarantee reliable system behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;v.-uml-diagram&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== UML Diagram ==&lt;br /&gt;
&lt;br /&gt;
Below is a visual representation of the models associated with the review bids functionality, depicting the data structures and their relationships.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml class diagram2.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi.-implementation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
'''ReviewBid: '''The &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; manages the bidding process, where participants indicate their preferences for reviewing different topics or assignments. This helps distribute review tasks fairly based on participants' interests.&lt;br /&gt;
&lt;br /&gt;
'''Assignment: '''The &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model represents team or individual assignments in Expertiza. It stores all the essential information required to keep the assignment process organized, like managing participants, setting due dates, forming teams, and overseeing the review process.&lt;br /&gt;
&lt;br /&gt;
'''SignUpTopic: '''The &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt; model identifies specific topics or areas within an assignment for which participants can sign up. It manages the availability of these topics, tracks bids, coordinates team sign-ups, and assigns topics based on both bids and participant availability. &lt;br /&gt;
&lt;br /&gt;
'''Participant: '''The &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model is about the individual users who participate in an assignment. This includes students, instructors, teaching assistants, or other relevant roles. This model is responsible for managing key user information, monitoring interactions with the assignment, keeping track of team memberships, and recording things like badges and review grades.&lt;br /&gt;
&lt;br /&gt;
== Implementation ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1.-decoupling-authorization-logic-from-reviewbidscontroller&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Decoupling Authorization Logic from &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| '''&amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; needs to use the authentication utilities'''&lt;br /&gt;
&lt;br /&gt;
The function &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; should use the available authentication utilities to support the Single Responsibility Principle and Separation of Concerns and to support code reusability.&lt;br /&gt;
&lt;br /&gt;
| '''Authentication Consistency:''' Remove authentication logic in the controller and call logic in the authentication utility class instead to ensure consistent use of the same logic across the application, foster reusability, and eliminate DRY.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
==== Authorization Flow Before Refactoring ====&lt;br /&gt;
The sequence diagram depicts the authorization process within the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;. When a user invokes an action, the controller checks if the action is allowed by verifying the user's role through the &amp;lt;code&amp;gt;current_role_name()&amp;lt;/code&amp;gt; method in the &amp;lt;code&amp;gt;ApplicationController&amp;lt;/code&amp;gt;. Depending on the action and the user's role, the controller either authorizes the user to proceed or denies access, ultimately executing the action logic if authorized and responding to the user.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 1.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We simplified the &amp;lt;code&amp;gt;action_allowed?&amp;lt;/code&amp;gt; method by decoupling authorization logic from the controller to use helper methods in the &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt; module. The &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; should not handle authentication, which is a violation of SRP. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;vertical-align:top;&amp;quot; | Original Code !! style=&amp;quot;vertical-align:top;&amp;quot; | Refactored Code&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index'&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator',&lt;br /&gt;
     'Student'].include? current_role_name and&lt;br /&gt;
        ((%w[list].include? action_name) ? are_needed_authorizations_present?(params[:id], &amp;quot;participant&amp;quot;, &amp;quot;reader&amp;quot;, &amp;quot;submitter&amp;quot;, &amp;quot;reviewer&amp;quot;) : true)&lt;br /&gt;
  else&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator'].include? current_role_name&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index', 'list'&lt;br /&gt;
    current_user_has_student_privileges? &amp;amp;&amp;amp; list_authorization_check&lt;br /&gt;
  else&lt;br /&gt;
    current_user_has_ta_privileges?&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; action ===&lt;br /&gt;
The refactor of the &amp;lt;code&amp;gt;ReviewBidsController's index&amp;lt;/code&amp;gt; action simplifies and streamlines the codebase, ensuring the action simply handles controller activities. Previously, the controller was overloaded with responsibilities—it handled everything from locating and validating assignments to fetching review mappings and counting completed reviews. This all-in-one approach made the controller action messy. &lt;br /&gt;
&lt;br /&gt;
Now we have moved the business logic out of the controller action and into dedicated service objects. With this change, we introduced two key service objects: &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; focuses on retrieving the participant along with their assignments, while the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; takes on the task of managing review mappings and calculating review-related stats, like how many reviews have been completed. &lt;br /&gt;
&lt;br /&gt;
This clear separation of responsibilities aligns with the Single Responsibility Principle, which helps each part of our code be more focused and easier to understand. With these new service objects, the code has become more reusable. The logic for fetching assignments, retrieving review mappings, and counting reviews is now more flexible and reusable throughout the application. For example, we used methods in the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; in both the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; and the &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;, helping to avoid code duplication.&lt;br /&gt;
&lt;br /&gt;
After moving the business logic to service objects, we can write unit tests to ensure each part of the code behaves as expected and prevents bugs. We also moved complex calculations, like those for completed reviews, into the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;, freeing the controller from dealing with the implementation details and promoting a cleaner design. The article https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial provides more information on when and how to use service objects in Rails.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
  return unless current_user_id?(@participant.user_id)&lt;br /&gt;
  @assignment = @participant.assignment&lt;br /&gt;
  @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
  # Finding how many reviews have been completed&lt;br /&gt;
  @num_reviews_completed = 0&lt;br /&gt;
  @review_mappings.each do |map|&lt;br /&gt;
    @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # render view for completing reviews after review bidding has been completed&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @assignment = participant_service.assignment&lt;br /&gt;
  unless @assignment.is_a?(Assignment)&lt;br /&gt;
    flash[:error] = 'Assignment not found.'&lt;br /&gt;
    redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  @review_mappings = review_service.review_mappings&lt;br /&gt;
  @num_reviews_completed = review_service.review_counts[:completed]&lt;br /&gt;
&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Optimize &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Action functionality ===&lt;br /&gt;
In the earlier version of the set_priority action in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, the server only needed a couple of key pieces of information: an array of topic IDs (params[:topic]) and the participant ID (params[:id}). From there, it would figure out the assignment_id by using the first topic as a reference. While this worked in simple cases, it could lead to problems if any data was missing or didn’t line up correctly.&lt;br /&gt;
&lt;br /&gt;
With the new implementation, the action now expects clear and structured parameters, including participant_id, an array of topic IDs, and assignment_id. This change ensures that the server always receives complete and reliable information, preventing issues caused by missing or inconsistent data. It also removes the need for the server to perform unnecessary lookups, allowing the controller to focus on its main job: validating inputs (like making sure the topics in params[:topic] belong to the same assignment as the participant), updating the database, handling errors properly, and sending the correct response back to the view.&lt;br /&gt;
&lt;br /&gt;
While these updates have cleaned up the set_priority action, they still contain a lot of business logic, like validations and priority updates. To make the code even more maintainable, there’s room for further refactoring—perhaps moving some of this logic into a service class or model method to streamline the controller even more.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | Old Code&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    if params[:topic].nil?&lt;br /&gt;
      ReviewBid.where(participant_id: params[:id]).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      participant = AssignmentParticipant.find_by(id: params[:id])&lt;br /&gt;
      assignment_id = SignUpTopic.find(params[:topic].first).assignment.id&lt;br /&gt;
      @bids = ReviewBid.where(participant_id: params[:id])&lt;br /&gt;
      signed_up_topics = ReviewBid.where(participant_id: params[:id]).map(&amp;amp;:signuptopic_id)&lt;br /&gt;
      signed_up_topics -= params[:topic].map(&amp;amp;:to_i)&lt;br /&gt;
      signed_up_topics.each do |topic|&lt;br /&gt;
        ReviewBid.where(signuptopic_id: topic, participant_id: params[:id]).destroy_all&lt;br /&gt;
      end&lt;br /&gt;
      params[:topic].each_with_index do |topic_id, index|&lt;br /&gt;
        bid_existence = ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id])&lt;br /&gt;
        if bid_existence.empty?&lt;br /&gt;
          ReviewBid.create(priority: index + 1, signuptopic_id: topic_id, participant_id: params[:id], assignment_id: assignment_id)&lt;br /&gt;
        else&lt;br /&gt;
          ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id]).update_all(priority: index + 1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to action: 'show', assignment_id: params[:assignment_id], id: params[:id]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    participant_id = params[:participant_id].to_i&lt;br /&gt;
    selected_topic_ids = params[:topic]&amp;amp;.map(&amp;amp;:to_i) || []&lt;br /&gt;
    assignment_id = params[:assignment_id].to_i&lt;br /&gt;
&lt;br /&gt;
    unless current_user_can_modify_participant?(participant_id)&lt;br /&gt;
      flash[:error] = 'You are not authorized to modify these bids.'&lt;br /&gt;
      respond_to do |format|&lt;br /&gt;
        format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
        format.json { render json: { status: 'unauthorized' }, status: :unauthorized }&lt;br /&gt;
      end&lt;br /&gt;
      return&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    if selected_topic_ids.empty?&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      assignment = Assignment.find_by(id: assignment_id)&lt;br /&gt;
      if assignment.nil?&lt;br /&gt;
        flash[:error] = &amp;quot;Invalid assignment.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_assignment' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      unless SignUpTopic.where(id: selected_topic_ids, assignment_id: assignment_id).count == selected_topic_ids.size&lt;br /&gt;
        flash[:error] = &amp;quot;One or more selected topics are invalid.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_topics' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).where.not(signuptopic_id: selected_topic_ids).destroy_all&lt;br /&gt;
&lt;br /&gt;
      selected_topic_ids.each_with_index do |topic_id, index|&lt;br /&gt;
        bid = ReviewBid.find_or_initialize_by(signuptopic_id: topic_id, participant_id: participant_id)&lt;br /&gt;
        bid.priority = index + 1&lt;br /&gt;
        bid.assignment_id = assignment_id&lt;br /&gt;
        bid.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_to action: 'show', assignment_id: assignment_id, id: participant_id, notice: 'Review bids updated successfully.' }&lt;br /&gt;
      format.json { render json: { status: 'success' }, status: :ok }&lt;br /&gt;
    end&lt;br /&gt;
  rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
    flash[:error] = &amp;quot;Failed to update priorities: #{e.message}&amp;quot;&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
      format.json { render json: { status: 'error', message: e.message }, status: :unprocessable_entity }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Client Side Changes to Support &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Changes ====&lt;br /&gt;
After tidying up the server-side logic, we needed to refactor the client-side code to ensure it passed the correct information from the view to the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action. On the client side, we now grab the topicIDs and send them as &amp;lt;code&amp;gt;selected_topic_ids&amp;lt;/code&amp;gt;, allowing the controller to figure out which topics were selected and in what order. It’s a cleaner and more reliable way to keep everything working together! &lt;br /&gt;
&lt;br /&gt;
We are also extracting the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, making it a lot easier to send data with AJAX and keeping things flexible. AJAX is awesome because it lets us update the server without reloading the page, making the user experience smooth and responsive. When we pull the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, we can grab it along with other data, like the list of topics, and include it in the request. This way, we don’t have to rely on the URL to know which participant we’re working with, which makes the code cleaner and more adaptable.&lt;br /&gt;
&lt;br /&gt;
If we were to rely on the route for the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt;, every AJAX request would need to be tied to a specific URL structure. That’s limiting, especially if the route doesn’t naturally include the participant ID, as can be the case with collection routes versus member routes in Rails. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery -&amp;gt;&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: -&amp;gt;&lt;br /&gt;
      $.post($(this).data('update-url'), $(this).sortable('serialize'))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery(document).ready ($) -&amp;gt;&lt;br /&gt;
  sendUpdatedOrder = -&amp;gt;&lt;br /&gt;
    sortedIDs = $(this).sortable('toArray')&lt;br /&gt;
&lt;br /&gt;
    topicIDs = sortedIDs.map (id) -&amp;gt;&lt;br /&gt;
      id.replace 'topic_', ''&lt;br /&gt;
&lt;br /&gt;
    participant_id = $(this).data('participant-id')&lt;br /&gt;
&lt;br /&gt;
    assignment_id = new URL($(this).data('update-url'), window.location.origin).searchParams.get('assignment_id')&lt;br /&gt;
&lt;br /&gt;
    $.ajax&lt;br /&gt;
      type: 'POST'&lt;br /&gt;
      url: $(this).data('update-url')&lt;br /&gt;
      data:&lt;br /&gt;
        topic: topicIDs&lt;br /&gt;
        participant_id: participant_id&lt;br /&gt;
        assignment_id: assignment_id&lt;br /&gt;
      success: (response) -&amp;gt;&lt;br /&gt;
        console.log 'Selections updated successfully.'&lt;br /&gt;
      error: (xhr, status, error) -&amp;gt;&lt;br /&gt;
        alert 'An error occurred while saving your selections. Please try again.'&lt;br /&gt;
        console.error error&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: sendUpdatedOrder&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-1.-signupsheetcontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; Updates ===&lt;br /&gt;
&lt;br /&gt;
The ReviewBidsController's index method renders the review_bids_others_work action in &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt;. However, the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; does not include the controller action for this view. We need to find the old implementation of the controller action, add it back, and review and update it for any DRY violations.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # provides variables for reviewing page located at views/review_bids/others_work.html.erb&lt;br /&gt;
  def index&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
    return unless current_user_id?(@participant.user_id)&lt;br /&gt;
&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
    # Finding how many reviews have been completed&lt;br /&gt;
    @num_reviews_completed = 0&lt;br /&gt;
    @review_mappings.each do |map|&lt;br /&gt;
      @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # render view for completing reviews after review bidding has been completed&lt;br /&gt;
    render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
  end  &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-2.-reviewbidsotherswork-view-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;review_bids_others_work.html.erb&amp;lt;/code&amp;gt; View Updates ====&lt;br /&gt;
&lt;br /&gt;
The code only contains a view with this name and no associated controller action. Reviewing the code in the view, we noticed some SRP violations where there is much logic in the presentation layer. Set up helper methods in the controller to handle some of that logic or see if the model can handle some of that logic.&lt;br /&gt;
&lt;br /&gt;
Note: The view is technically part of the SignUpSheetController, but it's being rendered by the ReviewBidsController, which initially threw us off. After chatting with the professor, we decided not to change this view. He indicated that the plan is to reuse these views for both assignment bidding and review bidding.&lt;br /&gt;
&lt;br /&gt;
We would suggest building the assignment bidding and review bidding features separately first to make sure each one works on its own. Once that’s done, we can go back and clean things up by combining any shared functionality. This could include creating shared views or even introducing an abstract model to handle both types of bidding in a cleaner way. It could also mean replacing the ReviewBidsController with something more generic, like a BidsController, to keep things reusable and easier to maintain down the line.&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Rename &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; method ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; should be &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt;&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:''' Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The primary activity here is to refactor the method name &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; and fix the hardcoded URL. However, the match topic bidding algorithm no longer works since Heroku is no longer free, per the professor. The reference link below has detailed information about the web service that we can leverage to gain that understanding.&lt;br /&gt;
&lt;br /&gt;
Reference Link for Webservice Details: [https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to rename the &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; method to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; to make the functionality of the method clear. We also plan to verify that the method is working appropriately by mocking up the service call since it is no longer working.&lt;br /&gt;
&lt;br /&gt;
==== Updated Implementation ====&lt;br /&gt;
We implemented an update using mocked data and a service as placeholders until a new URL is in place. We did not end up refactoring to rename this method as we found that the name describes the functionality quite well.&lt;br /&gt;
&lt;br /&gt;
==== review_bids_controller ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def run_bidding_algorithm&lt;br /&gt;
    review_bid = ReviewBid.new&lt;br /&gt;
    bidding_data = review_bid.bidding_data&lt;br /&gt;
    matched_topics = BiddingAlgorithmService.new(bidding_data).run&lt;br /&gt;
    raise ArgumentError, 'Failed to assign reviewers. Please try again later.' unless matched_topics&lt;br /&gt;
&lt;br /&gt;
    matched_topics&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== bidding_algorithm_service ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# frozen_string_literal: true&lt;br /&gt;
&lt;br /&gt;
# The `BiddingAlgorithmService` run the bid assignment algorithm&lt;br /&gt;
# Sends student IDs, topic IDs, student preferences, and timestamps to the web service&lt;br /&gt;
# The web service returns the matched assignments in the JSON response body&lt;br /&gt;
class BiddingAlgorithmService&lt;br /&gt;
  SERVICE_URL = 'http://app-csc517.herokuapp.com/match_topics'.freeze&lt;br /&gt;
&lt;br /&gt;
  # MOCK_DATA is based on the documentation detailing how the webservice behaves.&lt;br /&gt;
  # Reference: https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review#Webservice&lt;br /&gt;
  MOCK_DATA = {&lt;br /&gt;
    36239 =&amp;gt; [3970, 3972, 3975],&lt;br /&gt;
    36240 =&amp;gt; [3973, 3974, 3972],&lt;br /&gt;
    36241 =&amp;gt; [3969, 3971, 3972],&lt;br /&gt;
    36242 =&amp;gt; [3969, 3971, 3973],&lt;br /&gt;
    36243 =&amp;gt; [3969, 3970, 3971]&lt;br /&gt;
  }.freeze&lt;br /&gt;
&lt;br /&gt;
  def initialize(bidding_data)&lt;br /&gt;
    @bidding_data = bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def run&lt;br /&gt;
    return MOCK_DATA if Rails.application.config.use_mock_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
    perform_request&lt;br /&gt;
  rescue RestClient::ExceptionWithResponse, JSON::ParserError&lt;br /&gt;
    false&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def perform_request&lt;br /&gt;
    response = RestClient.post(&lt;br /&gt;
      SERVICE_URL,&lt;br /&gt;
      @bidding_data.to_json,&lt;br /&gt;
      content_type: :json,&lt;br /&gt;
      accept: :json&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    validate_response(response)&lt;br /&gt;
    JSON.parse(response.body)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def validate_response(response)&lt;br /&gt;
    raise 'Invalid response format' unless response.headers[:content_type] &amp;amp;&amp;amp; response.headers[:content_type].include?('application/json')&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Resolve Functionality Issues for Non Bidders ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Currently it doesn't work if some student does not bid. In this case, algorithm needs to be fixed to ignore anyone who didn’t bid, and let them choose from what’s left over.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to do several things within this controller. We will set a default state for non-bidders that we can point to to give them options to select topics to review from the leftovers. We will consolidate &amp;lt;code&amp;gt;@sign_up_topics&amp;lt;/code&amp;gt; to resolve the DRY violation. Lastly, we will remove business logic from the show method and push it to a new helper to conform to the SRP.&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # Additional method&lt;br /&gt;
&lt;br /&gt;
  # Provides variables for review bidding page&lt;br /&gt;
  def show&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id].to_i)&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @sign_up_topics = SignUpTopic.where(assignment_id: @assignment.id, private_to: nil)&lt;br /&gt;
    my_topic = SignedUpTeam.topic_id(@participant.parent_id, @participant.user_id)&lt;br /&gt;
    @sign_up_topics -= SignUpTopic.where(assignment_id: @assignment.id, id: my_topic)&lt;br /&gt;
    @num_participants = AssignmentParticipant.where(parent_id: @assignment.id).count&lt;br /&gt;
    @selected_topics = nil # This is used to list the topics assigned to review (i.e., select == assigned)&lt;br /&gt;
    @bids = ReviewBid.where(participant_id: @participant, assignment_id: @assignment.id)&lt;br /&gt;
    signed_up_topics = []&lt;br /&gt;
    &lt;br /&gt;
    @bids.each do |bid|&lt;br /&gt;
      sign_up_topic = SignUpTopic.find_by(id: bid.signuptopic_id)&lt;br /&gt;
      signed_up_topics &amp;lt;&amp;lt; sign_up_topic if sign_up_topic&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    signed_up_topics &amp;amp;= @sign_up_topics&lt;br /&gt;
    @sign_up_topics -= signed_up_topics&lt;br /&gt;
    @bids = signed_up_topics&lt;br /&gt;
    @num_of_topics = @sign_up_topics.size&lt;br /&gt;
    @assigned_review_maps = []&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: @assignment.id, reviewer_id: @participant.id).each do |review_map|&lt;br /&gt;
      @assigned_review_maps &amp;lt;&amp;lt; review_map&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Explicitly render view since it's in the sign up sheet views&lt;br /&gt;
    render 'sign_up_sheet/review_bids_show'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Additional methods&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-2.-reviewbidshelper-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
 # Assigns bidding topics to reviewers&lt;br /&gt;
  def assign_bidding&lt;br /&gt;
      assignment = validate_assignment(params[:assignment_id])&lt;br /&gt;
&lt;br /&gt;
      reviewers = validate_reviewers(assignment.id)&lt;br /&gt;
      reviewer_ids = reviewers.map(&amp;amp;:id)&lt;br /&gt;
&lt;br /&gt;
      matched_topics = run_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
      if matched_topics.blank?&lt;br /&gt;
        flash[:alert] = 'Topic or assignment is missing'&lt;br /&gt;
        redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      leftover_topics = find_leftover_topics(assignment.id, matched_topics)&lt;br /&gt;
      assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.new.assign_review_topics(matched_topics)&lt;br /&gt;
      assignment.update!(can_choose_topic_to_review: false)&lt;br /&gt;
&lt;br /&gt;
      flash[:notice] = 'Reviewers were successfully assigned to topics.'&lt;br /&gt;
      redirect_back fallback_location: root_path&lt;br /&gt;
    rescue ArgumentError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ArgumentError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: e.message&lt;br /&gt;
    rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::RecordInvalid: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue ActiveRecord::ActiveRecordError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::ActiveRecordError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue StandardError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;StandardError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers. Please try again later.'&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  #service methods here ...&lt;br /&gt;
&lt;br /&gt;
  def find_leftover_topics(assignment_id, matched_topics)&lt;br /&gt;
    all_topic_ids = SignUpTopic.where(assignment_id: assignment_id).pluck(:id)&lt;br /&gt;
    assigned_topic_ids = matched_topics.map { |match| match[:topic_id] }&lt;br /&gt;
&lt;br /&gt;
    # Calculate leftover topics&lt;br /&gt;
    all_topic_ids - assigned_topic_ids&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
    return if leftover_topics.blank?&lt;br /&gt;
&lt;br /&gt;
    # Find non-bidders by excluding already matched reviewers&lt;br /&gt;
    non_bidders = reviewer_ids - matched_topics.keys&lt;br /&gt;
&lt;br /&gt;
    # Assign leftover topics to non-bidders in a round-robin fashion&lt;br /&gt;
    non_bidders.each_with_index do |reviewer_id, index|&lt;br /&gt;
      topic_id = leftover_topics[index % leftover_topics.length]&lt;br /&gt;
      ReviewBid.create(priority: 1, signuptopic_id: topic_id, participant_id: reviewer_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirm Functionality for Single-person Teams ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Make sure your code works on individual assignments, i.e., assignments where participants sign up for topics instead of teams. In this case, a team is created for each participant when they sign up. So the code should work for assignments to which either individuals or teams submit.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to make an update to focus on topic rather than team or have it where teams are updated based on topic id.We will probably utilize sign up topic or a similar object to accomplish this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-5-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bid Status Messaging ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| To make the functionality more intuitive, include a message to say how many students are eligible to submit bids, how many have submitted their bids, when the deadline for submitting bids is.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
The logic for these messages will go into the helper file. It is effectively the same reasoning as our migration of other business/functionality code from the controller to the helper to conform to the SRP. It will go hand in hand with the SignUpTopic portion of the code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-6-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Why are the methods in review_bid.rb class methods? Can we change them to instance methods or move it to helpers?&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-1.-reviewbid-model-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model Updates ====&lt;br /&gt;
Since the current class methods are tied to &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model instances, we will change them to instance methods. There is no need to move them to helpers.&lt;br /&gt;
&lt;br /&gt;
===== Review Bid Model Flow Before Refactoring =====&lt;br /&gt;
This sequence diagram describes how the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; interacts with the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model in assigning review topics to reviewers according to their bids. Once the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; action is initiated by the user, the controller gathers the IDs of reviewers and calls &amp;lt;code&amp;gt;ReviewBid.bidding_data&amp;lt;/code&amp;gt; to gather bidding information. That is sent out to an external bidding algorithm, and the matched topics that come back are used by &amp;lt;code&amp;gt;ReviewBid.assign_review_topics&amp;lt;/code&amp;gt; to create &amp;lt;code&amp;gt;ReviewResponseMap&amp;lt;/code&amp;gt; entries, assigning topics to reviewers for the assignment.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
  belongs_to :assignment, class_name: 'Assignment'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  def self.bidding_data(assignment_id, reviewer_ids)&lt;br /&gt;
    # create basic hash and set basic hash data&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'users' =&amp;gt; {}, 'max_accepted_proposals' =&amp;gt; [] }&lt;br /&gt;
    bidding_data['tid'] = SignUpTopic.where(assignment_id: assignment_id).ids&lt;br /&gt;
    bidding_data['max_accepted_proposals'] = Assignment.where(id: assignment_id).pluck(:num_reviews_allowed).first&lt;br /&gt;
&lt;br /&gt;
    # loop through reviewer_ids to get reviewer specific bidding data&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      bidding_data['users'][reviewer_id] = reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # assigns topics to reviews as matched by the webservice algorithm&lt;br /&gt;
  def self.assign_review_topics(assignment_id, reviewer_ids, matched_topics, _min_num_reviews = 2)&lt;br /&gt;
    # if review response map already created, delete it&lt;br /&gt;
    if ReviewResponseMap.where(reviewed_object_id: assignment_id)&lt;br /&gt;
      ReviewResponseMap.where(reviewed_object_id: assignment_id).destroy_all&lt;br /&gt;
    end&lt;br /&gt;
    # loop through reviewer_ids to assign reviews to each reviewer&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      topics_to_assign = matched_topics[reviewer_id.to_s]&lt;br /&gt;
      topics_to_assign.each do |topic|&lt;br /&gt;
        assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method to assign a single topic to a reviewer&lt;br /&gt;
  def self.assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic).pluck(:team_id).first&lt;br /&gt;
    team_to_review.nil? ? [] : ReviewResponseMap.create(reviewed_object_id: assignment_id, reviewer_id: reviewer_id, reviewee_id: team_to_review, type: 'ReviewResponseMap')&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method for getting individual reviewer_ids bidding data&lt;br /&gt;
  # returns user's bidding data hash&lt;br /&gt;
  def self.reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    reviewer_user_id = AssignmentParticipant.find(reviewer_id).user_id&lt;br /&gt;
    self_topic = SignedUpTeam.topic_id(assignment_id, reviewer_user_id)&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'otid' =&amp;gt; self_topic, 'priority' =&amp;gt; [], 'time' =&amp;gt; [] }&lt;br /&gt;
    bids = ReviewBid.where(participant_id: reviewer_id)&lt;br /&gt;
&lt;br /&gt;
    # loop through each bid for a topic to get specific data&lt;br /&gt;
    bids.each do |bid|&lt;br /&gt;
      bidding_data['tid'] &amp;lt;&amp;lt; bid.signuptopic_id&lt;br /&gt;
      bidding_data['priority'] &amp;lt;&amp;lt; bid.priority&lt;br /&gt;
      bidding_data['time'] &amp;lt;&amp;lt; bid.updated_at&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  # Instance method to get bidding data for this review bid&lt;br /&gt;
  def bidding_data&lt;br /&gt;
    {&lt;br /&gt;
      'tid' =&amp;gt; topic_ids,&lt;br /&gt;
      'otid' =&amp;gt; self_topic,&lt;br /&gt;
      'priority' =&amp;gt; priorities,&lt;br /&gt;
      'time' =&amp;gt; updated_times&lt;br /&gt;
    }&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # bid object to limit db touches for similar data acquisitions&lt;br /&gt;
  def bids&lt;br /&gt;
    @bids ||= ReviewBid.where(participant_id: participant_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def topic_ids&lt;br /&gt;
    bids.pluck(:signuptopic_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def priorities&lt;br /&gt;
    bids.pluck(:priority)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def updated_times&lt;br /&gt;
    bids.pluck(:updated_at)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self_topic&lt;br /&gt;
    return nil unless topic &amp;amp;&amp;amp; topic.assignment_id&lt;br /&gt;
&lt;br /&gt;
    SignedUpTeam.topic_id(topic.assignment_id, participant.user_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign topics to reviews&lt;br /&gt;
  def assign_review_topics(matched_topics)&lt;br /&gt;
    validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: topic.assignment_id).destroy_all&lt;br /&gt;
&lt;br /&gt;
    matched_topics.each do |reviewer_id, topics|&lt;br /&gt;
      Array(topics).each { |topic_id| assign_topic_to_reviewer(reviewer_id, topic_id) }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign a single topic to a reviewer&lt;br /&gt;
  def assign_topic_to_reviewer(reviewer_id, topic_id)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic_id).pluck(:team_id).first&lt;br /&gt;
    return if team_to_review.nil?&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.create(&lt;br /&gt;
      reviewed_object_id: topic.assignment_id,&lt;br /&gt;
      reviewer_id: reviewer_id,&lt;br /&gt;
      reviewee_id: team_to_review,&lt;br /&gt;
      type: 'ReviewResponseMap'&lt;br /&gt;
    )&lt;br /&gt;
    # error handling&lt;br /&gt;
  rescue StandardError =&amp;gt; e&lt;br /&gt;
    Rails.logger.error(&amp;quot;Failed to assign topic #{topic_id} to reviewer #{reviewer_id}: #{e.message}&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
    raise ArgumentError, 'Topic or assignment is missing' if topic.nil? || topic.assignment_id.nil?&lt;br /&gt;
    raise ArgumentError, 'Matched topics must be a Hash' unless matched_topics.is_a?(Hash)&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
Once we change them to instance methods, we’ll need to modify their use in the code to instantiate the object first.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;Placeholder for implementation screenshots (before and after)&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-3.-schema.db-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;Schema.db&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We also need to remove the foreign key reference to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model since the &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model already has access to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;, and that is the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; we should be referencing. This will also involve modifying the database to remove the foreign key reference to the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Steps required:&lt;br /&gt;
&lt;br /&gt;
# Review the records in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table in the database to see if there are any records and if those records have values for &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;&lt;br /&gt;
# Create a migration file using the command: &amp;lt;code&amp;gt;rails generate migration RemoveAssignmentFromReviewBids&amp;lt;/code&amp;gt;&lt;br /&gt;
# In the generated file, indicate the change that needs to be made using the change method and migration columns &amp;lt;code&amp;gt;remove_foreign_key&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;remove_column&amp;lt;/code&amp;gt;. Migration should be straightforward since there is no cascading behavior (i.e., &amp;lt;code&amp;gt;dependent:destroy&amp;lt;/code&amp;gt;) to worry about.&lt;br /&gt;
# Once comfortable with the updates to the generated file, run the migration using the &amp;lt;code&amp;gt;rails db:migrate&amp;lt;/code&amp;gt; command.&lt;br /&gt;
# Run a query in the DB to ensure no orphaned records are in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table after the updates.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;Placeholder for implementation screenshots (before and after)&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-4.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implement Edge Case Testing ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| In the previous implementation wiki, there are edge cases which are not exhaustively tested. Should test those edge cases thoroughly and add more edge case testing - link&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span class=&amp;quot;mark&amp;quot;&amp;gt;TODO&amp;lt;/span&amp;gt;:We just got initial test results back. We still need to figure out why tests are failing and what may be slipping through the cracks.[[File:Test expertiza.png|411x459px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-8-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design Patterns and Principles ===&lt;br /&gt;
Below are the design patterns and principles we plan to use to address the problem statements outlined in the project requirements:&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
** Controller Responsibility&lt;br /&gt;
*** To address the issue of authentication logic residing in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, we will refactor the code to ensure the controller solely handles request and response flows.&lt;br /&gt;
*** Authentication logic will be moved to dedicated methods in &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt;, making the codebase easier to maintain and test.&lt;br /&gt;
** View Simplification&lt;br /&gt;
*** Currently, the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; view contains excessive business logic, violating SRP.&lt;br /&gt;
*** We will move this logic into helper methods or models, ensuring each component has a single responsibility and improving code readability.&lt;br /&gt;
&lt;br /&gt;
* Separation of Concerns (SoC)&lt;br /&gt;
** By relocating authorization logic out of the controller, we separate authentication from request handling.&lt;br /&gt;
** Adhering to SoC enhances readability and maintainability by allowing each part of the application to focus on its specific role.&lt;br /&gt;
* Don't Repeat Yourself (DRY)&lt;br /&gt;
** We will inspect the existing implementation for code duplication, particularly in the controller actions.&lt;br /&gt;
** Any repetitive code will be refactored or removed to reduce redundancy and improve clarity.&lt;br /&gt;
&lt;br /&gt;
* Model-View-Controller (MVC) Adherence&lt;br /&gt;
** Moving business logic from the view aligns with MVC best practices.&lt;br /&gt;
** By ensuring the view only presents data without processing it, we maintain clear boundaries between the model, view, and controller layers.&lt;br /&gt;
&lt;br /&gt;
* Dependency Inversion Principle (DIP)&lt;br /&gt;
** We plan to update class methods to instance methods, allowing the controller to depend on abstractions rather than concrete implementations.&lt;br /&gt;
** This change leverages Ruby's duck typing, providing flexibility and making the code easier to maintain and test.&lt;br /&gt;
&lt;br /&gt;
* Factory Pattern&lt;br /&gt;
** We will implement a factory pattern for the common processing of &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpBid&amp;lt;/code&amp;gt;, encapsulating object creation and promoting code reuse.&lt;br /&gt;
&lt;br /&gt;
* Law of Demeter (Principle of Least Knowledge)&lt;br /&gt;
** We'll delegate interactions appropriately to prevent tightly coupled code, especially when dealing with topics and teams.&lt;br /&gt;
** Clear and intentional naming during refactoring will enhance code clarity and maintainability.&lt;br /&gt;
&lt;br /&gt;
* Adapter Pattern&lt;br /&gt;
** To add messaging to bid states for users, we'll use the adapter pattern to allow different components to work together without modifying their existing code.&lt;br /&gt;
&lt;br /&gt;
== Files Modified/Added ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-1.-controllers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
review_bids_controller.rb&lt;br /&gt;
&lt;br /&gt;
signup_sheet_controller.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-2.-models&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
review_bid.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-3.-helpers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
&lt;br /&gt;
authorization_helper.rb&lt;br /&gt;
&lt;br /&gt;
Review_bids_helper.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;viii.-test-plan&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
The Excel spreadsheet below details our test plan for testing the code we changed or created to ensure code coverage. The process involved reviewing the code and determining all the possible scenarios that needed to be covered based on what the method was doing, then creating test cases accordingly.&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/spreadsheets/d/1iHzOdSwlTtxOtf5VsNdwOaCP8NzYMmiJ/edit?usp=drive_link Open Test Matrix Spreadsheet]&lt;br /&gt;
&lt;br /&gt;
== Test Coverage ==&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;screenshot of SimpleCov coverage analysis&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x.-team&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-1.-mentor&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Ed Gehringer&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-2.-members&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Members ===&lt;br /&gt;
&lt;br /&gt;
* Gavin Teague&lt;br /&gt;
* Janice Uwujaren&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xi.-links&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
Previous Wiki: [https://wiki.expertiza.ncsu.edu/index.php/CSC/ECE_517_Fall_2021_-_E2151._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;Link&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xii.-references&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/document/d/1LHVacylQmB15I6VcYUA5nuC7GRRU9szHzZhjlPcE-PA/edit?tab=t.0#heading=h.u0oycoj6e0ev &amp;lt;u&amp;gt;Project Instructions&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;appendix-a-uml-class-diagram-plantuml-code-for-review-bids&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Juwujar</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160947</id>
		<title>CSC/ECE 517 Fall 2024 - E2485. Allow reviewers to bid on what to review</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160947"/>
		<updated>2024-12-10T15:01:47Z</updated>

		<summary type="html">&lt;p&gt;Juwujar: /* ReviewBidsController Updates */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
This project will fix the defects and improve the functionality in Expertiza of the Reviewer Bidding feature, a process where reviewers can bid for assignments they want to review. It will make the functionality smoother, more reliable, and align with Expertiza's collaborative learning objectives.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iii.-problem-statements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Problem Statements ==&lt;br /&gt;
&lt;br /&gt;
Assigning reviews to users on Expertiza involves a complex bidding algorithm that requires optimization before production deployment. Below are key areas we plan to address as per the project requirements:&lt;br /&gt;
* Code Refactoring and Best Practices&lt;br /&gt;
** '''Utilize Authentication Utilities''': Modify the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; function in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to use existing authentication utilities, enhancing code reusability and adhering to the Single Responsibility Principle (SRP) and Separation of Concerns (SoC).&lt;br /&gt;
** '''Eliminate Code Duplication''': Refactor &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; to comply with the Don't Repeat Yourself (DRY) principle.&lt;br /&gt;
** '''Rename Functions for Clarity''': Change &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; to better reflect its purpose.&lt;br /&gt;
** '''Refactor Methods in '''&amp;lt;code&amp;gt;review_bid.rb&amp;lt;/code&amp;gt;''':''': Convert class methods to instance methods or relocate them to helper classes for improved code organization.&lt;br /&gt;
* Functionality Enhancements&lt;br /&gt;
** '''Handle Missing Bids Gracefully''': Adjust the bidding algorithm to accommodate students who do not bid, allowing them to select from the remaining options.&lt;br /&gt;
** '''Support Both Individual and Team Assignments''': Ensure the code functions correctly for participant and team-based assignments when signing up for topics.&lt;br /&gt;
* User Interface Improvements&lt;br /&gt;
** '''Increase Transparency in the Bidding Process''': Display messages indicating how many students are eligible to bid, how many have submitted bids, and the deadline.&lt;br /&gt;
** '''Accurate Bid Status Indicators''': Ensure topic colors change correctly based on the number of outstanding bids, providing clear visual feedback to users.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Exhaustive Edge Case Testing''': Conduct thorough testing for edge cases not previously covered, adding additional test cases as needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iv.-design-goals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Design Goals ==&lt;br /&gt;
&lt;br /&gt;
While addressing the existing defects and enhancing the Reviewer Bidding feature, we plan to adhere to the following design guidelines:&lt;br /&gt;
&lt;br /&gt;
* Code Quality and Maintainability&lt;br /&gt;
** '''Improve Code Structure: '''Implement refactoring strategies to enhance code readability and maintainability, ensuring adherence to best practices like the DRY principle.&lt;br /&gt;
** '''Standardize Authentication Logic: '''Utilize a centralized authentication utility to promote consistency and reusability across the application.&lt;br /&gt;
* System Stability and Flexibility&lt;br /&gt;
** '''Enhance Algorithm Robustness: '''Modify the bidding algorithm to handle scenarios where students do not bid, ensuring the system remains stable under all conditions.&lt;br /&gt;
** '''Ensure Assignment Versatility: '''Design the system to seamlessly support both individual and team assignments without additional configuration.&lt;br /&gt;
* User Experience Enhancements&lt;br /&gt;
** '''Increase Bidding Transparency: '''Develop user interface elements that clearly display bidding status, deadlines, and participation metrics.&lt;br /&gt;
** '''Implement Dynamic Visual Feedback: '''Use real-time visual cues, such as color changes, to inform users about the bidding status instantly.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Develop Comprehensive Test Suites: '''Create extensive automated tests covering edge cases and critical functionalities to guarantee reliable system behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;v.-uml-diagram&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== UML Diagram ==&lt;br /&gt;
&lt;br /&gt;
Below is a visual representation of the models associated with the review bids functionality, depicting the data structures and their relationships.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml class diagram2.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi.-implementation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
'''ReviewBid: '''The &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; manages the bidding process, where participants indicate their preferences for reviewing different topics or assignments. This helps distribute review tasks fairly based on participants' interests.&lt;br /&gt;
&lt;br /&gt;
'''Assignment: '''The &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model represents team or individual assignments in Expertiza. It stores all the essential information required to keep the assignment process organized, like managing participants, setting due dates, forming teams, and overseeing the review process.&lt;br /&gt;
&lt;br /&gt;
'''SignUpTopic: '''The &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt; model identifies specific topics or areas within an assignment for which participants can sign up. It manages the availability of these topics, tracks bids, coordinates team sign-ups, and assigns topics based on both bids and participant availability. &lt;br /&gt;
&lt;br /&gt;
'''Participant: '''The &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model is about the individual users who participate in an assignment. This includes students, instructors, teaching assistants, or other relevant roles. This model is responsible for managing key user information, monitoring interactions with the assignment, keeping track of team memberships, and recording things like badges and review grades.&lt;br /&gt;
&lt;br /&gt;
== Implementation ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1.-decoupling-authorization-logic-from-reviewbidscontroller&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Decoupling Authorization Logic from &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| '''&amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; needs to use the authentication utilities'''&lt;br /&gt;
&lt;br /&gt;
The function &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; should use the available authentication utilities to support the Single Responsibility Principle and Separation of Concerns and to support code reusability.&lt;br /&gt;
&lt;br /&gt;
| '''Authentication Consistency:''' Remove authentication logic in the controller and call logic in the authentication utility class instead to ensure consistent use of the same logic across the application, foster reusability, and eliminate DRY.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
==== Authorization Flow Before Refactoring ====&lt;br /&gt;
The sequence diagram depicts the authorization process within the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;. When a user invokes an action, the controller checks if the action is allowed by verifying the user's role through the &amp;lt;code&amp;gt;current_role_name()&amp;lt;/code&amp;gt; method in the &amp;lt;code&amp;gt;ApplicationController&amp;lt;/code&amp;gt;. Depending on the action and the user's role, the controller either authorizes the user to proceed or denies access, ultimately executing the action logic if authorized and responding to the user.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 1.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We simplified the &amp;lt;code&amp;gt;action_allowed?&amp;lt;/code&amp;gt; method by decoupling authorization logic from the controller to use helper methods in the &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt; module. The &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; should not handle authentication, which is a violation of SRP. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;vertical-align:top;&amp;quot; | Original Code !! style=&amp;quot;vertical-align:top;&amp;quot; | Refactored Code&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index'&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator',&lt;br /&gt;
     'Student'].include? current_role_name and&lt;br /&gt;
        ((%w[list].include? action_name) ? are_needed_authorizations_present?(params[:id], &amp;quot;participant&amp;quot;, &amp;quot;reader&amp;quot;, &amp;quot;submitter&amp;quot;, &amp;quot;reviewer&amp;quot;) : true)&lt;br /&gt;
  else&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator'].include? current_role_name&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index', 'list'&lt;br /&gt;
    current_user_has_student_privileges? &amp;amp;&amp;amp; list_authorization_check&lt;br /&gt;
  else&lt;br /&gt;
    current_user_has_ta_privileges?&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; action ===&lt;br /&gt;
The refactor of the &amp;lt;code&amp;gt;ReviewBidsController's index&amp;lt;/code&amp;gt; action simplifies and streamlines the codebase, ensuring the action simply handles controller activities. Previously, the controller was overloaded with responsibilities—it handled everything from locating and validating assignments to fetching review mappings and counting completed reviews. This all-in-one approach made the controller action messy. &lt;br /&gt;
&lt;br /&gt;
Now we have moved the business logic out of the controller action and into dedicated service objects. With this change, we introduced two key service objects: &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; focuses on retrieving the participant along with their assignments, while the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; takes on the task of managing review mappings and calculating review-related stats, like how many reviews have been completed. &lt;br /&gt;
&lt;br /&gt;
This clear separation of responsibilities aligns with the Single Responsibility Principle, which helps each part of our code be more focused and easier to understand. With these new service objects, the code has become more reusable. The logic for fetching assignments, retrieving review mappings, and counting reviews is now more flexible and reusable throughout the application. For example, we used methods in the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; in both the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; and the &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;, helping to avoid code duplication.&lt;br /&gt;
&lt;br /&gt;
After moving the business logic to service objects, we can write unit tests to ensure each part of the code behaves as expected and prevents bugs. We also moved complex calculations, like those for completed reviews, into the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;, freeing the controller from dealing with the implementation details and promoting a cleaner design. The article https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial provides more information on when and how to use service objects in Rails.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
  return unless current_user_id?(@participant.user_id)&lt;br /&gt;
  @assignment = @participant.assignment&lt;br /&gt;
  @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
  # Finding how many reviews have been completed&lt;br /&gt;
  @num_reviews_completed = 0&lt;br /&gt;
  @review_mappings.each do |map|&lt;br /&gt;
    @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # render view for completing reviews after review bidding has been completed&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @assignment = participant_service.assignment&lt;br /&gt;
  unless @assignment.is_a?(Assignment)&lt;br /&gt;
    flash[:error] = 'Assignment not found.'&lt;br /&gt;
    redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  @review_mappings = review_service.review_mappings&lt;br /&gt;
  @num_reviews_completed = review_service.review_counts[:completed]&lt;br /&gt;
&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Optimize &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Action functionality ===&lt;br /&gt;
In the earlier version of the set_priority action in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, the server only needed a couple of key pieces of information: an array of topic IDs (params[:topic]) and the participant ID (params[:id}). From there, it would figure out the assignment_id by using the first topic as a reference. While this worked in simple cases, it could lead to problems if any data was missing or didn’t line up correctly.&lt;br /&gt;
&lt;br /&gt;
With the new implementation, the action now expects clear and structured parameters, including participant_id, an array of topic IDs, and assignment_id. This change ensures that the server always receives complete and reliable information, preventing issues caused by missing or inconsistent data. It also removes the need for the server to perform unnecessary lookups, allowing the controller to focus on its main job: validating inputs (like making sure the topics in params[:topic] belong to the same assignment as the participant), updating the database, handling errors properly, and sending the correct response back to the view.&lt;br /&gt;
&lt;br /&gt;
While these updates have cleaned up the set_priority action, they still contain a lot of business logic, like validations and priority updates. To make the code even more maintainable, there’s room for further refactoring—perhaps moving some of this logic into a service class or model method to streamline the controller even more.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | Old Code&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    if params[:topic].nil?&lt;br /&gt;
      ReviewBid.where(participant_id: params[:id]).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      participant = AssignmentParticipant.find_by(id: params[:id])&lt;br /&gt;
      assignment_id = SignUpTopic.find(params[:topic].first).assignment.id&lt;br /&gt;
      @bids = ReviewBid.where(participant_id: params[:id])&lt;br /&gt;
      signed_up_topics = ReviewBid.where(participant_id: params[:id]).map(&amp;amp;:signuptopic_id)&lt;br /&gt;
      signed_up_topics -= params[:topic].map(&amp;amp;:to_i)&lt;br /&gt;
      signed_up_topics.each do |topic|&lt;br /&gt;
        ReviewBid.where(signuptopic_id: topic, participant_id: params[:id]).destroy_all&lt;br /&gt;
      end&lt;br /&gt;
      params[:topic].each_with_index do |topic_id, index|&lt;br /&gt;
        bid_existence = ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id])&lt;br /&gt;
        if bid_existence.empty?&lt;br /&gt;
          ReviewBid.create(priority: index + 1, signuptopic_id: topic_id, participant_id: params[:id], assignment_id: assignment_id)&lt;br /&gt;
        else&lt;br /&gt;
          ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id]).update_all(priority: index + 1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to action: 'show', assignment_id: params[:assignment_id], id: params[:id]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    participant_id = params[:participant_id].to_i&lt;br /&gt;
    selected_topic_ids = params[:topic]&amp;amp;.map(&amp;amp;:to_i) || []&lt;br /&gt;
    assignment_id = params[:assignment_id].to_i&lt;br /&gt;
&lt;br /&gt;
    unless current_user_can_modify_participant?(participant_id)&lt;br /&gt;
      flash[:error] = 'You are not authorized to modify these bids.'&lt;br /&gt;
      respond_to do |format|&lt;br /&gt;
        format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
        format.json { render json: { status: 'unauthorized' }, status: :unauthorized }&lt;br /&gt;
      end&lt;br /&gt;
      return&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    if selected_topic_ids.empty?&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      assignment = Assignment.find_by(id: assignment_id)&lt;br /&gt;
      if assignment.nil?&lt;br /&gt;
        flash[:error] = &amp;quot;Invalid assignment.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_assignment' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      unless SignUpTopic.where(id: selected_topic_ids, assignment_id: assignment_id).count == selected_topic_ids.size&lt;br /&gt;
        flash[:error] = &amp;quot;One or more selected topics are invalid.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_topics' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).where.not(signuptopic_id: selected_topic_ids).destroy_all&lt;br /&gt;
&lt;br /&gt;
      selected_topic_ids.each_with_index do |topic_id, index|&lt;br /&gt;
        bid = ReviewBid.find_or_initialize_by(signuptopic_id: topic_id, participant_id: participant_id)&lt;br /&gt;
        bid.priority = index + 1&lt;br /&gt;
        bid.assignment_id = assignment_id&lt;br /&gt;
        bid.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_to action: 'show', assignment_id: assignment_id, id: participant_id, notice: 'Review bids updated successfully.' }&lt;br /&gt;
      format.json { render json: { status: 'success' }, status: :ok }&lt;br /&gt;
    end&lt;br /&gt;
  rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
    flash[:error] = &amp;quot;Failed to update priorities: #{e.message}&amp;quot;&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
      format.json { render json: { status: 'error', message: e.message }, status: :unprocessable_entity }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Client Side Changes to Support &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Changes ====&lt;br /&gt;
After tidying up the server-side logic, we needed to refactor the client-side code to ensure it passed the correct information from the view to the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action. On the client side, we now grab the topicIDs and send them as &amp;lt;code&amp;gt;selected_topic_ids&amp;lt;/code&amp;gt;, allowing the controller to figure out which topics were selected and in what order. It’s a cleaner and more reliable way to keep everything working together! &lt;br /&gt;
&lt;br /&gt;
We are also extracting the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, making it a lot easier to send data with AJAX and keeping things flexible. AJAX is awesome because it lets us update the server without reloading the page, making the user experience smooth and responsive. When we pull the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, we can grab it along with other data, like the list of topics, and include it in the request. This way, we don’t have to rely on the URL to know which participant we’re working with, which makes the code cleaner and more adaptable.&lt;br /&gt;
&lt;br /&gt;
If we were to rely on the route for the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt;, every AJAX request would need to be tied to a specific URL structure. That’s limiting, especially if the route doesn’t naturally include the participant ID, as can be the case with collection routes versus member routes in Rails. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery -&amp;gt;&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: -&amp;gt;&lt;br /&gt;
      $.post($(this).data('update-url'), $(this).sortable('serialize'))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery(document).ready ($) -&amp;gt;&lt;br /&gt;
  sendUpdatedOrder = -&amp;gt;&lt;br /&gt;
    sortedIDs = $(this).sortable('toArray')&lt;br /&gt;
&lt;br /&gt;
    topicIDs = sortedIDs.map (id) -&amp;gt;&lt;br /&gt;
      id.replace 'topic_', ''&lt;br /&gt;
&lt;br /&gt;
    participant_id = $(this).data('participant-id')&lt;br /&gt;
&lt;br /&gt;
    assignment_id = new URL($(this).data('update-url'), window.location.origin).searchParams.get('assignment_id')&lt;br /&gt;
&lt;br /&gt;
    $.ajax&lt;br /&gt;
      type: 'POST'&lt;br /&gt;
      url: $(this).data('update-url')&lt;br /&gt;
      data:&lt;br /&gt;
        topic: topicIDs&lt;br /&gt;
        participant_id: participant_id&lt;br /&gt;
        assignment_id: assignment_id&lt;br /&gt;
      success: (response) -&amp;gt;&lt;br /&gt;
        console.log 'Selections updated successfully.'&lt;br /&gt;
      error: (xhr, status, error) -&amp;gt;&lt;br /&gt;
        alert 'An error occurred while saving your selections. Please try again.'&lt;br /&gt;
        console.error error&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: sendUpdatedOrder&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-1.-signupsheetcontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; Updates ===&lt;br /&gt;
&lt;br /&gt;
The ReviewBidsController's index method renders the review_bids_others_work action in &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt;. However, the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; does not include the controller action for this view. We need to find the old implementation of the controller action, add it back, and review and update it for any DRY violations.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # provides variables for reviewing page located at views/review_bids/others_work.html.erb&lt;br /&gt;
  def index&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
    return unless current_user_id?(@participant.user_id)&lt;br /&gt;
&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
    # Finding how many reviews have been completed&lt;br /&gt;
    @num_reviews_completed = 0&lt;br /&gt;
    @review_mappings.each do |map|&lt;br /&gt;
      @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # render view for completing reviews after review bidding has been completed&lt;br /&gt;
    render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
  end  &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-2.-reviewbidsotherswork-view-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;review_bids_others_work.html.erb&amp;lt;/code&amp;gt; View Updates ====&lt;br /&gt;
&lt;br /&gt;
The code only contains a view with this name and no associated controller action. Reviewing the code in the view, we noticed some SRP violations where there is much logic in the presentation layer. Set up helper methods in the controller to handle some of that logic or see if the model can handle some of that logic.&lt;br /&gt;
&lt;br /&gt;
Note: The view is technically part of the SignUpSheetController, but it's being rendered by the ReviewBidsController, which initially threw us off. After chatting with the professor, we decided not to change this view. He indicated that the plan is to reuse these views for both assignment bidding and review bidding.&lt;br /&gt;
&lt;br /&gt;
We would suggest building the assignment bidding and review bidding features separately first to make sure each one works on its own. Once that’s done, we can go back and clean things up by combining any shared functionality. This could include creating shared views or even introducing an abstract model to handle both types of bidding in a cleaner way. It could also mean replacing the ReviewBidsController with something more generic, like a BidsController, to keep things reusable and easier to maintain down the line.&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Rename &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; method ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; should be &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt;&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:''' Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The primary activity here is to refactor the method name &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; and fix the hardcoded URL. However, the match topic bidding algorithm no longer works since Heroku is no longer free, per the professor. The reference link below has detailed information about the web service that we can leverage to gain that understanding.&lt;br /&gt;
&lt;br /&gt;
Reference Link for Webservice Details: [https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to rename the &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; method to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; to make the functionality of the method clear. We also plan to verify that the method is working appropriately by mocking up the service call since it is no longer working.&lt;br /&gt;
&lt;br /&gt;
Note: The original requirement was to rename the run_bidding_algorithm method to assign_reviewers, but after digging into the code, we realized that renaming it didn’t make sense. The method runs the bidding algorithm to match topics, and there’s already a separate method to handle assigning review topics.&lt;br /&gt;
&lt;br /&gt;
However, we improved the logic in the assign_bidding method (which is called by the run_bidding_algorithm) by introducing a separate BiddingAlgorithmService object. This keeps the business logic out of the controller action, which makes things cleaner and more maintainable.&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # Other methods&lt;br /&gt;
&lt;br /&gt;
  # Call webserver for running the assigning algorithm&lt;br /&gt;
  # Passing to webserver:&lt;br /&gt;
  # - student_ids&lt;br /&gt;
  # - topic_ids&lt;br /&gt;
  # - student_preferences&lt;br /&gt;
  # - time_stamps&lt;br /&gt;
  # Webserver returns:&lt;br /&gt;
  # - matched assignments as JSON body&lt;br /&gt;
  def run_bidding_algorithm(bidding_data)&lt;br /&gt;
    url = 'http://app-csc517.herokuapp.com/match_topics' # Hard coding for the time being&lt;br /&gt;
    response = RestClient.post url, bidding_data.to_json, content_type: 'application/json', accept: :json&lt;br /&gt;
    JSON.parse(response.body)&lt;br /&gt;
  rescue StandardError =&amp;gt; e&lt;br /&gt;
    Rails.logger.error &amp;quot;Bidding algorithm failed: #{e.message}&amp;quot;&lt;br /&gt;
    false&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Updated Implementation ====&lt;br /&gt;
We implemented an update using mocked data and a service as placeholders until a new URL is in place. We did not end up refactoring to rename this method as we found that the name describes the functionality quite well.&lt;br /&gt;
&lt;br /&gt;
==== review_bids_controller ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def run_bidding_algorithm&lt;br /&gt;
    review_bid = ReviewBid.new&lt;br /&gt;
    bidding_data = review_bid.bidding_data&lt;br /&gt;
    matched_topics = BiddingAlgorithmService.new(bidding_data).run&lt;br /&gt;
    raise ArgumentError, 'Failed to assign reviewers. Please try again later.' unless matched_topics&lt;br /&gt;
&lt;br /&gt;
    matched_topics&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== bidding_algorithm_service ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# frozen_string_literal: true&lt;br /&gt;
&lt;br /&gt;
# The `BiddingAlgorithmService` run the bid assignment algorithm&lt;br /&gt;
# Sends student IDs, topic IDs, student preferences, and timestamps to the web service&lt;br /&gt;
# The web service returns the matched assignments in the JSON response body&lt;br /&gt;
class BiddingAlgorithmService&lt;br /&gt;
  SERVICE_URL = 'http://app-csc517.herokuapp.com/match_topics'.freeze&lt;br /&gt;
&lt;br /&gt;
  # MOCK_DATA is based on the documentation detailing how the webservice behaves.&lt;br /&gt;
  # Reference: https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review#Webservice&lt;br /&gt;
  MOCK_DATA = {&lt;br /&gt;
    36239 =&amp;gt; [3970, 3972, 3975],&lt;br /&gt;
    36240 =&amp;gt; [3973, 3974, 3972],&lt;br /&gt;
    36241 =&amp;gt; [3969, 3971, 3972],&lt;br /&gt;
    36242 =&amp;gt; [3969, 3971, 3973],&lt;br /&gt;
    36243 =&amp;gt; [3969, 3970, 3971]&lt;br /&gt;
  }.freeze&lt;br /&gt;
&lt;br /&gt;
  def initialize(bidding_data)&lt;br /&gt;
    @bidding_data = bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def run&lt;br /&gt;
    return MOCK_DATA if Rails.application.config.use_mock_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
    perform_request&lt;br /&gt;
  rescue RestClient::ExceptionWithResponse, JSON::ParserError&lt;br /&gt;
    false&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def perform_request&lt;br /&gt;
    response = RestClient.post(&lt;br /&gt;
      SERVICE_URL,&lt;br /&gt;
      @bidding_data.to_json,&lt;br /&gt;
      content_type: :json,&lt;br /&gt;
      accept: :json&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    validate_response(response)&lt;br /&gt;
    JSON.parse(response.body)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def validate_response(response)&lt;br /&gt;
    raise 'Invalid response format' unless response.headers[:content_type] &amp;amp;&amp;amp; response.headers[:content_type].include?('application/json')&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Resolve Functionality Issues for Non Bidders ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Currently it doesn't work if some student does not bid. In this case, algorithm needs to be fixed to ignore anyone who didn’t bid, and let them choose from what’s left over.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to do several things within this controller. We will set a default state for non-bidders that we can point to to give them options to select topics to review from the leftovers. We will consolidate &amp;lt;code&amp;gt;@sign_up_topics&amp;lt;/code&amp;gt; to resolve the DRY violation. Lastly, we will remove business logic from the show method and push it to a new helper to conform to the SRP.&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # Additional method&lt;br /&gt;
&lt;br /&gt;
  # Provides variables for review bidding page&lt;br /&gt;
  def show&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id].to_i)&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @sign_up_topics = SignUpTopic.where(assignment_id: @assignment.id, private_to: nil)&lt;br /&gt;
    my_topic = SignedUpTeam.topic_id(@participant.parent_id, @participant.user_id)&lt;br /&gt;
    @sign_up_topics -= SignUpTopic.where(assignment_id: @assignment.id, id: my_topic)&lt;br /&gt;
    @num_participants = AssignmentParticipant.where(parent_id: @assignment.id).count&lt;br /&gt;
    @selected_topics = nil # This is used to list the topics assigned to review (i.e., select == assigned)&lt;br /&gt;
    @bids = ReviewBid.where(participant_id: @participant, assignment_id: @assignment.id)&lt;br /&gt;
    signed_up_topics = []&lt;br /&gt;
    &lt;br /&gt;
    @bids.each do |bid|&lt;br /&gt;
      sign_up_topic = SignUpTopic.find_by(id: bid.signuptopic_id)&lt;br /&gt;
      signed_up_topics &amp;lt;&amp;lt; sign_up_topic if sign_up_topic&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    signed_up_topics &amp;amp;= @sign_up_topics&lt;br /&gt;
    @sign_up_topics -= signed_up_topics&lt;br /&gt;
    @bids = signed_up_topics&lt;br /&gt;
    @num_of_topics = @sign_up_topics.size&lt;br /&gt;
    @assigned_review_maps = []&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: @assignment.id, reviewer_id: @participant.id).each do |review_map|&lt;br /&gt;
      @assigned_review_maps &amp;lt;&amp;lt; review_map&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Explicitly render view since it's in the sign up sheet views&lt;br /&gt;
    render 'sign_up_sheet/review_bids_show'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Additional methods&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-2.-reviewbidshelper-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
 # Assigns bidding topics to reviewers&lt;br /&gt;
  def assign_bidding&lt;br /&gt;
      assignment = validate_assignment(params[:assignment_id])&lt;br /&gt;
&lt;br /&gt;
      reviewers = validate_reviewers(assignment.id)&lt;br /&gt;
      reviewer_ids = reviewers.map(&amp;amp;:id)&lt;br /&gt;
&lt;br /&gt;
      matched_topics = run_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
      if matched_topics.blank?&lt;br /&gt;
        flash[:alert] = 'Topic or assignment is missing'&lt;br /&gt;
        redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      leftover_topics = find_leftover_topics(assignment.id, matched_topics)&lt;br /&gt;
      assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.new.assign_review_topics(matched_topics)&lt;br /&gt;
      assignment.update!(can_choose_topic_to_review: false)&lt;br /&gt;
&lt;br /&gt;
      flash[:notice] = 'Reviewers were successfully assigned to topics.'&lt;br /&gt;
      redirect_back fallback_location: root_path&lt;br /&gt;
    rescue ArgumentError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ArgumentError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: e.message&lt;br /&gt;
    rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::RecordInvalid: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue ActiveRecord::ActiveRecordError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::ActiveRecordError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue StandardError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;StandardError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers. Please try again later.'&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  #service methods here ...&lt;br /&gt;
&lt;br /&gt;
  def find_leftover_topics(assignment_id, matched_topics)&lt;br /&gt;
    all_topic_ids = SignUpTopic.where(assignment_id: assignment_id).pluck(:id)&lt;br /&gt;
    assigned_topic_ids = matched_topics.map { |match| match[:topic_id] }&lt;br /&gt;
&lt;br /&gt;
    # Calculate leftover topics&lt;br /&gt;
    all_topic_ids - assigned_topic_ids&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
    return if leftover_topics.blank?&lt;br /&gt;
&lt;br /&gt;
    # Find non-bidders by excluding already matched reviewers&lt;br /&gt;
    non_bidders = reviewer_ids - matched_topics.keys&lt;br /&gt;
&lt;br /&gt;
    # Assign leftover topics to non-bidders in a round-robin fashion&lt;br /&gt;
    non_bidders.each_with_index do |reviewer_id, index|&lt;br /&gt;
      topic_id = leftover_topics[index % leftover_topics.length]&lt;br /&gt;
      ReviewBid.create(priority: 1, signuptopic_id: topic_id, participant_id: reviewer_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirm Functionality for Single-person Teams ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Make sure your code works on individual assignments, i.e., assignments where participants sign up for topics instead of teams. In this case, a team is created for each participant when they sign up. So the code should work for assignments to which either individuals or teams submit.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to make an update to focus on topic rather than team or have it where teams are updated based on topic id.We will probably utilize sign up topic or a similar object to accomplish this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-5-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bid Status Messaging ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| To make the functionality more intuitive, include a message to say how many students are eligible to submit bids, how many have submitted their bids, when the deadline for submitting bids is.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
The logic for these messages will go into the helper file. It is effectively the same reasoning as our migration of other business/functionality code from the controller to the helper to conform to the SRP. It will go hand in hand with the SignUpTopic portion of the code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-6-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Why are the methods in review_bid.rb class methods? Can we change them to instance methods or move it to helpers?&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-1.-reviewbid-model-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model Updates ====&lt;br /&gt;
Since the current class methods are tied to &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model instances, we will change them to instance methods. There is no need to move them to helpers.&lt;br /&gt;
&lt;br /&gt;
===== Review Bid Model Flow Before Refactoring =====&lt;br /&gt;
This sequence diagram describes how the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; interacts with the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model in assigning review topics to reviewers according to their bids. Once the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; action is initiated by the user, the controller gathers the IDs of reviewers and calls &amp;lt;code&amp;gt;ReviewBid.bidding_data&amp;lt;/code&amp;gt; to gather bidding information. That is sent out to an external bidding algorithm, and the matched topics that come back are used by &amp;lt;code&amp;gt;ReviewBid.assign_review_topics&amp;lt;/code&amp;gt; to create &amp;lt;code&amp;gt;ReviewResponseMap&amp;lt;/code&amp;gt; entries, assigning topics to reviewers for the assignment.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
  belongs_to :assignment, class_name: 'Assignment'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  def self.bidding_data(assignment_id, reviewer_ids)&lt;br /&gt;
    # create basic hash and set basic hash data&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'users' =&amp;gt; {}, 'max_accepted_proposals' =&amp;gt; [] }&lt;br /&gt;
    bidding_data['tid'] = SignUpTopic.where(assignment_id: assignment_id).ids&lt;br /&gt;
    bidding_data['max_accepted_proposals'] = Assignment.where(id: assignment_id).pluck(:num_reviews_allowed).first&lt;br /&gt;
&lt;br /&gt;
    # loop through reviewer_ids to get reviewer specific bidding data&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      bidding_data['users'][reviewer_id] = reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # assigns topics to reviews as matched by the webservice algorithm&lt;br /&gt;
  def self.assign_review_topics(assignment_id, reviewer_ids, matched_topics, _min_num_reviews = 2)&lt;br /&gt;
    # if review response map already created, delete it&lt;br /&gt;
    if ReviewResponseMap.where(reviewed_object_id: assignment_id)&lt;br /&gt;
      ReviewResponseMap.where(reviewed_object_id: assignment_id).destroy_all&lt;br /&gt;
    end&lt;br /&gt;
    # loop through reviewer_ids to assign reviews to each reviewer&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      topics_to_assign = matched_topics[reviewer_id.to_s]&lt;br /&gt;
      topics_to_assign.each do |topic|&lt;br /&gt;
        assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method to assign a single topic to a reviewer&lt;br /&gt;
  def self.assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic).pluck(:team_id).first&lt;br /&gt;
    team_to_review.nil? ? [] : ReviewResponseMap.create(reviewed_object_id: assignment_id, reviewer_id: reviewer_id, reviewee_id: team_to_review, type: 'ReviewResponseMap')&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method for getting individual reviewer_ids bidding data&lt;br /&gt;
  # returns user's bidding data hash&lt;br /&gt;
  def self.reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    reviewer_user_id = AssignmentParticipant.find(reviewer_id).user_id&lt;br /&gt;
    self_topic = SignedUpTeam.topic_id(assignment_id, reviewer_user_id)&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'otid' =&amp;gt; self_topic, 'priority' =&amp;gt; [], 'time' =&amp;gt; [] }&lt;br /&gt;
    bids = ReviewBid.where(participant_id: reviewer_id)&lt;br /&gt;
&lt;br /&gt;
    # loop through each bid for a topic to get specific data&lt;br /&gt;
    bids.each do |bid|&lt;br /&gt;
      bidding_data['tid'] &amp;lt;&amp;lt; bid.signuptopic_id&lt;br /&gt;
      bidding_data['priority'] &amp;lt;&amp;lt; bid.priority&lt;br /&gt;
      bidding_data['time'] &amp;lt;&amp;lt; bid.updated_at&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  # Instance method to get bidding data for this review bid&lt;br /&gt;
  def bidding_data&lt;br /&gt;
    {&lt;br /&gt;
      'tid' =&amp;gt; topic_ids,&lt;br /&gt;
      'otid' =&amp;gt; self_topic,&lt;br /&gt;
      'priority' =&amp;gt; priorities,&lt;br /&gt;
      'time' =&amp;gt; updated_times&lt;br /&gt;
    }&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # bid object to limit db touches for similar data acquisitions&lt;br /&gt;
  def bids&lt;br /&gt;
    @bids ||= ReviewBid.where(participant_id: participant_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def topic_ids&lt;br /&gt;
    bids.pluck(:signuptopic_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def priorities&lt;br /&gt;
    bids.pluck(:priority)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def updated_times&lt;br /&gt;
    bids.pluck(:updated_at)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self_topic&lt;br /&gt;
    return nil unless topic &amp;amp;&amp;amp; topic.assignment_id&lt;br /&gt;
&lt;br /&gt;
    SignedUpTeam.topic_id(topic.assignment_id, participant.user_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign topics to reviews&lt;br /&gt;
  def assign_review_topics(matched_topics)&lt;br /&gt;
    validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: topic.assignment_id).destroy_all&lt;br /&gt;
&lt;br /&gt;
    matched_topics.each do |reviewer_id, topics|&lt;br /&gt;
      Array(topics).each { |topic_id| assign_topic_to_reviewer(reviewer_id, topic_id) }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign a single topic to a reviewer&lt;br /&gt;
  def assign_topic_to_reviewer(reviewer_id, topic_id)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic_id).pluck(:team_id).first&lt;br /&gt;
    return if team_to_review.nil?&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.create(&lt;br /&gt;
      reviewed_object_id: topic.assignment_id,&lt;br /&gt;
      reviewer_id: reviewer_id,&lt;br /&gt;
      reviewee_id: team_to_review,&lt;br /&gt;
      type: 'ReviewResponseMap'&lt;br /&gt;
    )&lt;br /&gt;
    # error handling&lt;br /&gt;
  rescue StandardError =&amp;gt; e&lt;br /&gt;
    Rails.logger.error(&amp;quot;Failed to assign topic #{topic_id} to reviewer #{reviewer_id}: #{e.message}&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
    raise ArgumentError, 'Topic or assignment is missing' if topic.nil? || topic.assignment_id.nil?&lt;br /&gt;
    raise ArgumentError, 'Matched topics must be a Hash' unless matched_topics.is_a?(Hash)&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
Once we change them to instance methods, we’ll need to modify their use in the code to instantiate the object first.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;Placeholder for implementation screenshots (before and after)&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-3.-schema.db-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;Schema.db&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We also need to remove the foreign key reference to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model since the &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model already has access to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;, and that is the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; we should be referencing. This will also involve modifying the database to remove the foreign key reference to the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Steps required:&lt;br /&gt;
&lt;br /&gt;
# Review the records in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table in the database to see if there are any records and if those records have values for &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;&lt;br /&gt;
# Create a migration file using the command: &amp;lt;code&amp;gt;rails generate migration RemoveAssignmentFromReviewBids&amp;lt;/code&amp;gt;&lt;br /&gt;
# In the generated file, indicate the change that needs to be made using the change method and migration columns &amp;lt;code&amp;gt;remove_foreign_key&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;remove_column&amp;lt;/code&amp;gt;. Migration should be straightforward since there is no cascading behavior (i.e., &amp;lt;code&amp;gt;dependent:destroy&amp;lt;/code&amp;gt;) to worry about.&lt;br /&gt;
# Once comfortable with the updates to the generated file, run the migration using the &amp;lt;code&amp;gt;rails db:migrate&amp;lt;/code&amp;gt; command.&lt;br /&gt;
# Run a query in the DB to ensure no orphaned records are in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table after the updates.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;Placeholder for implementation screenshots (before and after)&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-4.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implement Edge Case Testing ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| In the previous implementation wiki, there are edge cases which are not exhaustively tested. Should test those edge cases thoroughly and add more edge case testing - link&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span class=&amp;quot;mark&amp;quot;&amp;gt;TODO&amp;lt;/span&amp;gt;:We just got initial test results back. We still need to figure out why tests are failing and what may be slipping through the cracks.[[File:Test expertiza.png|411x459px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-8-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design Patterns and Principles ===&lt;br /&gt;
Below are the design patterns and principles we plan to use to address the problem statements outlined in the project requirements:&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
** Controller Responsibility&lt;br /&gt;
*** To address the issue of authentication logic residing in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, we will refactor the code to ensure the controller solely handles request and response flows.&lt;br /&gt;
*** Authentication logic will be moved to dedicated methods in &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt;, making the codebase easier to maintain and test.&lt;br /&gt;
** View Simplification&lt;br /&gt;
*** Currently, the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; view contains excessive business logic, violating SRP.&lt;br /&gt;
*** We will move this logic into helper methods or models, ensuring each component has a single responsibility and improving code readability.&lt;br /&gt;
&lt;br /&gt;
* Separation of Concerns (SoC)&lt;br /&gt;
** By relocating authorization logic out of the controller, we separate authentication from request handling.&lt;br /&gt;
** Adhering to SoC enhances readability and maintainability by allowing each part of the application to focus on its specific role.&lt;br /&gt;
* Don't Repeat Yourself (DRY)&lt;br /&gt;
** We will inspect the existing implementation for code duplication, particularly in the controller actions.&lt;br /&gt;
** Any repetitive code will be refactored or removed to reduce redundancy and improve clarity.&lt;br /&gt;
&lt;br /&gt;
* Model-View-Controller (MVC) Adherence&lt;br /&gt;
** Moving business logic from the view aligns with MVC best practices.&lt;br /&gt;
** By ensuring the view only presents data without processing it, we maintain clear boundaries between the model, view, and controller layers.&lt;br /&gt;
&lt;br /&gt;
* Dependency Inversion Principle (DIP)&lt;br /&gt;
** We plan to update class methods to instance methods, allowing the controller to depend on abstractions rather than concrete implementations.&lt;br /&gt;
** This change leverages Ruby's duck typing, providing flexibility and making the code easier to maintain and test.&lt;br /&gt;
&lt;br /&gt;
* Factory Pattern&lt;br /&gt;
** We will implement a factory pattern for the common processing of &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpBid&amp;lt;/code&amp;gt;, encapsulating object creation and promoting code reuse.&lt;br /&gt;
&lt;br /&gt;
* Law of Demeter (Principle of Least Knowledge)&lt;br /&gt;
** We'll delegate interactions appropriately to prevent tightly coupled code, especially when dealing with topics and teams.&lt;br /&gt;
** Clear and intentional naming during refactoring will enhance code clarity and maintainability.&lt;br /&gt;
&lt;br /&gt;
* Adapter Pattern&lt;br /&gt;
** To add messaging to bid states for users, we'll use the adapter pattern to allow different components to work together without modifying their existing code.&lt;br /&gt;
&lt;br /&gt;
== Files Modified/Added ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-1.-controllers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
review_bids_controller.rb&lt;br /&gt;
&lt;br /&gt;
signup_sheet_controller.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-2.-models&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
review_bid.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-3.-helpers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
&lt;br /&gt;
authorization_helper.rb&lt;br /&gt;
&lt;br /&gt;
Review_bids_helper.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;viii.-test-plan&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
The Excel spreadsheet below details our test plan for testing the code we changed or created to ensure code coverage. The process involved reviewing the code and determining all the possible scenarios that needed to be covered based on what the method was doing, then creating test cases accordingly.&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/spreadsheets/d/1iHzOdSwlTtxOtf5VsNdwOaCP8NzYMmiJ/edit?usp=drive_link Open Test Matrix Spreadsheet]&lt;br /&gt;
&lt;br /&gt;
== Test Coverage ==&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;screenshot of SimpleCov coverage analysis&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x.-team&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-1.-mentor&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Ed Gehringer&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-2.-members&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Members ===&lt;br /&gt;
&lt;br /&gt;
* Gavin Teague&lt;br /&gt;
* Janice Uwujaren&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xi.-links&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
Previous Wiki: [https://wiki.expertiza.ncsu.edu/index.php/CSC/ECE_517_Fall_2021_-_E2151._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;Link&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xii.-references&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/document/d/1LHVacylQmB15I6VcYUA5nuC7GRRU9szHzZhjlPcE-PA/edit?tab=t.0#heading=h.u0oycoj6e0ev &amp;lt;u&amp;gt;Project Instructions&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;appendix-a-uml-class-diagram-plantuml-code-for-review-bids&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Juwujar</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160946</id>
		<title>CSC/ECE 517 Fall 2024 - E2485. Allow reviewers to bid on what to review</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160946"/>
		<updated>2024-12-10T14:48:44Z</updated>

		<summary type="html">&lt;p&gt;Juwujar: /* review_bids_others_work.html.erb View Updates */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
This project will fix the defects and improve the functionality in Expertiza of the Reviewer Bidding feature, a process where reviewers can bid for assignments they want to review. It will make the functionality smoother, more reliable, and align with Expertiza's collaborative learning objectives.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iii.-problem-statements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Problem Statements ==&lt;br /&gt;
&lt;br /&gt;
Assigning reviews to users on Expertiza involves a complex bidding algorithm that requires optimization before production deployment. Below are key areas we plan to address as per the project requirements:&lt;br /&gt;
* Code Refactoring and Best Practices&lt;br /&gt;
** '''Utilize Authentication Utilities''': Modify the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; function in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to use existing authentication utilities, enhancing code reusability and adhering to the Single Responsibility Principle (SRP) and Separation of Concerns (SoC).&lt;br /&gt;
** '''Eliminate Code Duplication''': Refactor &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; to comply with the Don't Repeat Yourself (DRY) principle.&lt;br /&gt;
** '''Rename Functions for Clarity''': Change &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; to better reflect its purpose.&lt;br /&gt;
** '''Refactor Methods in '''&amp;lt;code&amp;gt;review_bid.rb&amp;lt;/code&amp;gt;''':''': Convert class methods to instance methods or relocate them to helper classes for improved code organization.&lt;br /&gt;
* Functionality Enhancements&lt;br /&gt;
** '''Handle Missing Bids Gracefully''': Adjust the bidding algorithm to accommodate students who do not bid, allowing them to select from the remaining options.&lt;br /&gt;
** '''Support Both Individual and Team Assignments''': Ensure the code functions correctly for participant and team-based assignments when signing up for topics.&lt;br /&gt;
* User Interface Improvements&lt;br /&gt;
** '''Increase Transparency in the Bidding Process''': Display messages indicating how many students are eligible to bid, how many have submitted bids, and the deadline.&lt;br /&gt;
** '''Accurate Bid Status Indicators''': Ensure topic colors change correctly based on the number of outstanding bids, providing clear visual feedback to users.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Exhaustive Edge Case Testing''': Conduct thorough testing for edge cases not previously covered, adding additional test cases as needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iv.-design-goals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Design Goals ==&lt;br /&gt;
&lt;br /&gt;
While addressing the existing defects and enhancing the Reviewer Bidding feature, we plan to adhere to the following design guidelines:&lt;br /&gt;
&lt;br /&gt;
* Code Quality and Maintainability&lt;br /&gt;
** '''Improve Code Structure: '''Implement refactoring strategies to enhance code readability and maintainability, ensuring adherence to best practices like the DRY principle.&lt;br /&gt;
** '''Standardize Authentication Logic: '''Utilize a centralized authentication utility to promote consistency and reusability across the application.&lt;br /&gt;
* System Stability and Flexibility&lt;br /&gt;
** '''Enhance Algorithm Robustness: '''Modify the bidding algorithm to handle scenarios where students do not bid, ensuring the system remains stable under all conditions.&lt;br /&gt;
** '''Ensure Assignment Versatility: '''Design the system to seamlessly support both individual and team assignments without additional configuration.&lt;br /&gt;
* User Experience Enhancements&lt;br /&gt;
** '''Increase Bidding Transparency: '''Develop user interface elements that clearly display bidding status, deadlines, and participation metrics.&lt;br /&gt;
** '''Implement Dynamic Visual Feedback: '''Use real-time visual cues, such as color changes, to inform users about the bidding status instantly.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Develop Comprehensive Test Suites: '''Create extensive automated tests covering edge cases and critical functionalities to guarantee reliable system behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;v.-uml-diagram&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== UML Diagram ==&lt;br /&gt;
&lt;br /&gt;
Below is a visual representation of the models associated with the review bids functionality, depicting the data structures and their relationships.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml class diagram2.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi.-implementation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
'''ReviewBid: '''The &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; manages the bidding process, where participants indicate their preferences for reviewing different topics or assignments. This helps distribute review tasks fairly based on participants' interests.&lt;br /&gt;
&lt;br /&gt;
'''Assignment: '''The &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model represents team or individual assignments in Expertiza. It stores all the essential information required to keep the assignment process organized, like managing participants, setting due dates, forming teams, and overseeing the review process.&lt;br /&gt;
&lt;br /&gt;
'''SignUpTopic: '''The &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt; model identifies specific topics or areas within an assignment for which participants can sign up. It manages the availability of these topics, tracks bids, coordinates team sign-ups, and assigns topics based on both bids and participant availability. &lt;br /&gt;
&lt;br /&gt;
'''Participant: '''The &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model is about the individual users who participate in an assignment. This includes students, instructors, teaching assistants, or other relevant roles. This model is responsible for managing key user information, monitoring interactions with the assignment, keeping track of team memberships, and recording things like badges and review grades.&lt;br /&gt;
&lt;br /&gt;
== Implementation ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1.-decoupling-authorization-logic-from-reviewbidscontroller&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Decoupling Authorization Logic from &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| '''&amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; needs to use the authentication utilities'''&lt;br /&gt;
&lt;br /&gt;
The function &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; should use the available authentication utilities to support the Single Responsibility Principle and Separation of Concerns and to support code reusability.&lt;br /&gt;
&lt;br /&gt;
| '''Authentication Consistency:''' Remove authentication logic in the controller and call logic in the authentication utility class instead to ensure consistent use of the same logic across the application, foster reusability, and eliminate DRY.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
==== Authorization Flow Before Refactoring ====&lt;br /&gt;
The sequence diagram depicts the authorization process within the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;. When a user invokes an action, the controller checks if the action is allowed by verifying the user's role through the &amp;lt;code&amp;gt;current_role_name()&amp;lt;/code&amp;gt; method in the &amp;lt;code&amp;gt;ApplicationController&amp;lt;/code&amp;gt;. Depending on the action and the user's role, the controller either authorizes the user to proceed or denies access, ultimately executing the action logic if authorized and responding to the user.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 1.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We simplified the &amp;lt;code&amp;gt;action_allowed?&amp;lt;/code&amp;gt; method by decoupling authorization logic from the controller to use helper methods in the &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt; module. The &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; should not handle authentication, which is a violation of SRP. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;vertical-align:top;&amp;quot; | Original Code !! style=&amp;quot;vertical-align:top;&amp;quot; | Refactored Code&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index'&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator',&lt;br /&gt;
     'Student'].include? current_role_name and&lt;br /&gt;
        ((%w[list].include? action_name) ? are_needed_authorizations_present?(params[:id], &amp;quot;participant&amp;quot;, &amp;quot;reader&amp;quot;, &amp;quot;submitter&amp;quot;, &amp;quot;reviewer&amp;quot;) : true)&lt;br /&gt;
  else&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator'].include? current_role_name&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index', 'list'&lt;br /&gt;
    current_user_has_student_privileges? &amp;amp;&amp;amp; list_authorization_check&lt;br /&gt;
  else&lt;br /&gt;
    current_user_has_ta_privileges?&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; action ===&lt;br /&gt;
The refactor of the &amp;lt;code&amp;gt;ReviewBidsController's index&amp;lt;/code&amp;gt; action simplifies and streamlines the codebase, ensuring the action simply handles controller activities. Previously, the controller was overloaded with responsibilities—it handled everything from locating and validating assignments to fetching review mappings and counting completed reviews. This all-in-one approach made the controller action messy. &lt;br /&gt;
&lt;br /&gt;
Now we have moved the business logic out of the controller action and into dedicated service objects. With this change, we introduced two key service objects: &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; focuses on retrieving the participant along with their assignments, while the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; takes on the task of managing review mappings and calculating review-related stats, like how many reviews have been completed. &lt;br /&gt;
&lt;br /&gt;
This clear separation of responsibilities aligns with the Single Responsibility Principle, which helps each part of our code be more focused and easier to understand. With these new service objects, the code has become more reusable. The logic for fetching assignments, retrieving review mappings, and counting reviews is now more flexible and reusable throughout the application. For example, we used methods in the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; in both the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; and the &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;, helping to avoid code duplication.&lt;br /&gt;
&lt;br /&gt;
After moving the business logic to service objects, we can write unit tests to ensure each part of the code behaves as expected and prevents bugs. We also moved complex calculations, like those for completed reviews, into the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;, freeing the controller from dealing with the implementation details and promoting a cleaner design. The article https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial provides more information on when and how to use service objects in Rails.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
  return unless current_user_id?(@participant.user_id)&lt;br /&gt;
  @assignment = @participant.assignment&lt;br /&gt;
  @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
  # Finding how many reviews have been completed&lt;br /&gt;
  @num_reviews_completed = 0&lt;br /&gt;
  @review_mappings.each do |map|&lt;br /&gt;
    @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # render view for completing reviews after review bidding has been completed&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @assignment = participant_service.assignment&lt;br /&gt;
  unless @assignment.is_a?(Assignment)&lt;br /&gt;
    flash[:error] = 'Assignment not found.'&lt;br /&gt;
    redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  @review_mappings = review_service.review_mappings&lt;br /&gt;
  @num_reviews_completed = review_service.review_counts[:completed]&lt;br /&gt;
&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Optimize &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Action functionality ===&lt;br /&gt;
In the earlier version of the set_priority action in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, the server only needed a couple of key pieces of information: an array of topic IDs (params[:topic]) and the participant ID (params[:id}). From there, it would figure out the assignment_id by using the first topic as a reference. While this worked in simple cases, it could lead to problems if any data was missing or didn’t line up correctly.&lt;br /&gt;
&lt;br /&gt;
With the new implementation, the action now expects clear and structured parameters, including participant_id, an array of topic IDs, and assignment_id. This change ensures that the server always receives complete and reliable information, preventing issues caused by missing or inconsistent data. It also removes the need for the server to perform unnecessary lookups, allowing the controller to focus on its main job: validating inputs (like making sure the topics in params[:topic] belong to the same assignment as the participant), updating the database, handling errors properly, and sending the correct response back to the view.&lt;br /&gt;
&lt;br /&gt;
While these updates have cleaned up the set_priority action, they still contain a lot of business logic, like validations and priority updates. To make the code even more maintainable, there’s room for further refactoring—perhaps moving some of this logic into a service class or model method to streamline the controller even more.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | Old Code&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    if params[:topic].nil?&lt;br /&gt;
      ReviewBid.where(participant_id: params[:id]).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      participant = AssignmentParticipant.find_by(id: params[:id])&lt;br /&gt;
      assignment_id = SignUpTopic.find(params[:topic].first).assignment.id&lt;br /&gt;
      @bids = ReviewBid.where(participant_id: params[:id])&lt;br /&gt;
      signed_up_topics = ReviewBid.where(participant_id: params[:id]).map(&amp;amp;:signuptopic_id)&lt;br /&gt;
      signed_up_topics -= params[:topic].map(&amp;amp;:to_i)&lt;br /&gt;
      signed_up_topics.each do |topic|&lt;br /&gt;
        ReviewBid.where(signuptopic_id: topic, participant_id: params[:id]).destroy_all&lt;br /&gt;
      end&lt;br /&gt;
      params[:topic].each_with_index do |topic_id, index|&lt;br /&gt;
        bid_existence = ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id])&lt;br /&gt;
        if bid_existence.empty?&lt;br /&gt;
          ReviewBid.create(priority: index + 1, signuptopic_id: topic_id, participant_id: params[:id], assignment_id: assignment_id)&lt;br /&gt;
        else&lt;br /&gt;
          ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id]).update_all(priority: index + 1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to action: 'show', assignment_id: params[:assignment_id], id: params[:id]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    participant_id = params[:participant_id].to_i&lt;br /&gt;
    selected_topic_ids = params[:topic]&amp;amp;.map(&amp;amp;:to_i) || []&lt;br /&gt;
    assignment_id = params[:assignment_id].to_i&lt;br /&gt;
&lt;br /&gt;
    unless current_user_can_modify_participant?(participant_id)&lt;br /&gt;
      flash[:error] = 'You are not authorized to modify these bids.'&lt;br /&gt;
      respond_to do |format|&lt;br /&gt;
        format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
        format.json { render json: { status: 'unauthorized' }, status: :unauthorized }&lt;br /&gt;
      end&lt;br /&gt;
      return&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    if selected_topic_ids.empty?&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      assignment = Assignment.find_by(id: assignment_id)&lt;br /&gt;
      if assignment.nil?&lt;br /&gt;
        flash[:error] = &amp;quot;Invalid assignment.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_assignment' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      unless SignUpTopic.where(id: selected_topic_ids, assignment_id: assignment_id).count == selected_topic_ids.size&lt;br /&gt;
        flash[:error] = &amp;quot;One or more selected topics are invalid.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_topics' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).where.not(signuptopic_id: selected_topic_ids).destroy_all&lt;br /&gt;
&lt;br /&gt;
      selected_topic_ids.each_with_index do |topic_id, index|&lt;br /&gt;
        bid = ReviewBid.find_or_initialize_by(signuptopic_id: topic_id, participant_id: participant_id)&lt;br /&gt;
        bid.priority = index + 1&lt;br /&gt;
        bid.assignment_id = assignment_id&lt;br /&gt;
        bid.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_to action: 'show', assignment_id: assignment_id, id: participant_id, notice: 'Review bids updated successfully.' }&lt;br /&gt;
      format.json { render json: { status: 'success' }, status: :ok }&lt;br /&gt;
    end&lt;br /&gt;
  rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
    flash[:error] = &amp;quot;Failed to update priorities: #{e.message}&amp;quot;&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
      format.json { render json: { status: 'error', message: e.message }, status: :unprocessable_entity }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Client Side Changes to Support &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Changes ====&lt;br /&gt;
After tidying up the server-side logic, we needed to refactor the client-side code to ensure it passed the correct information from the view to the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action. On the client side, we now grab the topicIDs and send them as &amp;lt;code&amp;gt;selected_topic_ids&amp;lt;/code&amp;gt;, allowing the controller to figure out which topics were selected and in what order. It’s a cleaner and more reliable way to keep everything working together! &lt;br /&gt;
&lt;br /&gt;
We are also extracting the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, making it a lot easier to send data with AJAX and keeping things flexible. AJAX is awesome because it lets us update the server without reloading the page, making the user experience smooth and responsive. When we pull the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, we can grab it along with other data, like the list of topics, and include it in the request. This way, we don’t have to rely on the URL to know which participant we’re working with, which makes the code cleaner and more adaptable.&lt;br /&gt;
&lt;br /&gt;
If we were to rely on the route for the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt;, every AJAX request would need to be tied to a specific URL structure. That’s limiting, especially if the route doesn’t naturally include the participant ID, as can be the case with collection routes versus member routes in Rails. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery -&amp;gt;&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: -&amp;gt;&lt;br /&gt;
      $.post($(this).data('update-url'), $(this).sortable('serialize'))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery(document).ready ($) -&amp;gt;&lt;br /&gt;
  sendUpdatedOrder = -&amp;gt;&lt;br /&gt;
    sortedIDs = $(this).sortable('toArray')&lt;br /&gt;
&lt;br /&gt;
    topicIDs = sortedIDs.map (id) -&amp;gt;&lt;br /&gt;
      id.replace 'topic_', ''&lt;br /&gt;
&lt;br /&gt;
    participant_id = $(this).data('participant-id')&lt;br /&gt;
&lt;br /&gt;
    assignment_id = new URL($(this).data('update-url'), window.location.origin).searchParams.get('assignment_id')&lt;br /&gt;
&lt;br /&gt;
    $.ajax&lt;br /&gt;
      type: 'POST'&lt;br /&gt;
      url: $(this).data('update-url')&lt;br /&gt;
      data:&lt;br /&gt;
        topic: topicIDs&lt;br /&gt;
        participant_id: participant_id&lt;br /&gt;
        assignment_id: assignment_id&lt;br /&gt;
      success: (response) -&amp;gt;&lt;br /&gt;
        console.log 'Selections updated successfully.'&lt;br /&gt;
      error: (xhr, status, error) -&amp;gt;&lt;br /&gt;
        alert 'An error occurred while saving your selections. Please try again.'&lt;br /&gt;
        console.error error&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: sendUpdatedOrder&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-1.-signupsheetcontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; Updates ===&lt;br /&gt;
&lt;br /&gt;
The ReviewBidsController's index method renders the review_bids_others_work action in &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt;. However, the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; does not include the controller action for this view. We need to find the old implementation of the controller action, add it back, and review and update it for any DRY violations.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # provides variables for reviewing page located at views/review_bids/others_work.html.erb&lt;br /&gt;
  def index&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
    return unless current_user_id?(@participant.user_id)&lt;br /&gt;
&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
    # Finding how many reviews have been completed&lt;br /&gt;
    @num_reviews_completed = 0&lt;br /&gt;
    @review_mappings.each do |map|&lt;br /&gt;
      @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # render view for completing reviews after review bidding has been completed&lt;br /&gt;
    render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
  end  &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-2.-reviewbidsotherswork-view-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;review_bids_others_work.html.erb&amp;lt;/code&amp;gt; View Updates ====&lt;br /&gt;
&lt;br /&gt;
The code only contains a view with this name and no associated controller action. Reviewing the code in the view, we noticed some SRP violations where there is much logic in the presentation layer. Set up helper methods in the controller to handle some of that logic or see if the model can handle some of that logic.&lt;br /&gt;
&lt;br /&gt;
Note: The view is technically part of the SignUpSheetController, but it's being rendered by the ReviewBidsController, which initially threw us off. After chatting with the professor, we decided not to change this view. He indicated that the plan is to reuse these views for both assignment bidding and review bidding.&lt;br /&gt;
&lt;br /&gt;
We would suggest building the assignment bidding and review bidding features separately first to make sure each one works on its own. Once that’s done, we can go back and clean things up by combining any shared functionality. This could include creating shared views or even introducing an abstract model to handle both types of bidding in a cleaner way. It could also mean replacing the ReviewBidsController with something more generic, like a BidsController, to keep things reusable and easier to maintain down the line.&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Rename &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; method ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; should be &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt;&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:''' Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The primary activity here is to refactor the method name &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; and fix the hardcoded URL. However, the match topic bidding algorithm no longer works since Heroku is no longer free, per the professor. The reference link below has detailed information about the web service that we can leverage to gain that understanding.&lt;br /&gt;
&lt;br /&gt;
Reference Link for Webservice Details: [https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to rename the &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; method to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; to make the functionality of the method clear. We also plan to verify that the method is working appropriately by mocking up the service call since it is no longer working.&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # Other methods&lt;br /&gt;
&lt;br /&gt;
  # Call webserver for running the assigning algorithm&lt;br /&gt;
  # Passing to webserver:&lt;br /&gt;
  # - student_ids&lt;br /&gt;
  # - topic_ids&lt;br /&gt;
  # - student_preferences&lt;br /&gt;
  # - time_stamps&lt;br /&gt;
  # Webserver returns:&lt;br /&gt;
  # - matched assignments as JSON body&lt;br /&gt;
  def run_bidding_algorithm(bidding_data)&lt;br /&gt;
    url = 'http://app-csc517.herokuapp.com/match_topics' # Hard coding for the time being&lt;br /&gt;
    response = RestClient.post url, bidding_data.to_json, content_type: 'application/json', accept: :json&lt;br /&gt;
    JSON.parse(response.body)&lt;br /&gt;
  rescue StandardError =&amp;gt; e&lt;br /&gt;
    Rails.logger.error &amp;quot;Bidding algorithm failed: #{e.message}&amp;quot;&lt;br /&gt;
    false&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Updated Implementation ====&lt;br /&gt;
We implemented an update using mocked data and a service as placeholders until a new URL is in place. We did not end up refactoring to rename this method as we found that the name describes the functionality quite well.&lt;br /&gt;
&lt;br /&gt;
==== review_bids_controller ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def run_bidding_algorithm&lt;br /&gt;
    review_bid = ReviewBid.new&lt;br /&gt;
    bidding_data = review_bid.bidding_data&lt;br /&gt;
    matched_topics = BiddingAlgorithmService.new(bidding_data).run&lt;br /&gt;
    raise ArgumentError, 'Failed to assign reviewers. Please try again later.' unless matched_topics&lt;br /&gt;
&lt;br /&gt;
    matched_topics&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== bidding_algorithm_service ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# frozen_string_literal: true&lt;br /&gt;
&lt;br /&gt;
# The `BiddingAlgorithmService` run the bid assignment algorithm&lt;br /&gt;
# Sends student IDs, topic IDs, student preferences, and timestamps to the web service&lt;br /&gt;
# The web service returns the matched assignments in the JSON response body&lt;br /&gt;
class BiddingAlgorithmService&lt;br /&gt;
  SERVICE_URL = 'http://app-csc517.herokuapp.com/match_topics'.freeze&lt;br /&gt;
&lt;br /&gt;
  # MOCK_DATA is based on the documentation detailing how the webservice behaves.&lt;br /&gt;
  # Reference: https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review#Webservice&lt;br /&gt;
  MOCK_DATA = {&lt;br /&gt;
    36239 =&amp;gt; [3970, 3972, 3975],&lt;br /&gt;
    36240 =&amp;gt; [3973, 3974, 3972],&lt;br /&gt;
    36241 =&amp;gt; [3969, 3971, 3972],&lt;br /&gt;
    36242 =&amp;gt; [3969, 3971, 3973],&lt;br /&gt;
    36243 =&amp;gt; [3969, 3970, 3971]&lt;br /&gt;
  }.freeze&lt;br /&gt;
&lt;br /&gt;
  def initialize(bidding_data)&lt;br /&gt;
    @bidding_data = bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def run&lt;br /&gt;
    return MOCK_DATA if Rails.application.config.use_mock_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
    perform_request&lt;br /&gt;
  rescue RestClient::ExceptionWithResponse, JSON::ParserError&lt;br /&gt;
    false&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def perform_request&lt;br /&gt;
    response = RestClient.post(&lt;br /&gt;
      SERVICE_URL,&lt;br /&gt;
      @bidding_data.to_json,&lt;br /&gt;
      content_type: :json,&lt;br /&gt;
      accept: :json&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    validate_response(response)&lt;br /&gt;
    JSON.parse(response.body)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def validate_response(response)&lt;br /&gt;
    raise 'Invalid response format' unless response.headers[:content_type] &amp;amp;&amp;amp; response.headers[:content_type].include?('application/json')&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Resolve Functionality Issues for Non Bidders ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Currently it doesn't work if some student does not bid. In this case, algorithm needs to be fixed to ignore anyone who didn’t bid, and let them choose from what’s left over.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to do several things within this controller. We will set a default state for non-bidders that we can point to to give them options to select topics to review from the leftovers. We will consolidate &amp;lt;code&amp;gt;@sign_up_topics&amp;lt;/code&amp;gt; to resolve the DRY violation. Lastly, we will remove business logic from the show method and push it to a new helper to conform to the SRP.&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # Additional method&lt;br /&gt;
&lt;br /&gt;
  # Provides variables for review bidding page&lt;br /&gt;
  def show&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id].to_i)&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @sign_up_topics = SignUpTopic.where(assignment_id: @assignment.id, private_to: nil)&lt;br /&gt;
    my_topic = SignedUpTeam.topic_id(@participant.parent_id, @participant.user_id)&lt;br /&gt;
    @sign_up_topics -= SignUpTopic.where(assignment_id: @assignment.id, id: my_topic)&lt;br /&gt;
    @num_participants = AssignmentParticipant.where(parent_id: @assignment.id).count&lt;br /&gt;
    @selected_topics = nil # This is used to list the topics assigned to review (i.e., select == assigned)&lt;br /&gt;
    @bids = ReviewBid.where(participant_id: @participant, assignment_id: @assignment.id)&lt;br /&gt;
    signed_up_topics = []&lt;br /&gt;
    &lt;br /&gt;
    @bids.each do |bid|&lt;br /&gt;
      sign_up_topic = SignUpTopic.find_by(id: bid.signuptopic_id)&lt;br /&gt;
      signed_up_topics &amp;lt;&amp;lt; sign_up_topic if sign_up_topic&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    signed_up_topics &amp;amp;= @sign_up_topics&lt;br /&gt;
    @sign_up_topics -= signed_up_topics&lt;br /&gt;
    @bids = signed_up_topics&lt;br /&gt;
    @num_of_topics = @sign_up_topics.size&lt;br /&gt;
    @assigned_review_maps = []&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: @assignment.id, reviewer_id: @participant.id).each do |review_map|&lt;br /&gt;
      @assigned_review_maps &amp;lt;&amp;lt; review_map&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Explicitly render view since it's in the sign up sheet views&lt;br /&gt;
    render 'sign_up_sheet/review_bids_show'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Additional methods&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-2.-reviewbidshelper-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
 # Assigns bidding topics to reviewers&lt;br /&gt;
  def assign_bidding&lt;br /&gt;
      assignment = validate_assignment(params[:assignment_id])&lt;br /&gt;
&lt;br /&gt;
      reviewers = validate_reviewers(assignment.id)&lt;br /&gt;
      reviewer_ids = reviewers.map(&amp;amp;:id)&lt;br /&gt;
&lt;br /&gt;
      matched_topics = run_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
      if matched_topics.blank?&lt;br /&gt;
        flash[:alert] = 'Topic or assignment is missing'&lt;br /&gt;
        redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      leftover_topics = find_leftover_topics(assignment.id, matched_topics)&lt;br /&gt;
      assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.new.assign_review_topics(matched_topics)&lt;br /&gt;
      assignment.update!(can_choose_topic_to_review: false)&lt;br /&gt;
&lt;br /&gt;
      flash[:notice] = 'Reviewers were successfully assigned to topics.'&lt;br /&gt;
      redirect_back fallback_location: root_path&lt;br /&gt;
    rescue ArgumentError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ArgumentError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: e.message&lt;br /&gt;
    rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::RecordInvalid: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue ActiveRecord::ActiveRecordError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::ActiveRecordError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue StandardError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;StandardError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers. Please try again later.'&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  #service methods here ...&lt;br /&gt;
&lt;br /&gt;
  def find_leftover_topics(assignment_id, matched_topics)&lt;br /&gt;
    all_topic_ids = SignUpTopic.where(assignment_id: assignment_id).pluck(:id)&lt;br /&gt;
    assigned_topic_ids = matched_topics.map { |match| match[:topic_id] }&lt;br /&gt;
&lt;br /&gt;
    # Calculate leftover topics&lt;br /&gt;
    all_topic_ids - assigned_topic_ids&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
    return if leftover_topics.blank?&lt;br /&gt;
&lt;br /&gt;
    # Find non-bidders by excluding already matched reviewers&lt;br /&gt;
    non_bidders = reviewer_ids - matched_topics.keys&lt;br /&gt;
&lt;br /&gt;
    # Assign leftover topics to non-bidders in a round-robin fashion&lt;br /&gt;
    non_bidders.each_with_index do |reviewer_id, index|&lt;br /&gt;
      topic_id = leftover_topics[index % leftover_topics.length]&lt;br /&gt;
      ReviewBid.create(priority: 1, signuptopic_id: topic_id, participant_id: reviewer_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirm Functionality for Single-person Teams ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Make sure your code works on individual assignments, i.e., assignments where participants sign up for topics instead of teams. In this case, a team is created for each participant when they sign up. So the code should work for assignments to which either individuals or teams submit.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to make an update to focus on topic rather than team or have it where teams are updated based on topic id.We will probably utilize sign up topic or a similar object to accomplish this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-5-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bid Status Messaging ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| To make the functionality more intuitive, include a message to say how many students are eligible to submit bids, how many have submitted their bids, when the deadline for submitting bids is.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
The logic for these messages will go into the helper file. It is effectively the same reasoning as our migration of other business/functionality code from the controller to the helper to conform to the SRP. It will go hand in hand with the SignUpTopic portion of the code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-6-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Why are the methods in review_bid.rb class methods? Can we change them to instance methods or move it to helpers?&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-1.-reviewbid-model-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model Updates ====&lt;br /&gt;
Since the current class methods are tied to &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model instances, we will change them to instance methods. There is no need to move them to helpers.&lt;br /&gt;
&lt;br /&gt;
===== Review Bid Model Flow Before Refactoring =====&lt;br /&gt;
This sequence diagram describes how the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; interacts with the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model in assigning review topics to reviewers according to their bids. Once the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; action is initiated by the user, the controller gathers the IDs of reviewers and calls &amp;lt;code&amp;gt;ReviewBid.bidding_data&amp;lt;/code&amp;gt; to gather bidding information. That is sent out to an external bidding algorithm, and the matched topics that come back are used by &amp;lt;code&amp;gt;ReviewBid.assign_review_topics&amp;lt;/code&amp;gt; to create &amp;lt;code&amp;gt;ReviewResponseMap&amp;lt;/code&amp;gt; entries, assigning topics to reviewers for the assignment.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
  belongs_to :assignment, class_name: 'Assignment'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  def self.bidding_data(assignment_id, reviewer_ids)&lt;br /&gt;
    # create basic hash and set basic hash data&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'users' =&amp;gt; {}, 'max_accepted_proposals' =&amp;gt; [] }&lt;br /&gt;
    bidding_data['tid'] = SignUpTopic.where(assignment_id: assignment_id).ids&lt;br /&gt;
    bidding_data['max_accepted_proposals'] = Assignment.where(id: assignment_id).pluck(:num_reviews_allowed).first&lt;br /&gt;
&lt;br /&gt;
    # loop through reviewer_ids to get reviewer specific bidding data&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      bidding_data['users'][reviewer_id] = reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # assigns topics to reviews as matched by the webservice algorithm&lt;br /&gt;
  def self.assign_review_topics(assignment_id, reviewer_ids, matched_topics, _min_num_reviews = 2)&lt;br /&gt;
    # if review response map already created, delete it&lt;br /&gt;
    if ReviewResponseMap.where(reviewed_object_id: assignment_id)&lt;br /&gt;
      ReviewResponseMap.where(reviewed_object_id: assignment_id).destroy_all&lt;br /&gt;
    end&lt;br /&gt;
    # loop through reviewer_ids to assign reviews to each reviewer&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      topics_to_assign = matched_topics[reviewer_id.to_s]&lt;br /&gt;
      topics_to_assign.each do |topic|&lt;br /&gt;
        assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method to assign a single topic to a reviewer&lt;br /&gt;
  def self.assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic).pluck(:team_id).first&lt;br /&gt;
    team_to_review.nil? ? [] : ReviewResponseMap.create(reviewed_object_id: assignment_id, reviewer_id: reviewer_id, reviewee_id: team_to_review, type: 'ReviewResponseMap')&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method for getting individual reviewer_ids bidding data&lt;br /&gt;
  # returns user's bidding data hash&lt;br /&gt;
  def self.reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    reviewer_user_id = AssignmentParticipant.find(reviewer_id).user_id&lt;br /&gt;
    self_topic = SignedUpTeam.topic_id(assignment_id, reviewer_user_id)&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'otid' =&amp;gt; self_topic, 'priority' =&amp;gt; [], 'time' =&amp;gt; [] }&lt;br /&gt;
    bids = ReviewBid.where(participant_id: reviewer_id)&lt;br /&gt;
&lt;br /&gt;
    # loop through each bid for a topic to get specific data&lt;br /&gt;
    bids.each do |bid|&lt;br /&gt;
      bidding_data['tid'] &amp;lt;&amp;lt; bid.signuptopic_id&lt;br /&gt;
      bidding_data['priority'] &amp;lt;&amp;lt; bid.priority&lt;br /&gt;
      bidding_data['time'] &amp;lt;&amp;lt; bid.updated_at&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  # Instance method to get bidding data for this review bid&lt;br /&gt;
  def bidding_data&lt;br /&gt;
    {&lt;br /&gt;
      'tid' =&amp;gt; topic_ids,&lt;br /&gt;
      'otid' =&amp;gt; self_topic,&lt;br /&gt;
      'priority' =&amp;gt; priorities,&lt;br /&gt;
      'time' =&amp;gt; updated_times&lt;br /&gt;
    }&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # bid object to limit db touches for similar data acquisitions&lt;br /&gt;
  def bids&lt;br /&gt;
    @bids ||= ReviewBid.where(participant_id: participant_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def topic_ids&lt;br /&gt;
    bids.pluck(:signuptopic_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def priorities&lt;br /&gt;
    bids.pluck(:priority)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def updated_times&lt;br /&gt;
    bids.pluck(:updated_at)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self_topic&lt;br /&gt;
    return nil unless topic &amp;amp;&amp;amp; topic.assignment_id&lt;br /&gt;
&lt;br /&gt;
    SignedUpTeam.topic_id(topic.assignment_id, participant.user_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign topics to reviews&lt;br /&gt;
  def assign_review_topics(matched_topics)&lt;br /&gt;
    validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: topic.assignment_id).destroy_all&lt;br /&gt;
&lt;br /&gt;
    matched_topics.each do |reviewer_id, topics|&lt;br /&gt;
      Array(topics).each { |topic_id| assign_topic_to_reviewer(reviewer_id, topic_id) }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign a single topic to a reviewer&lt;br /&gt;
  def assign_topic_to_reviewer(reviewer_id, topic_id)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic_id).pluck(:team_id).first&lt;br /&gt;
    return if team_to_review.nil?&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.create(&lt;br /&gt;
      reviewed_object_id: topic.assignment_id,&lt;br /&gt;
      reviewer_id: reviewer_id,&lt;br /&gt;
      reviewee_id: team_to_review,&lt;br /&gt;
      type: 'ReviewResponseMap'&lt;br /&gt;
    )&lt;br /&gt;
    # error handling&lt;br /&gt;
  rescue StandardError =&amp;gt; e&lt;br /&gt;
    Rails.logger.error(&amp;quot;Failed to assign topic #{topic_id} to reviewer #{reviewer_id}: #{e.message}&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
    raise ArgumentError, 'Topic or assignment is missing' if topic.nil? || topic.assignment_id.nil?&lt;br /&gt;
    raise ArgumentError, 'Matched topics must be a Hash' unless matched_topics.is_a?(Hash)&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
Once we change them to instance methods, we’ll need to modify their use in the code to instantiate the object first.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;Placeholder for implementation screenshots (before and after)&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-3.-schema.db-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;Schema.db&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We also need to remove the foreign key reference to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model since the &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model already has access to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;, and that is the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; we should be referencing. This will also involve modifying the database to remove the foreign key reference to the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Steps required:&lt;br /&gt;
&lt;br /&gt;
# Review the records in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table in the database to see if there are any records and if those records have values for &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;&lt;br /&gt;
# Create a migration file using the command: &amp;lt;code&amp;gt;rails generate migration RemoveAssignmentFromReviewBids&amp;lt;/code&amp;gt;&lt;br /&gt;
# In the generated file, indicate the change that needs to be made using the change method and migration columns &amp;lt;code&amp;gt;remove_foreign_key&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;remove_column&amp;lt;/code&amp;gt;. Migration should be straightforward since there is no cascading behavior (i.e., &amp;lt;code&amp;gt;dependent:destroy&amp;lt;/code&amp;gt;) to worry about.&lt;br /&gt;
# Once comfortable with the updates to the generated file, run the migration using the &amp;lt;code&amp;gt;rails db:migrate&amp;lt;/code&amp;gt; command.&lt;br /&gt;
# Run a query in the DB to ensure no orphaned records are in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table after the updates.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;Placeholder for implementation screenshots (before and after)&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-4.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implement Edge Case Testing ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| In the previous implementation wiki, there are edge cases which are not exhaustively tested. Should test those edge cases thoroughly and add more edge case testing - link&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span class=&amp;quot;mark&amp;quot;&amp;gt;TODO&amp;lt;/span&amp;gt;:We just got initial test results back. We still need to figure out why tests are failing and what may be slipping through the cracks.[[File:Test expertiza.png|411x459px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-8-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design Patterns and Principles ===&lt;br /&gt;
Below are the design patterns and principles we plan to use to address the problem statements outlined in the project requirements:&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
** Controller Responsibility&lt;br /&gt;
*** To address the issue of authentication logic residing in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, we will refactor the code to ensure the controller solely handles request and response flows.&lt;br /&gt;
*** Authentication logic will be moved to dedicated methods in &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt;, making the codebase easier to maintain and test.&lt;br /&gt;
** View Simplification&lt;br /&gt;
*** Currently, the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; view contains excessive business logic, violating SRP.&lt;br /&gt;
*** We will move this logic into helper methods or models, ensuring each component has a single responsibility and improving code readability.&lt;br /&gt;
&lt;br /&gt;
* Separation of Concerns (SoC)&lt;br /&gt;
** By relocating authorization logic out of the controller, we separate authentication from request handling.&lt;br /&gt;
** Adhering to SoC enhances readability and maintainability by allowing each part of the application to focus on its specific role.&lt;br /&gt;
* Don't Repeat Yourself (DRY)&lt;br /&gt;
** We will inspect the existing implementation for code duplication, particularly in the controller actions.&lt;br /&gt;
** Any repetitive code will be refactored or removed to reduce redundancy and improve clarity.&lt;br /&gt;
&lt;br /&gt;
* Model-View-Controller (MVC) Adherence&lt;br /&gt;
** Moving business logic from the view aligns with MVC best practices.&lt;br /&gt;
** By ensuring the view only presents data without processing it, we maintain clear boundaries between the model, view, and controller layers.&lt;br /&gt;
&lt;br /&gt;
* Dependency Inversion Principle (DIP)&lt;br /&gt;
** We plan to update class methods to instance methods, allowing the controller to depend on abstractions rather than concrete implementations.&lt;br /&gt;
** This change leverages Ruby's duck typing, providing flexibility and making the code easier to maintain and test.&lt;br /&gt;
&lt;br /&gt;
* Factory Pattern&lt;br /&gt;
** We will implement a factory pattern for the common processing of &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpBid&amp;lt;/code&amp;gt;, encapsulating object creation and promoting code reuse.&lt;br /&gt;
&lt;br /&gt;
* Law of Demeter (Principle of Least Knowledge)&lt;br /&gt;
** We'll delegate interactions appropriately to prevent tightly coupled code, especially when dealing with topics and teams.&lt;br /&gt;
** Clear and intentional naming during refactoring will enhance code clarity and maintainability.&lt;br /&gt;
&lt;br /&gt;
* Adapter Pattern&lt;br /&gt;
** To add messaging to bid states for users, we'll use the adapter pattern to allow different components to work together without modifying their existing code.&lt;br /&gt;
&lt;br /&gt;
== Files Modified/Added ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-1.-controllers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
review_bids_controller.rb&lt;br /&gt;
&lt;br /&gt;
signup_sheet_controller.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-2.-models&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
review_bid.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-3.-helpers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
&lt;br /&gt;
authorization_helper.rb&lt;br /&gt;
&lt;br /&gt;
Review_bids_helper.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;viii.-test-plan&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
The Excel spreadsheet below details our test plan for testing the code we changed or created to ensure code coverage. The process involved reviewing the code and determining all the possible scenarios that needed to be covered based on what the method was doing, then creating test cases accordingly.&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/spreadsheets/d/1iHzOdSwlTtxOtf5VsNdwOaCP8NzYMmiJ/edit?usp=drive_link Open Test Matrix Spreadsheet]&lt;br /&gt;
&lt;br /&gt;
== Test Coverage ==&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;screenshot of SimpleCov coverage analysis&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x.-team&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-1.-mentor&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Ed Gehringer&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-2.-members&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Members ===&lt;br /&gt;
&lt;br /&gt;
* Gavin Teague&lt;br /&gt;
* Janice Uwujaren&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xi.-links&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
Previous Wiki: [https://wiki.expertiza.ncsu.edu/index.php/CSC/ECE_517_Fall_2021_-_E2151._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;Link&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xii.-references&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/document/d/1LHVacylQmB15I6VcYUA5nuC7GRRU9szHzZhjlPcE-PA/edit?tab=t.0#heading=h.u0oycoj6e0ev &amp;lt;u&amp;gt;Project Instructions&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;appendix-a-uml-class-diagram-plantuml-code-for-review-bids&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Juwujar</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160945</id>
		<title>CSC/ECE 517 Fall 2024 - E2485. Allow reviewers to bid on what to review</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160945"/>
		<updated>2024-12-10T14:25:50Z</updated>

		<summary type="html">&lt;p&gt;Juwujar: /* Optimize ReviewBidsController#set_priority Action functionality */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
This project will fix the defects and improve the functionality in Expertiza of the Reviewer Bidding feature, a process where reviewers can bid for assignments they want to review. It will make the functionality smoother, more reliable, and align with Expertiza's collaborative learning objectives.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iii.-problem-statements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Problem Statements ==&lt;br /&gt;
&lt;br /&gt;
Assigning reviews to users on Expertiza involves a complex bidding algorithm that requires optimization before production deployment. Below are key areas we plan to address as per the project requirements:&lt;br /&gt;
* Code Refactoring and Best Practices&lt;br /&gt;
** '''Utilize Authentication Utilities''': Modify the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; function in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to use existing authentication utilities, enhancing code reusability and adhering to the Single Responsibility Principle (SRP) and Separation of Concerns (SoC).&lt;br /&gt;
** '''Eliminate Code Duplication''': Refactor &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; to comply with the Don't Repeat Yourself (DRY) principle.&lt;br /&gt;
** '''Rename Functions for Clarity''': Change &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; to better reflect its purpose.&lt;br /&gt;
** '''Refactor Methods in '''&amp;lt;code&amp;gt;review_bid.rb&amp;lt;/code&amp;gt;''':''': Convert class methods to instance methods or relocate them to helper classes for improved code organization.&lt;br /&gt;
* Functionality Enhancements&lt;br /&gt;
** '''Handle Missing Bids Gracefully''': Adjust the bidding algorithm to accommodate students who do not bid, allowing them to select from the remaining options.&lt;br /&gt;
** '''Support Both Individual and Team Assignments''': Ensure the code functions correctly for participant and team-based assignments when signing up for topics.&lt;br /&gt;
* User Interface Improvements&lt;br /&gt;
** '''Increase Transparency in the Bidding Process''': Display messages indicating how many students are eligible to bid, how many have submitted bids, and the deadline.&lt;br /&gt;
** '''Accurate Bid Status Indicators''': Ensure topic colors change correctly based on the number of outstanding bids, providing clear visual feedback to users.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Exhaustive Edge Case Testing''': Conduct thorough testing for edge cases not previously covered, adding additional test cases as needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iv.-design-goals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Design Goals ==&lt;br /&gt;
&lt;br /&gt;
While addressing the existing defects and enhancing the Reviewer Bidding feature, we plan to adhere to the following design guidelines:&lt;br /&gt;
&lt;br /&gt;
* Code Quality and Maintainability&lt;br /&gt;
** '''Improve Code Structure: '''Implement refactoring strategies to enhance code readability and maintainability, ensuring adherence to best practices like the DRY principle.&lt;br /&gt;
** '''Standardize Authentication Logic: '''Utilize a centralized authentication utility to promote consistency and reusability across the application.&lt;br /&gt;
* System Stability and Flexibility&lt;br /&gt;
** '''Enhance Algorithm Robustness: '''Modify the bidding algorithm to handle scenarios where students do not bid, ensuring the system remains stable under all conditions.&lt;br /&gt;
** '''Ensure Assignment Versatility: '''Design the system to seamlessly support both individual and team assignments without additional configuration.&lt;br /&gt;
* User Experience Enhancements&lt;br /&gt;
** '''Increase Bidding Transparency: '''Develop user interface elements that clearly display bidding status, deadlines, and participation metrics.&lt;br /&gt;
** '''Implement Dynamic Visual Feedback: '''Use real-time visual cues, such as color changes, to inform users about the bidding status instantly.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Develop Comprehensive Test Suites: '''Create extensive automated tests covering edge cases and critical functionalities to guarantee reliable system behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;v.-uml-diagram&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== UML Diagram ==&lt;br /&gt;
&lt;br /&gt;
Below is a visual representation of the models associated with the review bids functionality, depicting the data structures and their relationships.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml class diagram2.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi.-implementation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
'''ReviewBid: '''The &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; manages the bidding process, where participants indicate their preferences for reviewing different topics or assignments. This helps distribute review tasks fairly based on participants' interests.&lt;br /&gt;
&lt;br /&gt;
'''Assignment: '''The &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model represents team or individual assignments in Expertiza. It stores all the essential information required to keep the assignment process organized, like managing participants, setting due dates, forming teams, and overseeing the review process.&lt;br /&gt;
&lt;br /&gt;
'''SignUpTopic: '''The &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt; model identifies specific topics or areas within an assignment for which participants can sign up. It manages the availability of these topics, tracks bids, coordinates team sign-ups, and assigns topics based on both bids and participant availability. &lt;br /&gt;
&lt;br /&gt;
'''Participant: '''The &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model is about the individual users who participate in an assignment. This includes students, instructors, teaching assistants, or other relevant roles. This model is responsible for managing key user information, monitoring interactions with the assignment, keeping track of team memberships, and recording things like badges and review grades.&lt;br /&gt;
&lt;br /&gt;
== Implementation ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1.-decoupling-authorization-logic-from-reviewbidscontroller&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Decoupling Authorization Logic from &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| '''&amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; needs to use the authentication utilities'''&lt;br /&gt;
&lt;br /&gt;
The function &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; should use the available authentication utilities to support the Single Responsibility Principle and Separation of Concerns and to support code reusability.&lt;br /&gt;
&lt;br /&gt;
| '''Authentication Consistency:''' Remove authentication logic in the controller and call logic in the authentication utility class instead to ensure consistent use of the same logic across the application, foster reusability, and eliminate DRY.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
==== Authorization Flow Before Refactoring ====&lt;br /&gt;
The sequence diagram depicts the authorization process within the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;. When a user invokes an action, the controller checks if the action is allowed by verifying the user's role through the &amp;lt;code&amp;gt;current_role_name()&amp;lt;/code&amp;gt; method in the &amp;lt;code&amp;gt;ApplicationController&amp;lt;/code&amp;gt;. Depending on the action and the user's role, the controller either authorizes the user to proceed or denies access, ultimately executing the action logic if authorized and responding to the user.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 1.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We simplified the &amp;lt;code&amp;gt;action_allowed?&amp;lt;/code&amp;gt; method by decoupling authorization logic from the controller to use helper methods in the &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt; module. The &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; should not handle authentication, which is a violation of SRP. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;vertical-align:top;&amp;quot; | Original Code !! style=&amp;quot;vertical-align:top;&amp;quot; | Refactored Code&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index'&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator',&lt;br /&gt;
     'Student'].include? current_role_name and&lt;br /&gt;
        ((%w[list].include? action_name) ? are_needed_authorizations_present?(params[:id], &amp;quot;participant&amp;quot;, &amp;quot;reader&amp;quot;, &amp;quot;submitter&amp;quot;, &amp;quot;reviewer&amp;quot;) : true)&lt;br /&gt;
  else&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator'].include? current_role_name&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index', 'list'&lt;br /&gt;
    current_user_has_student_privileges? &amp;amp;&amp;amp; list_authorization_check&lt;br /&gt;
  else&lt;br /&gt;
    current_user_has_ta_privileges?&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; action ===&lt;br /&gt;
The refactor of the &amp;lt;code&amp;gt;ReviewBidsController's index&amp;lt;/code&amp;gt; action simplifies and streamlines the codebase, ensuring the action simply handles controller activities. Previously, the controller was overloaded with responsibilities—it handled everything from locating and validating assignments to fetching review mappings and counting completed reviews. This all-in-one approach made the controller action messy. &lt;br /&gt;
&lt;br /&gt;
Now we have moved the business logic out of the controller action and into dedicated service objects. With this change, we introduced two key service objects: &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; focuses on retrieving the participant along with their assignments, while the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; takes on the task of managing review mappings and calculating review-related stats, like how many reviews have been completed. &lt;br /&gt;
&lt;br /&gt;
This clear separation of responsibilities aligns with the Single Responsibility Principle, which helps each part of our code be more focused and easier to understand. With these new service objects, the code has become more reusable. The logic for fetching assignments, retrieving review mappings, and counting reviews is now more flexible and reusable throughout the application. For example, we used methods in the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; in both the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; and the &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;, helping to avoid code duplication.&lt;br /&gt;
&lt;br /&gt;
After moving the business logic to service objects, we can write unit tests to ensure each part of the code behaves as expected and prevents bugs. We also moved complex calculations, like those for completed reviews, into the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;, freeing the controller from dealing with the implementation details and promoting a cleaner design. The article https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial provides more information on when and how to use service objects in Rails.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
  return unless current_user_id?(@participant.user_id)&lt;br /&gt;
  @assignment = @participant.assignment&lt;br /&gt;
  @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
  # Finding how many reviews have been completed&lt;br /&gt;
  @num_reviews_completed = 0&lt;br /&gt;
  @review_mappings.each do |map|&lt;br /&gt;
    @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # render view for completing reviews after review bidding has been completed&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @assignment = participant_service.assignment&lt;br /&gt;
  unless @assignment.is_a?(Assignment)&lt;br /&gt;
    flash[:error] = 'Assignment not found.'&lt;br /&gt;
    redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  @review_mappings = review_service.review_mappings&lt;br /&gt;
  @num_reviews_completed = review_service.review_counts[:completed]&lt;br /&gt;
&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Optimize &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Action functionality ===&lt;br /&gt;
In the earlier version of the set_priority action in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, the server only needed a couple of key pieces of information: an array of topic IDs (params[:topic]) and the participant ID (params[:id}). From there, it would figure out the assignment_id by using the first topic as a reference. While this worked in simple cases, it could lead to problems if any data was missing or didn’t line up correctly.&lt;br /&gt;
&lt;br /&gt;
With the new implementation, the action now expects clear and structured parameters, including participant_id, an array of topic IDs, and assignment_id. This change ensures that the server always receives complete and reliable information, preventing issues caused by missing or inconsistent data. It also removes the need for the server to perform unnecessary lookups, allowing the controller to focus on its main job: validating inputs (like making sure the topics in params[:topic] belong to the same assignment as the participant), updating the database, handling errors properly, and sending the correct response back to the view.&lt;br /&gt;
&lt;br /&gt;
While these updates have cleaned up the set_priority action, they still contain a lot of business logic, like validations and priority updates. To make the code even more maintainable, there’s room for further refactoring—perhaps moving some of this logic into a service class or model method to streamline the controller even more.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | Old Code&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    if params[:topic].nil?&lt;br /&gt;
      ReviewBid.where(participant_id: params[:id]).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      participant = AssignmentParticipant.find_by(id: params[:id])&lt;br /&gt;
      assignment_id = SignUpTopic.find(params[:topic].first).assignment.id&lt;br /&gt;
      @bids = ReviewBid.where(participant_id: params[:id])&lt;br /&gt;
      signed_up_topics = ReviewBid.where(participant_id: params[:id]).map(&amp;amp;:signuptopic_id)&lt;br /&gt;
      signed_up_topics -= params[:topic].map(&amp;amp;:to_i)&lt;br /&gt;
      signed_up_topics.each do |topic|&lt;br /&gt;
        ReviewBid.where(signuptopic_id: topic, participant_id: params[:id]).destroy_all&lt;br /&gt;
      end&lt;br /&gt;
      params[:topic].each_with_index do |topic_id, index|&lt;br /&gt;
        bid_existence = ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id])&lt;br /&gt;
        if bid_existence.empty?&lt;br /&gt;
          ReviewBid.create(priority: index + 1, signuptopic_id: topic_id, participant_id: params[:id], assignment_id: assignment_id)&lt;br /&gt;
        else&lt;br /&gt;
          ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id]).update_all(priority: index + 1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to action: 'show', assignment_id: params[:assignment_id], id: params[:id]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    participant_id = params[:participant_id].to_i&lt;br /&gt;
    selected_topic_ids = params[:topic]&amp;amp;.map(&amp;amp;:to_i) || []&lt;br /&gt;
    assignment_id = params[:assignment_id].to_i&lt;br /&gt;
&lt;br /&gt;
    unless current_user_can_modify_participant?(participant_id)&lt;br /&gt;
      flash[:error] = 'You are not authorized to modify these bids.'&lt;br /&gt;
      respond_to do |format|&lt;br /&gt;
        format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
        format.json { render json: { status: 'unauthorized' }, status: :unauthorized }&lt;br /&gt;
      end&lt;br /&gt;
      return&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    if selected_topic_ids.empty?&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      assignment = Assignment.find_by(id: assignment_id)&lt;br /&gt;
      if assignment.nil?&lt;br /&gt;
        flash[:error] = &amp;quot;Invalid assignment.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_assignment' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      unless SignUpTopic.where(id: selected_topic_ids, assignment_id: assignment_id).count == selected_topic_ids.size&lt;br /&gt;
        flash[:error] = &amp;quot;One or more selected topics are invalid.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_topics' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).where.not(signuptopic_id: selected_topic_ids).destroy_all&lt;br /&gt;
&lt;br /&gt;
      selected_topic_ids.each_with_index do |topic_id, index|&lt;br /&gt;
        bid = ReviewBid.find_or_initialize_by(signuptopic_id: topic_id, participant_id: participant_id)&lt;br /&gt;
        bid.priority = index + 1&lt;br /&gt;
        bid.assignment_id = assignment_id&lt;br /&gt;
        bid.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_to action: 'show', assignment_id: assignment_id, id: participant_id, notice: 'Review bids updated successfully.' }&lt;br /&gt;
      format.json { render json: { status: 'success' }, status: :ok }&lt;br /&gt;
    end&lt;br /&gt;
  rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
    flash[:error] = &amp;quot;Failed to update priorities: #{e.message}&amp;quot;&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
      format.json { render json: { status: 'error', message: e.message }, status: :unprocessable_entity }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Client Side Changes to Support &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Changes ====&lt;br /&gt;
After tidying up the server-side logic, we needed to refactor the client-side code to ensure it passed the correct information from the view to the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action. On the client side, we now grab the topicIDs and send them as &amp;lt;code&amp;gt;selected_topic_ids&amp;lt;/code&amp;gt;, allowing the controller to figure out which topics were selected and in what order. It’s a cleaner and more reliable way to keep everything working together! &lt;br /&gt;
&lt;br /&gt;
We are also extracting the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, making it a lot easier to send data with AJAX and keeping things flexible. AJAX is awesome because it lets us update the server without reloading the page, making the user experience smooth and responsive. When we pull the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt; from the DOM, we can grab it along with other data, like the list of topics, and include it in the request. This way, we don’t have to rely on the URL to know which participant we’re working with, which makes the code cleaner and more adaptable.&lt;br /&gt;
&lt;br /&gt;
If we were to rely on the route for the &amp;lt;code&amp;gt;participant_id&amp;lt;/code&amp;gt;, every AJAX request would need to be tied to a specific URL structure. That’s limiting, especially if the route doesn’t naturally include the participant ID, as can be the case with collection routes versus member routes in Rails. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery -&amp;gt;&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move',&lt;br /&gt;
    opacity: 0.65,&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: -&amp;gt;&lt;br /&gt;
      $.post($(this).data('update-url'), $(this).sortable('serialize'))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;coffeescript&amp;quot;&amp;gt;&lt;br /&gt;
jQuery(document).ready ($) -&amp;gt;&lt;br /&gt;
  sendUpdatedOrder = -&amp;gt;&lt;br /&gt;
    sortedIDs = $(this).sortable('toArray')&lt;br /&gt;
&lt;br /&gt;
    topicIDs = sortedIDs.map (id) -&amp;gt;&lt;br /&gt;
      id.replace 'topic_', ''&lt;br /&gt;
&lt;br /&gt;
    participant_id = $(this).data('participant-id')&lt;br /&gt;
&lt;br /&gt;
    assignment_id = new URL($(this).data('update-url'), window.location.origin).searchParams.get('assignment_id')&lt;br /&gt;
&lt;br /&gt;
    $.ajax&lt;br /&gt;
      type: 'POST'&lt;br /&gt;
      url: $(this).data('update-url')&lt;br /&gt;
      data:&lt;br /&gt;
        topic: topicIDs&lt;br /&gt;
        participant_id: participant_id&lt;br /&gt;
        assignment_id: assignment_id&lt;br /&gt;
      success: (response) -&amp;gt;&lt;br /&gt;
        console.log 'Selections updated successfully.'&lt;br /&gt;
      error: (xhr, status, error) -&amp;gt;&lt;br /&gt;
        alert 'An error occurred while saving your selections. Please try again.'&lt;br /&gt;
        console.error error&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#topics&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  $(&amp;quot;#selections&amp;quot;).sortable&lt;br /&gt;
    cursor: 'move'&lt;br /&gt;
    opacity: 0.65&lt;br /&gt;
    tolerance: 'pointer'&lt;br /&gt;
    connectWith: &amp;quot;.connectedSortable&amp;quot;&lt;br /&gt;
    items: &amp;quot;&amp;gt;*:not(.sort-disabled)&amp;quot;&lt;br /&gt;
    update: sendUpdatedOrder&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-1.-signupsheetcontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; Updates ===&lt;br /&gt;
&lt;br /&gt;
The ReviewBidsController's index method renders the review_bids_others_work action in &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt;. However, the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; does not include the controller action for this view. We need to find the old implementation of the controller action, add it back, and review and update it for any DRY violations.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # provides variables for reviewing page located at views/review_bids/others_work.html.erb&lt;br /&gt;
  def index&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
    return unless current_user_id?(@participant.user_id)&lt;br /&gt;
&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
    # Finding how many reviews have been completed&lt;br /&gt;
    @num_reviews_completed = 0&lt;br /&gt;
    @review_mappings.each do |map|&lt;br /&gt;
      @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # render view for completing reviews after review bidding has been completed&lt;br /&gt;
    render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
  end  &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-2.-reviewbidsotherswork-view-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;review_bids_others_work.html.erb&amp;lt;/code&amp;gt; View Updates ====&lt;br /&gt;
&lt;br /&gt;
The code only contains a view with this name and no associated controller action. Reviewing the code in the view, we noticed some SRP violations where there is much logic in the presentation layer. Set up helper methods in the controller to handle some of that logic or see if the model can handle some of that logic.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Rename &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; method ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; should be &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt;&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:''' Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The primary activity here is to refactor the method name &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; and fix the hardcoded URL. However, the match topic bidding algorithm no longer works since Heroku is no longer free, per the professor. The reference link below has detailed information about the web service that we can leverage to gain that understanding.&lt;br /&gt;
&lt;br /&gt;
Reference Link for Webservice Details: [https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to rename the &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; method to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; to make the functionality of the method clear. We also plan to verify that the method is working appropriately by mocking up the service call since it is no longer working.&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # Other methods&lt;br /&gt;
&lt;br /&gt;
  # Call webserver for running the assigning algorithm&lt;br /&gt;
  # Passing to webserver:&lt;br /&gt;
  # - student_ids&lt;br /&gt;
  # - topic_ids&lt;br /&gt;
  # - student_preferences&lt;br /&gt;
  # - time_stamps&lt;br /&gt;
  # Webserver returns:&lt;br /&gt;
  # - matched assignments as JSON body&lt;br /&gt;
  def run_bidding_algorithm(bidding_data)&lt;br /&gt;
    url = 'http://app-csc517.herokuapp.com/match_topics' # Hard coding for the time being&lt;br /&gt;
    response = RestClient.post url, bidding_data.to_json, content_type: 'application/json', accept: :json&lt;br /&gt;
    JSON.parse(response.body)&lt;br /&gt;
  rescue StandardError =&amp;gt; e&lt;br /&gt;
    Rails.logger.error &amp;quot;Bidding algorithm failed: #{e.message}&amp;quot;&lt;br /&gt;
    false&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Updated Implementation ====&lt;br /&gt;
We implemented an update using mocked data and a service as placeholders until a new URL is in place. We did not end up refactoring to rename this method as we found that the name describes the functionality quite well.&lt;br /&gt;
&lt;br /&gt;
==== review_bids_controller ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def run_bidding_algorithm&lt;br /&gt;
    review_bid = ReviewBid.new&lt;br /&gt;
    bidding_data = review_bid.bidding_data&lt;br /&gt;
    matched_topics = BiddingAlgorithmService.new(bidding_data).run&lt;br /&gt;
    raise ArgumentError, 'Failed to assign reviewers. Please try again later.' unless matched_topics&lt;br /&gt;
&lt;br /&gt;
    matched_topics&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== bidding_algorithm_service ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# frozen_string_literal: true&lt;br /&gt;
&lt;br /&gt;
# The `BiddingAlgorithmService` run the bid assignment algorithm&lt;br /&gt;
# Sends student IDs, topic IDs, student preferences, and timestamps to the web service&lt;br /&gt;
# The web service returns the matched assignments in the JSON response body&lt;br /&gt;
class BiddingAlgorithmService&lt;br /&gt;
  SERVICE_URL = 'http://app-csc517.herokuapp.com/match_topics'.freeze&lt;br /&gt;
&lt;br /&gt;
  # MOCK_DATA is based on the documentation detailing how the webservice behaves.&lt;br /&gt;
  # Reference: https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review#Webservice&lt;br /&gt;
  MOCK_DATA = {&lt;br /&gt;
    36239 =&amp;gt; [3970, 3972, 3975],&lt;br /&gt;
    36240 =&amp;gt; [3973, 3974, 3972],&lt;br /&gt;
    36241 =&amp;gt; [3969, 3971, 3972],&lt;br /&gt;
    36242 =&amp;gt; [3969, 3971, 3973],&lt;br /&gt;
    36243 =&amp;gt; [3969, 3970, 3971]&lt;br /&gt;
  }.freeze&lt;br /&gt;
&lt;br /&gt;
  def initialize(bidding_data)&lt;br /&gt;
    @bidding_data = bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def run&lt;br /&gt;
    return MOCK_DATA if Rails.application.config.use_mock_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
    perform_request&lt;br /&gt;
  rescue RestClient::ExceptionWithResponse, JSON::ParserError&lt;br /&gt;
    false&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def perform_request&lt;br /&gt;
    response = RestClient.post(&lt;br /&gt;
      SERVICE_URL,&lt;br /&gt;
      @bidding_data.to_json,&lt;br /&gt;
      content_type: :json,&lt;br /&gt;
      accept: :json&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    validate_response(response)&lt;br /&gt;
    JSON.parse(response.body)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def validate_response(response)&lt;br /&gt;
    raise 'Invalid response format' unless response.headers[:content_type] &amp;amp;&amp;amp; response.headers[:content_type].include?('application/json')&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Resolve Functionality Issues for Non Bidders ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Currently it doesn't work if some student does not bid. In this case, algorithm needs to be fixed to ignore anyone who didn’t bid, and let them choose from what’s left over.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to do several things within this controller. We will set a default state for non-bidders that we can point to to give them options to select topics to review from the leftovers. We will consolidate &amp;lt;code&amp;gt;@sign_up_topics&amp;lt;/code&amp;gt; to resolve the DRY violation. Lastly, we will remove business logic from the show method and push it to a new helper to conform to the SRP.&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # Additional method&lt;br /&gt;
&lt;br /&gt;
  # Provides variables for review bidding page&lt;br /&gt;
  def show&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id].to_i)&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @sign_up_topics = SignUpTopic.where(assignment_id: @assignment.id, private_to: nil)&lt;br /&gt;
    my_topic = SignedUpTeam.topic_id(@participant.parent_id, @participant.user_id)&lt;br /&gt;
    @sign_up_topics -= SignUpTopic.where(assignment_id: @assignment.id, id: my_topic)&lt;br /&gt;
    @num_participants = AssignmentParticipant.where(parent_id: @assignment.id).count&lt;br /&gt;
    @selected_topics = nil # This is used to list the topics assigned to review (i.e., select == assigned)&lt;br /&gt;
    @bids = ReviewBid.where(participant_id: @participant, assignment_id: @assignment.id)&lt;br /&gt;
    signed_up_topics = []&lt;br /&gt;
    &lt;br /&gt;
    @bids.each do |bid|&lt;br /&gt;
      sign_up_topic = SignUpTopic.find_by(id: bid.signuptopic_id)&lt;br /&gt;
      signed_up_topics &amp;lt;&amp;lt; sign_up_topic if sign_up_topic&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    signed_up_topics &amp;amp;= @sign_up_topics&lt;br /&gt;
    @sign_up_topics -= signed_up_topics&lt;br /&gt;
    @bids = signed_up_topics&lt;br /&gt;
    @num_of_topics = @sign_up_topics.size&lt;br /&gt;
    @assigned_review_maps = []&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: @assignment.id, reviewer_id: @participant.id).each do |review_map|&lt;br /&gt;
      @assigned_review_maps &amp;lt;&amp;lt; review_map&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Explicitly render view since it's in the sign up sheet views&lt;br /&gt;
    render 'sign_up_sheet/review_bids_show'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Additional methods&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-2.-reviewbidshelper-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
 # Assigns bidding topics to reviewers&lt;br /&gt;
  def assign_bidding&lt;br /&gt;
      assignment = validate_assignment(params[:assignment_id])&lt;br /&gt;
&lt;br /&gt;
      reviewers = validate_reviewers(assignment.id)&lt;br /&gt;
      reviewer_ids = reviewers.map(&amp;amp;:id)&lt;br /&gt;
&lt;br /&gt;
      matched_topics = run_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
      if matched_topics.blank?&lt;br /&gt;
        flash[:alert] = 'Topic or assignment is missing'&lt;br /&gt;
        redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      leftover_topics = find_leftover_topics(assignment.id, matched_topics)&lt;br /&gt;
      assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.new.assign_review_topics(matched_topics)&lt;br /&gt;
      assignment.update!(can_choose_topic_to_review: false)&lt;br /&gt;
&lt;br /&gt;
      flash[:notice] = 'Reviewers were successfully assigned to topics.'&lt;br /&gt;
      redirect_back fallback_location: root_path&lt;br /&gt;
    rescue ArgumentError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ArgumentError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: e.message&lt;br /&gt;
    rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::RecordInvalid: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue ActiveRecord::ActiveRecordError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::ActiveRecordError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue StandardError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;StandardError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers. Please try again later.'&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  #service methods here ...&lt;br /&gt;
&lt;br /&gt;
  def find_leftover_topics(assignment_id, matched_topics)&lt;br /&gt;
    all_topic_ids = SignUpTopic.where(assignment_id: assignment_id).pluck(:id)&lt;br /&gt;
    assigned_topic_ids = matched_topics.map { |match| match[:topic_id] }&lt;br /&gt;
&lt;br /&gt;
    # Calculate leftover topics&lt;br /&gt;
    all_topic_ids - assigned_topic_ids&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
    return if leftover_topics.blank?&lt;br /&gt;
&lt;br /&gt;
    # Find non-bidders by excluding already matched reviewers&lt;br /&gt;
    non_bidders = reviewer_ids - matched_topics.keys&lt;br /&gt;
&lt;br /&gt;
    # Assign leftover topics to non-bidders in a round-robin fashion&lt;br /&gt;
    non_bidders.each_with_index do |reviewer_id, index|&lt;br /&gt;
      topic_id = leftover_topics[index % leftover_topics.length]&lt;br /&gt;
      ReviewBid.create(priority: 1, signuptopic_id: topic_id, participant_id: reviewer_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirm Functionality for Single-person Teams ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Make sure your code works on individual assignments, i.e., assignments where participants sign up for topics instead of teams. In this case, a team is created for each participant when they sign up. So the code should work for assignments to which either individuals or teams submit.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to make an update to focus on topic rather than team or have it where teams are updated based on topic id.We will probably utilize sign up topic or a similar object to accomplish this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-5-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bid Status Messaging ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| To make the functionality more intuitive, include a message to say how many students are eligible to submit bids, how many have submitted their bids, when the deadline for submitting bids is.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
The logic for these messages will go into the helper file. It is effectively the same reasoning as our migration of other business/functionality code from the controller to the helper to conform to the SRP. It will go hand in hand with the SignUpTopic portion of the code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-6-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Why are the methods in review_bid.rb class methods? Can we change them to instance methods or move it to helpers?&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-1.-reviewbid-model-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model Updates ====&lt;br /&gt;
Since the current class methods are tied to &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model instances, we will change them to instance methods. There is no need to move them to helpers.&lt;br /&gt;
&lt;br /&gt;
===== Review Bid Model Flow Before Refactoring =====&lt;br /&gt;
This sequence diagram describes how the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; interacts with the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model in assigning review topics to reviewers according to their bids. Once the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; action is initiated by the user, the controller gathers the IDs of reviewers and calls &amp;lt;code&amp;gt;ReviewBid.bidding_data&amp;lt;/code&amp;gt; to gather bidding information. That is sent out to an external bidding algorithm, and the matched topics that come back are used by &amp;lt;code&amp;gt;ReviewBid.assign_review_topics&amp;lt;/code&amp;gt; to create &amp;lt;code&amp;gt;ReviewResponseMap&amp;lt;/code&amp;gt; entries, assigning topics to reviewers for the assignment.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
  belongs_to :assignment, class_name: 'Assignment'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  def self.bidding_data(assignment_id, reviewer_ids)&lt;br /&gt;
    # create basic hash and set basic hash data&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'users' =&amp;gt; {}, 'max_accepted_proposals' =&amp;gt; [] }&lt;br /&gt;
    bidding_data['tid'] = SignUpTopic.where(assignment_id: assignment_id).ids&lt;br /&gt;
    bidding_data['max_accepted_proposals'] = Assignment.where(id: assignment_id).pluck(:num_reviews_allowed).first&lt;br /&gt;
&lt;br /&gt;
    # loop through reviewer_ids to get reviewer specific bidding data&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      bidding_data['users'][reviewer_id] = reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # assigns topics to reviews as matched by the webservice algorithm&lt;br /&gt;
  def self.assign_review_topics(assignment_id, reviewer_ids, matched_topics, _min_num_reviews = 2)&lt;br /&gt;
    # if review response map already created, delete it&lt;br /&gt;
    if ReviewResponseMap.where(reviewed_object_id: assignment_id)&lt;br /&gt;
      ReviewResponseMap.where(reviewed_object_id: assignment_id).destroy_all&lt;br /&gt;
    end&lt;br /&gt;
    # loop through reviewer_ids to assign reviews to each reviewer&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      topics_to_assign = matched_topics[reviewer_id.to_s]&lt;br /&gt;
      topics_to_assign.each do |topic|&lt;br /&gt;
        assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method to assign a single topic to a reviewer&lt;br /&gt;
  def self.assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic).pluck(:team_id).first&lt;br /&gt;
    team_to_review.nil? ? [] : ReviewResponseMap.create(reviewed_object_id: assignment_id, reviewer_id: reviewer_id, reviewee_id: team_to_review, type: 'ReviewResponseMap')&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method for getting individual reviewer_ids bidding data&lt;br /&gt;
  # returns user's bidding data hash&lt;br /&gt;
  def self.reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    reviewer_user_id = AssignmentParticipant.find(reviewer_id).user_id&lt;br /&gt;
    self_topic = SignedUpTeam.topic_id(assignment_id, reviewer_user_id)&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'otid' =&amp;gt; self_topic, 'priority' =&amp;gt; [], 'time' =&amp;gt; [] }&lt;br /&gt;
    bids = ReviewBid.where(participant_id: reviewer_id)&lt;br /&gt;
&lt;br /&gt;
    # loop through each bid for a topic to get specific data&lt;br /&gt;
    bids.each do |bid|&lt;br /&gt;
      bidding_data['tid'] &amp;lt;&amp;lt; bid.signuptopic_id&lt;br /&gt;
      bidding_data['priority'] &amp;lt;&amp;lt; bid.priority&lt;br /&gt;
      bidding_data['time'] &amp;lt;&amp;lt; bid.updated_at&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  # Instance method to get bidding data for this review bid&lt;br /&gt;
  def bidding_data&lt;br /&gt;
    {&lt;br /&gt;
      'tid' =&amp;gt; topic_ids,&lt;br /&gt;
      'otid' =&amp;gt; self_topic,&lt;br /&gt;
      'priority' =&amp;gt; priorities,&lt;br /&gt;
      'time' =&amp;gt; updated_times&lt;br /&gt;
    }&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # bid object to limit db touches for similar data acquisitions&lt;br /&gt;
  def bids&lt;br /&gt;
    @bids ||= ReviewBid.where(participant_id: participant_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def topic_ids&lt;br /&gt;
    bids.pluck(:signuptopic_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def priorities&lt;br /&gt;
    bids.pluck(:priority)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def updated_times&lt;br /&gt;
    bids.pluck(:updated_at)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self_topic&lt;br /&gt;
    return nil unless topic &amp;amp;&amp;amp; topic.assignment_id&lt;br /&gt;
&lt;br /&gt;
    SignedUpTeam.topic_id(topic.assignment_id, participant.user_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign topics to reviews&lt;br /&gt;
  def assign_review_topics(matched_topics)&lt;br /&gt;
    validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: topic.assignment_id).destroy_all&lt;br /&gt;
&lt;br /&gt;
    matched_topics.each do |reviewer_id, topics|&lt;br /&gt;
      Array(topics).each { |topic_id| assign_topic_to_reviewer(reviewer_id, topic_id) }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign a single topic to a reviewer&lt;br /&gt;
  def assign_topic_to_reviewer(reviewer_id, topic_id)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic_id).pluck(:team_id).first&lt;br /&gt;
    return if team_to_review.nil?&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.create(&lt;br /&gt;
      reviewed_object_id: topic.assignment_id,&lt;br /&gt;
      reviewer_id: reviewer_id,&lt;br /&gt;
      reviewee_id: team_to_review,&lt;br /&gt;
      type: 'ReviewResponseMap'&lt;br /&gt;
    )&lt;br /&gt;
    # error handling&lt;br /&gt;
  rescue StandardError =&amp;gt; e&lt;br /&gt;
    Rails.logger.error(&amp;quot;Failed to assign topic #{topic_id} to reviewer #{reviewer_id}: #{e.message}&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
    raise ArgumentError, 'Topic or assignment is missing' if topic.nil? || topic.assignment_id.nil?&lt;br /&gt;
    raise ArgumentError, 'Matched topics must be a Hash' unless matched_topics.is_a?(Hash)&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
Once we change them to instance methods, we’ll need to modify their use in the code to instantiate the object first.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;Placeholder for implementation screenshots (before and after)&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-3.-schema.db-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;Schema.db&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We also need to remove the foreign key reference to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model since the &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model already has access to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;, and that is the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; we should be referencing. This will also involve modifying the database to remove the foreign key reference to the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Steps required:&lt;br /&gt;
&lt;br /&gt;
# Review the records in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table in the database to see if there are any records and if those records have values for &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;&lt;br /&gt;
# Create a migration file using the command: &amp;lt;code&amp;gt;rails generate migration RemoveAssignmentFromReviewBids&amp;lt;/code&amp;gt;&lt;br /&gt;
# In the generated file, indicate the change that needs to be made using the change method and migration columns &amp;lt;code&amp;gt;remove_foreign_key&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;remove_column&amp;lt;/code&amp;gt;. Migration should be straightforward since there is no cascading behavior (i.e., &amp;lt;code&amp;gt;dependent:destroy&amp;lt;/code&amp;gt;) to worry about.&lt;br /&gt;
# Once comfortable with the updates to the generated file, run the migration using the &amp;lt;code&amp;gt;rails db:migrate&amp;lt;/code&amp;gt; command.&lt;br /&gt;
# Run a query in the DB to ensure no orphaned records are in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table after the updates.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;Placeholder for implementation screenshots (before and after)&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-4.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implement Edge Case Testing ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| In the previous implementation wiki, there are edge cases which are not exhaustively tested. Should test those edge cases thoroughly and add more edge case testing - link&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span class=&amp;quot;mark&amp;quot;&amp;gt;TODO&amp;lt;/span&amp;gt;:We just got initial test results back. We still need to figure out why tests are failing and what may be slipping through the cracks.[[File:Test expertiza.png|411x459px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-8-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design Patterns and Principles ===&lt;br /&gt;
Below are the design patterns and principles we plan to use to address the problem statements outlined in the project requirements:&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
** Controller Responsibility&lt;br /&gt;
*** To address the issue of authentication logic residing in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, we will refactor the code to ensure the controller solely handles request and response flows.&lt;br /&gt;
*** Authentication logic will be moved to dedicated methods in &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt;, making the codebase easier to maintain and test.&lt;br /&gt;
** View Simplification&lt;br /&gt;
*** Currently, the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; view contains excessive business logic, violating SRP.&lt;br /&gt;
*** We will move this logic into helper methods or models, ensuring each component has a single responsibility and improving code readability.&lt;br /&gt;
&lt;br /&gt;
* Separation of Concerns (SoC)&lt;br /&gt;
** By relocating authorization logic out of the controller, we separate authentication from request handling.&lt;br /&gt;
** Adhering to SoC enhances readability and maintainability by allowing each part of the application to focus on its specific role.&lt;br /&gt;
* Don't Repeat Yourself (DRY)&lt;br /&gt;
** We will inspect the existing implementation for code duplication, particularly in the controller actions.&lt;br /&gt;
** Any repetitive code will be refactored or removed to reduce redundancy and improve clarity.&lt;br /&gt;
&lt;br /&gt;
* Model-View-Controller (MVC) Adherence&lt;br /&gt;
** Moving business logic from the view aligns with MVC best practices.&lt;br /&gt;
** By ensuring the view only presents data without processing it, we maintain clear boundaries between the model, view, and controller layers.&lt;br /&gt;
&lt;br /&gt;
* Dependency Inversion Principle (DIP)&lt;br /&gt;
** We plan to update class methods to instance methods, allowing the controller to depend on abstractions rather than concrete implementations.&lt;br /&gt;
** This change leverages Ruby's duck typing, providing flexibility and making the code easier to maintain and test.&lt;br /&gt;
&lt;br /&gt;
* Factory Pattern&lt;br /&gt;
** We will implement a factory pattern for the common processing of &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpBid&amp;lt;/code&amp;gt;, encapsulating object creation and promoting code reuse.&lt;br /&gt;
&lt;br /&gt;
* Law of Demeter (Principle of Least Knowledge)&lt;br /&gt;
** We'll delegate interactions appropriately to prevent tightly coupled code, especially when dealing with topics and teams.&lt;br /&gt;
** Clear and intentional naming during refactoring will enhance code clarity and maintainability.&lt;br /&gt;
&lt;br /&gt;
* Adapter Pattern&lt;br /&gt;
** To add messaging to bid states for users, we'll use the adapter pattern to allow different components to work together without modifying their existing code.&lt;br /&gt;
&lt;br /&gt;
== Files Modified/Added ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-1.-controllers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
review_bids_controller.rb&lt;br /&gt;
&lt;br /&gt;
signup_sheet_controller.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-2.-models&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
review_bid.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-3.-helpers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
&lt;br /&gt;
authorization_helper.rb&lt;br /&gt;
&lt;br /&gt;
Review_bids_helper.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;viii.-test-plan&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
The Excel spreadsheet below details our test plan for testing the code we changed or created to ensure code coverage. The process involved reviewing the code and determining all the possible scenarios that needed to be covered based on what the method was doing, then creating test cases accordingly.&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/spreadsheets/d/1iHzOdSwlTtxOtf5VsNdwOaCP8NzYMmiJ/edit?usp=drive_link Open Test Matrix Spreadsheet]&lt;br /&gt;
&lt;br /&gt;
== Test Coverage ==&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;screenshot of SimpleCov coverage analysis&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x.-team&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-1.-mentor&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Ed Gehringer&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-2.-members&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Members ===&lt;br /&gt;
&lt;br /&gt;
* Gavin Teague&lt;br /&gt;
* Janice Uwujaren&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xi.-links&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
Previous Wiki: [https://wiki.expertiza.ncsu.edu/index.php/CSC/ECE_517_Fall_2021_-_E2151._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;Link&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xii.-references&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/document/d/1LHVacylQmB15I6VcYUA5nuC7GRRU9szHzZhjlPcE-PA/edit?tab=t.0#heading=h.u0oycoj6e0ev &amp;lt;u&amp;gt;Project Instructions&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;appendix-a-uml-class-diagram-plantuml-code-for-review-bids&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Juwujar</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160942</id>
		<title>CSC/ECE 517 Fall 2024 - E2485. Allow reviewers to bid on what to review</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160942"/>
		<updated>2024-12-10T13:34:15Z</updated>

		<summary type="html">&lt;p&gt;Juwujar: /* Optimize set_priority action functionality */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
This project will fix the defects and improve the functionality in Expertiza of the Reviewer Bidding feature, a process where reviewers can bid for assignments they want to review. It will make the functionality smoother, more reliable, and align with Expertiza's collaborative learning objectives.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iii.-problem-statements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Problem Statements ==&lt;br /&gt;
&lt;br /&gt;
Assigning reviews to users on Expertiza involves a complex bidding algorithm that requires optimization before production deployment. Below are key areas we plan to address as per the project requirements:&lt;br /&gt;
* Code Refactoring and Best Practices&lt;br /&gt;
** '''Utilize Authentication Utilities''': Modify the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; function in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to use existing authentication utilities, enhancing code reusability and adhering to the Single Responsibility Principle (SRP) and Separation of Concerns (SoC).&lt;br /&gt;
** '''Eliminate Code Duplication''': Refactor &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; to comply with the Don't Repeat Yourself (DRY) principle.&lt;br /&gt;
** '''Rename Functions for Clarity''': Change &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; to better reflect its purpose.&lt;br /&gt;
** '''Refactor Methods in '''&amp;lt;code&amp;gt;review_bid.rb&amp;lt;/code&amp;gt;''':''': Convert class methods to instance methods or relocate them to helper classes for improved code organization.&lt;br /&gt;
* Functionality Enhancements&lt;br /&gt;
** '''Handle Missing Bids Gracefully''': Adjust the bidding algorithm to accommodate students who do not bid, allowing them to select from the remaining options.&lt;br /&gt;
** '''Support Both Individual and Team Assignments''': Ensure the code functions correctly for participant and team-based assignments when signing up for topics.&lt;br /&gt;
* User Interface Improvements&lt;br /&gt;
** '''Increase Transparency in the Bidding Process''': Display messages indicating how many students are eligible to bid, how many have submitted bids, and the deadline.&lt;br /&gt;
** '''Accurate Bid Status Indicators''': Ensure topic colors change correctly based on the number of outstanding bids, providing clear visual feedback to users.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Exhaustive Edge Case Testing''': Conduct thorough testing for edge cases not previously covered, adding additional test cases as needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iv.-design-goals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Design Goals ==&lt;br /&gt;
&lt;br /&gt;
While addressing the existing defects and enhancing the Reviewer Bidding feature, we plan to adhere to the following design guidelines:&lt;br /&gt;
&lt;br /&gt;
* Code Quality and Maintainability&lt;br /&gt;
** '''Improve Code Structure: '''Implement refactoring strategies to enhance code readability and maintainability, ensuring adherence to best practices like the DRY principle.&lt;br /&gt;
** '''Standardize Authentication Logic: '''Utilize a centralized authentication utility to promote consistency and reusability across the application.&lt;br /&gt;
* System Stability and Flexibility&lt;br /&gt;
** '''Enhance Algorithm Robustness: '''Modify the bidding algorithm to handle scenarios where students do not bid, ensuring the system remains stable under all conditions.&lt;br /&gt;
** '''Ensure Assignment Versatility: '''Design the system to seamlessly support both individual and team assignments without additional configuration.&lt;br /&gt;
* User Experience Enhancements&lt;br /&gt;
** '''Increase Bidding Transparency: '''Develop user interface elements that clearly display bidding status, deadlines, and participation metrics.&lt;br /&gt;
** '''Implement Dynamic Visual Feedback: '''Use real-time visual cues, such as color changes, to inform users about the bidding status instantly.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Develop Comprehensive Test Suites: '''Create extensive automated tests covering edge cases and critical functionalities to guarantee reliable system behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;v.-uml-diagram&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== UML Diagram ==&lt;br /&gt;
&lt;br /&gt;
Below is a visual representation of the models associated with the review bids functionality, depicting the data structures and their relationships.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml class diagram2.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi.-implementation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
'''ReviewBid: '''The &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; manages the bidding process, where participants indicate their preferences for reviewing different topics or assignments. This helps distribute review tasks fairly based on participants' interests.&lt;br /&gt;
&lt;br /&gt;
'''Assignment: '''The &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model represents team or individual assignments in Expertiza. It stores all the essential information required to keep the assignment process organized, like managing participants, setting due dates, forming teams, and overseeing the review process.&lt;br /&gt;
&lt;br /&gt;
'''SignUpTopic: '''The &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt; model identifies specific topics or areas within an assignment for which participants can sign up. It manages the availability of these topics, tracks bids, coordinates team sign-ups, and assigns topics based on both bids and participant availability. &lt;br /&gt;
&lt;br /&gt;
'''Participant: '''The &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model is about the individual users who participate in an assignment. This includes students, instructors, teaching assistants, or other relevant roles. This model is responsible for managing key user information, monitoring interactions with the assignment, keeping track of team memberships, and recording things like badges and review grades.&lt;br /&gt;
&lt;br /&gt;
== Implementation ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1.-decoupling-authorization-logic-from-reviewbidscontroller&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Decoupling Authorization Logic from &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| '''&amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; needs to use the authentication utilities'''&lt;br /&gt;
&lt;br /&gt;
The function &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; should use the available authentication utilities to support the Single Responsibility Principle and Separation of Concerns and to support code reusability.&lt;br /&gt;
&lt;br /&gt;
| '''Authentication Consistency:''' Remove authentication logic in the controller and call logic in the authentication utility class instead to ensure consistent use of the same logic across the application, foster reusability, and eliminate DRY.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
==== Authorization Flow Before Refactoring ====&lt;br /&gt;
The sequence diagram depicts the authorization process within the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;. When a user invokes an action, the controller checks if the action is allowed by verifying the user's role through the &amp;lt;code&amp;gt;current_role_name()&amp;lt;/code&amp;gt; method in the &amp;lt;code&amp;gt;ApplicationController&amp;lt;/code&amp;gt;. Depending on the action and the user's role, the controller either authorizes the user to proceed or denies access, ultimately executing the action logic if authorized and responding to the user.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 1.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We simplified the &amp;lt;code&amp;gt;action_allowed?&amp;lt;/code&amp;gt; method by decoupling authorization logic from the controller to use helper methods in the &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt; module. The &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; should not handle authentication, which is a violation of SRP. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;vertical-align:top;&amp;quot; | Original Code !! style=&amp;quot;vertical-align:top;&amp;quot; | Refactored Code&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index'&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator',&lt;br /&gt;
     'Student'].include? current_role_name and&lt;br /&gt;
        ((%w[list].include? action_name) ? are_needed_authorizations_present?(params[:id], &amp;quot;participant&amp;quot;, &amp;quot;reader&amp;quot;, &amp;quot;submitter&amp;quot;, &amp;quot;reviewer&amp;quot;) : true)&lt;br /&gt;
  else&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator'].include? current_role_name&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index', 'list'&lt;br /&gt;
    current_user_has_student_privileges? &amp;amp;&amp;amp; list_authorization_check&lt;br /&gt;
  else&lt;br /&gt;
    current_user_has_ta_privileges?&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; action ===&lt;br /&gt;
The refactor of the &amp;lt;code&amp;gt;ReviewBidsController's index&amp;lt;/code&amp;gt; action simplifies and streamlines the codebase, ensuring the action simply handles controller activities. Previously, the controller was overloaded with responsibilities—it handled everything from locating and validating assignments to fetching review mappings and counting completed reviews. This all-in-one approach made the controller action messy. &lt;br /&gt;
&lt;br /&gt;
Now we have moved the business logic out of the controller action and into dedicated service objects. With this change, we introduced two key service objects: &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; focuses on retrieving the participant along with their assignments, while the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; takes on the task of managing review mappings and calculating review-related stats, like how many reviews have been completed. &lt;br /&gt;
&lt;br /&gt;
This clear separation of responsibilities aligns with the Single Responsibility Principle, which helps each part of our code be more focused and easier to understand. With these new service objects, the code has become more reusable. The logic for fetching assignments, retrieving review mappings, and counting reviews is now more flexible and reusable throughout the application. For example, we used methods in the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; in both the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; and the &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;, helping to avoid code duplication.&lt;br /&gt;
&lt;br /&gt;
After moving the business logic to service objects, we can write unit tests to ensure each part of the code behaves as expected and prevents bugs. We also moved complex calculations, like those for completed reviews, into the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;, freeing the controller from dealing with the implementation details and promoting a cleaner design. The article https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial provides more information on when and how to use service objects in Rails.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
  return unless current_user_id?(@participant.user_id)&lt;br /&gt;
  @assignment = @participant.assignment&lt;br /&gt;
  @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
  # Finding how many reviews have been completed&lt;br /&gt;
  @num_reviews_completed = 0&lt;br /&gt;
  @review_mappings.each do |map|&lt;br /&gt;
    @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # render view for completing reviews after review bidding has been completed&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @assignment = participant_service.assignment&lt;br /&gt;
  unless @assignment.is_a?(Assignment)&lt;br /&gt;
    flash[:error] = 'Assignment not found.'&lt;br /&gt;
    redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  @review_mappings = review_service.review_mappings&lt;br /&gt;
  @num_reviews_completed = review_service.review_counts[:completed]&lt;br /&gt;
&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Optimize &amp;lt;code&amp;gt;ReviewBidsController#set_priority&amp;lt;/code&amp;gt; Action functionality ===&lt;br /&gt;
&lt;br /&gt;
In the earlier version of the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action within the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, the server only needed a couple of essential pieces of information: the array of topic IDs (params[:topic]) and the participant ID (params[:id]). From there, the code would figure out the assignment_id using the first topic as a reference point. But this approach could cause some problems, especially if some data is missing or doesn’t align correctly. &lt;br /&gt;
&lt;br /&gt;
With the new implementation, the action now expects well-defined and structured parameters, including participant_id, an array of topic IDs, and assignment_id. This change stops the server from working with potentially incomplete data and ensures that all the necessary information is sent to the server so that the controller action does not have to perform unnecessary lookups and can focus on what it does best: validating the input, like ensuring that the topics in params[:topic] belong to the same assignment as the participant, updating the database, handling errors appropriately, and sending the appropriate response back to the view.&lt;br /&gt;
&lt;br /&gt;
While we have made these initial changes to clean up the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action, the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action currently is still handling a lot of business logic like validations, updating priorities, and still requires some additional refactoring to move some of that logic to possibly a service class or model method.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | Old Code&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    if params[:topic].nil?&lt;br /&gt;
      ReviewBid.where(participant_id: params[:id]).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      participant = AssignmentParticipant.find_by(id: params[:id])&lt;br /&gt;
      assignment_id = SignUpTopic.find(params[:topic].first).assignment.id&lt;br /&gt;
      @bids = ReviewBid.where(participant_id: params[:id])&lt;br /&gt;
      signed_up_topics = ReviewBid.where(participant_id: params[:id]).map(&amp;amp;:signuptopic_id)&lt;br /&gt;
      signed_up_topics -= params[:topic].map(&amp;amp;:to_i)&lt;br /&gt;
      signed_up_topics.each do |topic|&lt;br /&gt;
        ReviewBid.where(signuptopic_id: topic, participant_id: params[:id]).destroy_all&lt;br /&gt;
      end&lt;br /&gt;
      params[:topic].each_with_index do |topic_id, index|&lt;br /&gt;
        bid_existence = ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id])&lt;br /&gt;
        if bid_existence.empty?&lt;br /&gt;
          ReviewBid.create(priority: index + 1, signuptopic_id: topic_id, participant_id: params[:id], assignment_id: assignment_id)&lt;br /&gt;
        else&lt;br /&gt;
          ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id]).update_all(priority: index + 1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to action: 'show', assignment_id: params[:assignment_id], id: params[:id]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    participant_id = params[:participant_id].to_i&lt;br /&gt;
    selected_topic_ids = params[:topic]&amp;amp;.map(&amp;amp;:to_i) || []&lt;br /&gt;
    assignment_id = params[:assignment_id].to_i&lt;br /&gt;
&lt;br /&gt;
    unless current_user_can_modify_participant?(participant_id)&lt;br /&gt;
      flash[:error] = 'You are not authorized to modify these bids.'&lt;br /&gt;
      respond_to do |format|&lt;br /&gt;
        format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
        format.json { render json: { status: 'unauthorized' }, status: :unauthorized }&lt;br /&gt;
      end&lt;br /&gt;
      return&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    if selected_topic_ids.empty?&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      assignment = Assignment.find_by(id: assignment_id)&lt;br /&gt;
      if assignment.nil?&lt;br /&gt;
        flash[:error] = &amp;quot;Invalid assignment.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_assignment' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      unless SignUpTopic.where(id: selected_topic_ids, assignment_id: assignment_id).count == selected_topic_ids.size&lt;br /&gt;
        flash[:error] = &amp;quot;One or more selected topics are invalid.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_topics' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).where.not(signuptopic_id: selected_topic_ids).destroy_all&lt;br /&gt;
&lt;br /&gt;
      selected_topic_ids.each_with_index do |topic_id, index|&lt;br /&gt;
        bid = ReviewBid.find_or_initialize_by(signuptopic_id: topic_id, participant_id: participant_id)&lt;br /&gt;
        bid.priority = index + 1&lt;br /&gt;
        bid.assignment_id = assignment_id&lt;br /&gt;
        bid.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_to action: 'show', assignment_id: assignment_id, id: participant_id, notice: 'Review bids updated successfully.' }&lt;br /&gt;
      format.json { render json: { status: 'success' }, status: :ok }&lt;br /&gt;
    end&lt;br /&gt;
  rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
    flash[:error] = &amp;quot;Failed to update priorities: #{e.message}&amp;quot;&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
      format.json { render json: { status: 'error', message: e.message }, status: :unprocessable_entity }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-1.-signupsheetcontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; Updates ===&lt;br /&gt;
&lt;br /&gt;
The ReviewBidsController's index method renders the review_bids_others_work action in &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt;. However, the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; does not include the controller action for this view. We need to find the old implementation of the controller action, add it back, and review and update it for any DRY violations.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # provides variables for reviewing page located at views/review_bids/others_work.html.erb&lt;br /&gt;
  def index&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
    return unless current_user_id?(@participant.user_id)&lt;br /&gt;
&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
    # Finding how many reviews have been completed&lt;br /&gt;
    @num_reviews_completed = 0&lt;br /&gt;
    @review_mappings.each do |map|&lt;br /&gt;
      @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # render view for completing reviews after review bidding has been completed&lt;br /&gt;
    render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
  end  &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-2.-reviewbidsotherswork-view-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;review_bids_others_work.html.erb&amp;lt;/code&amp;gt; View Updates ====&lt;br /&gt;
&lt;br /&gt;
The code only contains a view with this name and no associated controller action. Reviewing the code in the view, we noticed some SRP violations where there is much logic in the presentation layer. Set up helper methods in the controller to handle some of that logic or see if the model can handle some of that logic.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Rename &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; method ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; should be &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt;&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:''' Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The primary activity here is to refactor the method name &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; and fix the hardcoded URL. However, the match topic bidding algorithm no longer works since Heroku is no longer free, per the professor. The reference link below has detailed information about the web service that we can leverage to gain that understanding.&lt;br /&gt;
&lt;br /&gt;
Reference Link for Webservice Details: [https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to rename the &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; method to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; to make the functionality of the method clear. We also plan to verify that the method is working appropriately by mocking up the service call since it is no longer working.&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # Other methods&lt;br /&gt;
&lt;br /&gt;
  # Call webserver for running the assigning algorithm&lt;br /&gt;
  # Passing to webserver:&lt;br /&gt;
  # - student_ids&lt;br /&gt;
  # - topic_ids&lt;br /&gt;
  # - student_preferences&lt;br /&gt;
  # - time_stamps&lt;br /&gt;
  # Webserver returns:&lt;br /&gt;
  # - matched assignments as JSON body&lt;br /&gt;
  def run_bidding_algorithm(bidding_data)&lt;br /&gt;
    url = 'http://app-csc517.herokuapp.com/match_topics' # Hard coding for the time being&lt;br /&gt;
    response = RestClient.post url, bidding_data.to_json, content_type: 'application/json', accept: :json&lt;br /&gt;
    JSON.parse(response.body)&lt;br /&gt;
  rescue StandardError =&amp;gt; e&lt;br /&gt;
    Rails.logger.error &amp;quot;Bidding algorithm failed: #{e.message}&amp;quot;&lt;br /&gt;
    false&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Updated Implementation ====&lt;br /&gt;
We implemented an update using mocked data and a service as placeholders until a new URL is in place. We did not end up refactoring to rename this method as we found that the name describes the functionality quite well.&lt;br /&gt;
&lt;br /&gt;
==== review_bids_controller ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def run_bidding_algorithm&lt;br /&gt;
    review_bid = ReviewBid.new&lt;br /&gt;
    bidding_data = review_bid.bidding_data&lt;br /&gt;
    matched_topics = BiddingAlgorithmService.new(bidding_data).run&lt;br /&gt;
    raise ArgumentError, 'Failed to assign reviewers. Please try again later.' unless matched_topics&lt;br /&gt;
&lt;br /&gt;
    matched_topics&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== bidding_algorithm_service ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# frozen_string_literal: true&lt;br /&gt;
&lt;br /&gt;
# The `BiddingAlgorithmService` run the bid assignment algorithm&lt;br /&gt;
# Sends student IDs, topic IDs, student preferences, and timestamps to the web service&lt;br /&gt;
# The web service returns the matched assignments in the JSON response body&lt;br /&gt;
class BiddingAlgorithmService&lt;br /&gt;
  SERVICE_URL = 'http://app-csc517.herokuapp.com/match_topics'.freeze&lt;br /&gt;
&lt;br /&gt;
  # MOCK_DATA is based on the documentation detailing how the webservice behaves.&lt;br /&gt;
  # Reference: https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review#Webservice&lt;br /&gt;
  MOCK_DATA = {&lt;br /&gt;
    36239 =&amp;gt; [3970, 3972, 3975],&lt;br /&gt;
    36240 =&amp;gt; [3973, 3974, 3972],&lt;br /&gt;
    36241 =&amp;gt; [3969, 3971, 3972],&lt;br /&gt;
    36242 =&amp;gt; [3969, 3971, 3973],&lt;br /&gt;
    36243 =&amp;gt; [3969, 3970, 3971]&lt;br /&gt;
  }.freeze&lt;br /&gt;
&lt;br /&gt;
  def initialize(bidding_data)&lt;br /&gt;
    @bidding_data = bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def run&lt;br /&gt;
    return MOCK_DATA if Rails.application.config.use_mock_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
    perform_request&lt;br /&gt;
  rescue RestClient::ExceptionWithResponse, JSON::ParserError&lt;br /&gt;
    false&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def perform_request&lt;br /&gt;
    response = RestClient.post(&lt;br /&gt;
      SERVICE_URL,&lt;br /&gt;
      @bidding_data.to_json,&lt;br /&gt;
      content_type: :json,&lt;br /&gt;
      accept: :json&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    validate_response(response)&lt;br /&gt;
    JSON.parse(response.body)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def validate_response(response)&lt;br /&gt;
    raise 'Invalid response format' unless response.headers[:content_type] &amp;amp;&amp;amp; response.headers[:content_type].include?('application/json')&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Resolve Functionality Issues for Non Bidders ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Currently it doesn't work if some student does not bid. In this case, algorithm needs to be fixed to ignore anyone who didn’t bid, and let them choose from what’s left over.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to do several things within this controller. We will set a default state for non-bidders that we can point to to give them options to select topics to review from the leftovers. We will consolidate &amp;lt;code&amp;gt;@sign_up_topics&amp;lt;/code&amp;gt; to resolve the DRY violation. Lastly, we will remove business logic from the show method and push it to a new helper to conform to the SRP.&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # Additional method&lt;br /&gt;
&lt;br /&gt;
  # Provides variables for review bidding page&lt;br /&gt;
  def show&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id].to_i)&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @sign_up_topics = SignUpTopic.where(assignment_id: @assignment.id, private_to: nil)&lt;br /&gt;
    my_topic = SignedUpTeam.topic_id(@participant.parent_id, @participant.user_id)&lt;br /&gt;
    @sign_up_topics -= SignUpTopic.where(assignment_id: @assignment.id, id: my_topic)&lt;br /&gt;
    @num_participants = AssignmentParticipant.where(parent_id: @assignment.id).count&lt;br /&gt;
    @selected_topics = nil # This is used to list the topics assigned to review (i.e., select == assigned)&lt;br /&gt;
    @bids = ReviewBid.where(participant_id: @participant, assignment_id: @assignment.id)&lt;br /&gt;
    signed_up_topics = []&lt;br /&gt;
    &lt;br /&gt;
    @bids.each do |bid|&lt;br /&gt;
      sign_up_topic = SignUpTopic.find_by(id: bid.signuptopic_id)&lt;br /&gt;
      signed_up_topics &amp;lt;&amp;lt; sign_up_topic if sign_up_topic&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    signed_up_topics &amp;amp;= @sign_up_topics&lt;br /&gt;
    @sign_up_topics -= signed_up_topics&lt;br /&gt;
    @bids = signed_up_topics&lt;br /&gt;
    @num_of_topics = @sign_up_topics.size&lt;br /&gt;
    @assigned_review_maps = []&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: @assignment.id, reviewer_id: @participant.id).each do |review_map|&lt;br /&gt;
      @assigned_review_maps &amp;lt;&amp;lt; review_map&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Explicitly render view since it's in the sign up sheet views&lt;br /&gt;
    render 'sign_up_sheet/review_bids_show'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Additional methods&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-2.-reviewbidshelper-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
 # Assigns bidding topics to reviewers&lt;br /&gt;
  def assign_bidding&lt;br /&gt;
      assignment = validate_assignment(params[:assignment_id])&lt;br /&gt;
&lt;br /&gt;
      reviewers = validate_reviewers(assignment.id)&lt;br /&gt;
      reviewer_ids = reviewers.map(&amp;amp;:id)&lt;br /&gt;
&lt;br /&gt;
      matched_topics = run_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
      if matched_topics.blank?&lt;br /&gt;
        flash[:alert] = 'Topic or assignment is missing'&lt;br /&gt;
        redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      leftover_topics = find_leftover_topics(assignment.id, matched_topics)&lt;br /&gt;
      assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.new.assign_review_topics(matched_topics)&lt;br /&gt;
      assignment.update!(can_choose_topic_to_review: false)&lt;br /&gt;
&lt;br /&gt;
      flash[:notice] = 'Reviewers were successfully assigned to topics.'&lt;br /&gt;
      redirect_back fallback_location: root_path&lt;br /&gt;
    rescue ArgumentError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ArgumentError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: e.message&lt;br /&gt;
    rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::RecordInvalid: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue ActiveRecord::ActiveRecordError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::ActiveRecordError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue StandardError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;StandardError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers. Please try again later.'&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  #service methods here ...&lt;br /&gt;
&lt;br /&gt;
  def find_leftover_topics(assignment_id, matched_topics)&lt;br /&gt;
    all_topic_ids = SignUpTopic.where(assignment_id: assignment_id).pluck(:id)&lt;br /&gt;
    assigned_topic_ids = matched_topics.map { |match| match[:topic_id] }&lt;br /&gt;
&lt;br /&gt;
    # Calculate leftover topics&lt;br /&gt;
    all_topic_ids - assigned_topic_ids&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
    return if leftover_topics.blank?&lt;br /&gt;
&lt;br /&gt;
    # Find non-bidders by excluding already matched reviewers&lt;br /&gt;
    non_bidders = reviewer_ids - matched_topics.keys&lt;br /&gt;
&lt;br /&gt;
    # Assign leftover topics to non-bidders in a round-robin fashion&lt;br /&gt;
    non_bidders.each_with_index do |reviewer_id, index|&lt;br /&gt;
      topic_id = leftover_topics[index % leftover_topics.length]&lt;br /&gt;
      ReviewBid.create(priority: 1, signuptopic_id: topic_id, participant_id: reviewer_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirm Functionality for Single-person Teams ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Make sure your code works on individual assignments, i.e., assignments where participants sign up for topics instead of teams. In this case, a team is created for each participant when they sign up. So the code should work for assignments to which either individuals or teams submit.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to make an update to focus on topic rather than team or have it where teams are updated based on topic id.We will probably utilize sign up topic or a similar object to accomplish this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-5-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bid Status Messaging ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| To make the functionality more intuitive, include a message to say how many students are eligible to submit bids, how many have submitted their bids, when the deadline for submitting bids is.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
The logic for these messages will go into the helper file. It is effectively the same reasoning as our migration of other business/functionality code from the controller to the helper to conform to the SRP. It will go hand in hand with the SignUpTopic portion of the code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-6-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Why are the methods in review_bid.rb class methods? Can we change them to instance methods or move it to helpers?&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-1.-reviewbid-model-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model Updates ====&lt;br /&gt;
Since the current class methods are tied to &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model instances, we will change them to instance methods. There is no need to move them to helpers.&lt;br /&gt;
&lt;br /&gt;
===== Review Bid Model Flow Before Refactoring =====&lt;br /&gt;
This sequence diagram describes how the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; interacts with the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model in assigning review topics to reviewers according to their bids. Once the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; action is initiated by the user, the controller gathers the IDs of reviewers and calls &amp;lt;code&amp;gt;ReviewBid.bidding_data&amp;lt;/code&amp;gt; to gather bidding information. That is sent out to an external bidding algorithm, and the matched topics that come back are used by &amp;lt;code&amp;gt;ReviewBid.assign_review_topics&amp;lt;/code&amp;gt; to create &amp;lt;code&amp;gt;ReviewResponseMap&amp;lt;/code&amp;gt; entries, assigning topics to reviewers for the assignment.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
  belongs_to :assignment, class_name: 'Assignment'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  def self.bidding_data(assignment_id, reviewer_ids)&lt;br /&gt;
    # create basic hash and set basic hash data&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'users' =&amp;gt; {}, 'max_accepted_proposals' =&amp;gt; [] }&lt;br /&gt;
    bidding_data['tid'] = SignUpTopic.where(assignment_id: assignment_id).ids&lt;br /&gt;
    bidding_data['max_accepted_proposals'] = Assignment.where(id: assignment_id).pluck(:num_reviews_allowed).first&lt;br /&gt;
&lt;br /&gt;
    # loop through reviewer_ids to get reviewer specific bidding data&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      bidding_data['users'][reviewer_id] = reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # assigns topics to reviews as matched by the webservice algorithm&lt;br /&gt;
  def self.assign_review_topics(assignment_id, reviewer_ids, matched_topics, _min_num_reviews = 2)&lt;br /&gt;
    # if review response map already created, delete it&lt;br /&gt;
    if ReviewResponseMap.where(reviewed_object_id: assignment_id)&lt;br /&gt;
      ReviewResponseMap.where(reviewed_object_id: assignment_id).destroy_all&lt;br /&gt;
    end&lt;br /&gt;
    # loop through reviewer_ids to assign reviews to each reviewer&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      topics_to_assign = matched_topics[reviewer_id.to_s]&lt;br /&gt;
      topics_to_assign.each do |topic|&lt;br /&gt;
        assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method to assign a single topic to a reviewer&lt;br /&gt;
  def self.assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic).pluck(:team_id).first&lt;br /&gt;
    team_to_review.nil? ? [] : ReviewResponseMap.create(reviewed_object_id: assignment_id, reviewer_id: reviewer_id, reviewee_id: team_to_review, type: 'ReviewResponseMap')&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method for getting individual reviewer_ids bidding data&lt;br /&gt;
  # returns user's bidding data hash&lt;br /&gt;
  def self.reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    reviewer_user_id = AssignmentParticipant.find(reviewer_id).user_id&lt;br /&gt;
    self_topic = SignedUpTeam.topic_id(assignment_id, reviewer_user_id)&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'otid' =&amp;gt; self_topic, 'priority' =&amp;gt; [], 'time' =&amp;gt; [] }&lt;br /&gt;
    bids = ReviewBid.where(participant_id: reviewer_id)&lt;br /&gt;
&lt;br /&gt;
    # loop through each bid for a topic to get specific data&lt;br /&gt;
    bids.each do |bid|&lt;br /&gt;
      bidding_data['tid'] &amp;lt;&amp;lt; bid.signuptopic_id&lt;br /&gt;
      bidding_data['priority'] &amp;lt;&amp;lt; bid.priority&lt;br /&gt;
      bidding_data['time'] &amp;lt;&amp;lt; bid.updated_at&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  # Instance method to get bidding data for this review bid&lt;br /&gt;
  def bidding_data&lt;br /&gt;
    {&lt;br /&gt;
      'tid' =&amp;gt; topic_ids,&lt;br /&gt;
      'otid' =&amp;gt; self_topic,&lt;br /&gt;
      'priority' =&amp;gt; priorities,&lt;br /&gt;
      'time' =&amp;gt; updated_times&lt;br /&gt;
    }&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # bid object to limit db touches for similar data acquisitions&lt;br /&gt;
  def bids&lt;br /&gt;
    @bids ||= ReviewBid.where(participant_id: participant_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def topic_ids&lt;br /&gt;
    bids.pluck(:signuptopic_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def priorities&lt;br /&gt;
    bids.pluck(:priority)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def updated_times&lt;br /&gt;
    bids.pluck(:updated_at)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self_topic&lt;br /&gt;
    return nil unless topic &amp;amp;&amp;amp; topic.assignment_id&lt;br /&gt;
&lt;br /&gt;
    SignedUpTeam.topic_id(topic.assignment_id, participant.user_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign topics to reviews&lt;br /&gt;
  def assign_review_topics(matched_topics)&lt;br /&gt;
    validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: topic.assignment_id).destroy_all&lt;br /&gt;
&lt;br /&gt;
    matched_topics.each do |reviewer_id, topics|&lt;br /&gt;
      Array(topics).each { |topic_id| assign_topic_to_reviewer(reviewer_id, topic_id) }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign a single topic to a reviewer&lt;br /&gt;
  def assign_topic_to_reviewer(reviewer_id, topic_id)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic_id).pluck(:team_id).first&lt;br /&gt;
    return if team_to_review.nil?&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.create(&lt;br /&gt;
      reviewed_object_id: topic.assignment_id,&lt;br /&gt;
      reviewer_id: reviewer_id,&lt;br /&gt;
      reviewee_id: team_to_review,&lt;br /&gt;
      type: 'ReviewResponseMap'&lt;br /&gt;
    )&lt;br /&gt;
    # error handling&lt;br /&gt;
  rescue StandardError =&amp;gt; e&lt;br /&gt;
    Rails.logger.error(&amp;quot;Failed to assign topic #{topic_id} to reviewer #{reviewer_id}: #{e.message}&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
    raise ArgumentError, 'Topic or assignment is missing' if topic.nil? || topic.assignment_id.nil?&lt;br /&gt;
    raise ArgumentError, 'Matched topics must be a Hash' unless matched_topics.is_a?(Hash)&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
Once we change them to instance methods, we’ll need to modify their use in the code to instantiate the object first.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;Placeholder for implementation screenshots (before and after)&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-3.-schema.db-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;Schema.db&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We also need to remove the foreign key reference to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model since the &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model already has access to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;, and that is the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; we should be referencing. This will also involve modifying the database to remove the foreign key reference to the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Steps required:&lt;br /&gt;
&lt;br /&gt;
# Review the records in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table in the database to see if there are any records and if those records have values for &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;&lt;br /&gt;
# Create a migration file using the command: &amp;lt;code&amp;gt;rails generate migration RemoveAssignmentFromReviewBids&amp;lt;/code&amp;gt;&lt;br /&gt;
# In the generated file, indicate the change that needs to be made using the change method and migration columns &amp;lt;code&amp;gt;remove_foreign_key&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;remove_column&amp;lt;/code&amp;gt;. Migration should be straightforward since there is no cascading behavior (i.e., &amp;lt;code&amp;gt;dependent:destroy&amp;lt;/code&amp;gt;) to worry about.&lt;br /&gt;
# Once comfortable with the updates to the generated file, run the migration using the &amp;lt;code&amp;gt;rails db:migrate&amp;lt;/code&amp;gt; command.&lt;br /&gt;
# Run a query in the DB to ensure no orphaned records are in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table after the updates.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;Placeholder for implementation screenshots (before and after)&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-4.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implement Edge Case Testing ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| In the previous implementation wiki, there are edge cases which are not exhaustively tested. Should test those edge cases thoroughly and add more edge case testing - link&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span class=&amp;quot;mark&amp;quot;&amp;gt;TODO&amp;lt;/span&amp;gt;:We just got initial test results back. We still need to figure out why tests are failing and what may be slipping through the cracks.[[File:Test expertiza.png|411x459px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-8-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design Patterns and Principles ===&lt;br /&gt;
Below are the design patterns and principles we plan to use to address the problem statements outlined in the project requirements:&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
** Controller Responsibility&lt;br /&gt;
*** To address the issue of authentication logic residing in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, we will refactor the code to ensure the controller solely handles request and response flows.&lt;br /&gt;
*** Authentication logic will be moved to dedicated methods in &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt;, making the codebase easier to maintain and test.&lt;br /&gt;
** View Simplification&lt;br /&gt;
*** Currently, the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; view contains excessive business logic, violating SRP.&lt;br /&gt;
*** We will move this logic into helper methods or models, ensuring each component has a single responsibility and improving code readability.&lt;br /&gt;
&lt;br /&gt;
* Separation of Concerns (SoC)&lt;br /&gt;
** By relocating authorization logic out of the controller, we separate authentication from request handling.&lt;br /&gt;
** Adhering to SoC enhances readability and maintainability by allowing each part of the application to focus on its specific role.&lt;br /&gt;
* Don't Repeat Yourself (DRY)&lt;br /&gt;
** We will inspect the existing implementation for code duplication, particularly in the controller actions.&lt;br /&gt;
** Any repetitive code will be refactored or removed to reduce redundancy and improve clarity.&lt;br /&gt;
&lt;br /&gt;
* Model-View-Controller (MVC) Adherence&lt;br /&gt;
** Moving business logic from the view aligns with MVC best practices.&lt;br /&gt;
** By ensuring the view only presents data without processing it, we maintain clear boundaries between the model, view, and controller layers.&lt;br /&gt;
&lt;br /&gt;
* Dependency Inversion Principle (DIP)&lt;br /&gt;
** We plan to update class methods to instance methods, allowing the controller to depend on abstractions rather than concrete implementations.&lt;br /&gt;
** This change leverages Ruby's duck typing, providing flexibility and making the code easier to maintain and test.&lt;br /&gt;
&lt;br /&gt;
* Factory Pattern&lt;br /&gt;
** We will implement a factory pattern for the common processing of &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpBid&amp;lt;/code&amp;gt;, encapsulating object creation and promoting code reuse.&lt;br /&gt;
&lt;br /&gt;
* Law of Demeter (Principle of Least Knowledge)&lt;br /&gt;
** We'll delegate interactions appropriately to prevent tightly coupled code, especially when dealing with topics and teams.&lt;br /&gt;
** Clear and intentional naming during refactoring will enhance code clarity and maintainability.&lt;br /&gt;
&lt;br /&gt;
* Adapter Pattern&lt;br /&gt;
** To add messaging to bid states for users, we'll use the adapter pattern to allow different components to work together without modifying their existing code.&lt;br /&gt;
&lt;br /&gt;
== Files Modified/Added ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-1.-controllers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
review_bids_controller.rb&lt;br /&gt;
&lt;br /&gt;
signup_sheet_controller.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-2.-models&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
review_bid.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-3.-helpers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
&lt;br /&gt;
authorization_helper.rb&lt;br /&gt;
&lt;br /&gt;
Review_bids_helper.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;viii.-test-plan&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
The Excel spreadsheet below details our test plan for testing the code we changed or created to ensure code coverage. The process involved reviewing the code and determining all the possible scenarios that needed to be covered based on what the method was doing, then creating test cases accordingly.&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/spreadsheets/d/1iHzOdSwlTtxOtf5VsNdwOaCP8NzYMmiJ/edit?usp=drive_link Open Test Matrix Spreadsheet]&lt;br /&gt;
&lt;br /&gt;
== Test Coverage ==&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;screenshot of SimpleCov coverage analysis&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x.-team&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-1.-mentor&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Ed Gehringer&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-2.-members&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Members ===&lt;br /&gt;
&lt;br /&gt;
* Gavin Teague&lt;br /&gt;
* Janice Uwujaren&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xi.-links&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
Previous Wiki: [https://wiki.expertiza.ncsu.edu/index.php/CSC/ECE_517_Fall_2021_-_E2151._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;Link&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xii.-references&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/document/d/1LHVacylQmB15I6VcYUA5nuC7GRRU9szHzZhjlPcE-PA/edit?tab=t.0#heading=h.u0oycoj6e0ev &amp;lt;u&amp;gt;Project Instructions&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;appendix-a-uml-class-diagram-plantuml-code-for-review-bids&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Juwujar</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160941</id>
		<title>CSC/ECE 517 Fall 2024 - E2485. Allow reviewers to bid on what to review</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2024_-_E2485._Allow_reviewers_to_bid_on_what_to_review&amp;diff=160941"/>
		<updated>2024-12-10T13:33:40Z</updated>

		<summary type="html">&lt;p&gt;Juwujar: /* Refactor ReviewBidsController#Index action */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
This project will fix the defects and improve the functionality in Expertiza of the Reviewer Bidding feature, a process where reviewers can bid for assignments they want to review. It will make the functionality smoother, more reliable, and align with Expertiza's collaborative learning objectives.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iii.-problem-statements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Problem Statements ==&lt;br /&gt;
&lt;br /&gt;
Assigning reviews to users on Expertiza involves a complex bidding algorithm that requires optimization before production deployment. Below are key areas we plan to address as per the project requirements:&lt;br /&gt;
* Code Refactoring and Best Practices&lt;br /&gt;
** '''Utilize Authentication Utilities''': Modify the &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; function in the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to use existing authentication utilities, enhancing code reusability and adhering to the Single Responsibility Principle (SRP) and Separation of Concerns (SoC).&lt;br /&gt;
** '''Eliminate Code Duplication''': Refactor &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; to comply with the Don't Repeat Yourself (DRY) principle.&lt;br /&gt;
** '''Rename Functions for Clarity''': Change &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; to better reflect its purpose.&lt;br /&gt;
** '''Refactor Methods in '''&amp;lt;code&amp;gt;review_bid.rb&amp;lt;/code&amp;gt;''':''': Convert class methods to instance methods or relocate them to helper classes for improved code organization.&lt;br /&gt;
* Functionality Enhancements&lt;br /&gt;
** '''Handle Missing Bids Gracefully''': Adjust the bidding algorithm to accommodate students who do not bid, allowing them to select from the remaining options.&lt;br /&gt;
** '''Support Both Individual and Team Assignments''': Ensure the code functions correctly for participant and team-based assignments when signing up for topics.&lt;br /&gt;
* User Interface Improvements&lt;br /&gt;
** '''Increase Transparency in the Bidding Process''': Display messages indicating how many students are eligible to bid, how many have submitted bids, and the deadline.&lt;br /&gt;
** '''Accurate Bid Status Indicators''': Ensure topic colors change correctly based on the number of outstanding bids, providing clear visual feedback to users.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Exhaustive Edge Case Testing''': Conduct thorough testing for edge cases not previously covered, adding additional test cases as needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;iv.-design-goals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Design Goals ==&lt;br /&gt;
&lt;br /&gt;
While addressing the existing defects and enhancing the Reviewer Bidding feature, we plan to adhere to the following design guidelines:&lt;br /&gt;
&lt;br /&gt;
* Code Quality and Maintainability&lt;br /&gt;
** '''Improve Code Structure: '''Implement refactoring strategies to enhance code readability and maintainability, ensuring adherence to best practices like the DRY principle.&lt;br /&gt;
** '''Standardize Authentication Logic: '''Utilize a centralized authentication utility to promote consistency and reusability across the application.&lt;br /&gt;
* System Stability and Flexibility&lt;br /&gt;
** '''Enhance Algorithm Robustness: '''Modify the bidding algorithm to handle scenarios where students do not bid, ensuring the system remains stable under all conditions.&lt;br /&gt;
** '''Ensure Assignment Versatility: '''Design the system to seamlessly support both individual and team assignments without additional configuration.&lt;br /&gt;
* User Experience Enhancements&lt;br /&gt;
** '''Increase Bidding Transparency: '''Develop user interface elements that clearly display bidding status, deadlines, and participation metrics.&lt;br /&gt;
** '''Implement Dynamic Visual Feedback: '''Use real-time visual cues, such as color changes, to inform users about the bidding status instantly.&lt;br /&gt;
* Testing and Validation&lt;br /&gt;
** '''Develop Comprehensive Test Suites: '''Create extensive automated tests covering edge cases and critical functionalities to guarantee reliable system behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;v.-uml-diagram&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== UML Diagram ==&lt;br /&gt;
&lt;br /&gt;
Below is a visual representation of the models associated with the review bids functionality, depicting the data structures and their relationships.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml class diagram2.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi.-implementation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
'''ReviewBid: '''The &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; manages the bidding process, where participants indicate their preferences for reviewing different topics or assignments. This helps distribute review tasks fairly based on participants' interests.&lt;br /&gt;
&lt;br /&gt;
'''Assignment: '''The &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model represents team or individual assignments in Expertiza. It stores all the essential information required to keep the assignment process organized, like managing participants, setting due dates, forming teams, and overseeing the review process.&lt;br /&gt;
&lt;br /&gt;
'''SignUpTopic: '''The &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt; model identifies specific topics or areas within an assignment for which participants can sign up. It manages the availability of these topics, tracks bids, coordinates team sign-ups, and assigns topics based on both bids and participant availability. &lt;br /&gt;
&lt;br /&gt;
'''Participant: '''The &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model is about the individual users who participate in an assignment. This includes students, instructors, teaching assistants, or other relevant roles. This model is responsible for managing key user information, monitoring interactions with the assignment, keeping track of team memberships, and recording things like badges and review grades.&lt;br /&gt;
&lt;br /&gt;
== Implementation ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1.-decoupling-authorization-logic-from-reviewbidscontroller&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Decoupling Authorization Logic from &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| '''&amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; needs to use the authentication utilities'''&lt;br /&gt;
&lt;br /&gt;
The function &amp;lt;code&amp;gt;action_allowed&amp;lt;/code&amp;gt; should use the available authentication utilities to support the Single Responsibility Principle and Separation of Concerns and to support code reusability.&lt;br /&gt;
&lt;br /&gt;
| '''Authentication Consistency:''' Remove authentication logic in the controller and call logic in the authentication utility class instead to ensure consistent use of the same logic across the application, foster reusability, and eliminate DRY.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
==== Authorization Flow Before Refactoring ====&lt;br /&gt;
The sequence diagram depicts the authorization process within the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;. When a user invokes an action, the controller checks if the action is allowed by verifying the user's role through the &amp;lt;code&amp;gt;current_role_name()&amp;lt;/code&amp;gt; method in the &amp;lt;code&amp;gt;ApplicationController&amp;lt;/code&amp;gt;. Depending on the action and the user's role, the controller either authorizes the user to proceed or denies access, ultimately executing the action logic if authorized and responding to the user.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 1.png|600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-1-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We simplified the &amp;lt;code&amp;gt;action_allowed?&amp;lt;/code&amp;gt; method by decoupling authorization logic from the controller to use helper methods in the &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt; module. The &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; should not handle authentication, which is a violation of SRP. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;vertical-align:top;&amp;quot; | Original Code !! style=&amp;quot;vertical-align:top;&amp;quot; | Refactored Code&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index'&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator',&lt;br /&gt;
     'Student'].include? current_role_name and&lt;br /&gt;
        ((%w[list].include? action_name) ? are_needed_authorizations_present?(params[:id], &amp;quot;participant&amp;quot;, &amp;quot;reader&amp;quot;, &amp;quot;submitter&amp;quot;, &amp;quot;reviewer&amp;quot;) : true)&lt;br /&gt;
  else&lt;br /&gt;
    ['Instructor',&lt;br /&gt;
     'Teaching Assistant',&lt;br /&gt;
     'Administrator',&lt;br /&gt;
     'Super-Administrator'].include? current_role_name&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|| &amp;lt;div style=&amp;quot;vertical-align:top;&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def action_allowed?&lt;br /&gt;
  case params[:action]&lt;br /&gt;
  when 'show', 'set_priority', 'index', 'list'&lt;br /&gt;
    current_user_has_student_privileges? &amp;amp;&amp;amp; list_authorization_check&lt;br /&gt;
  else&lt;br /&gt;
    current_user_has_ta_privileges?&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBidsController#index&amp;lt;/code&amp;gt; action ===&lt;br /&gt;
The refactor of the &amp;lt;code&amp;gt;ReviewBidsController's index&amp;lt;/code&amp;gt; action simplifies and streamlines the codebase, ensuring the action simply handles controller activities. Previously, the controller was overloaded with responsibilities—it handled everything from locating and validating assignments to fetching review mappings and counting completed reviews. This all-in-one approach made the controller action messy. &lt;br /&gt;
&lt;br /&gt;
Now we have moved the business logic out of the controller action and into dedicated service objects. With this change, we introduced two key service objects: &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;ParticipantService&amp;lt;/code&amp;gt; focuses on retrieving the participant along with their assignments, while the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; takes on the task of managing review mappings and calculating review-related stats, like how many reviews have been completed. &lt;br /&gt;
&lt;br /&gt;
This clear separation of responsibilities aligns with the Single Responsibility Principle, which helps each part of our code be more focused and easier to understand. With these new service objects, the code has become more reusable. The logic for fetching assignments, retrieving review mappings, and counting reviews is now more flexible and reusable throughout the application. For example, we used methods in the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt; in both the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; and the &amp;lt;code&amp;gt;StudentReviewController&amp;lt;/code&amp;gt;, helping to avoid code duplication.&lt;br /&gt;
&lt;br /&gt;
After moving the business logic to service objects, we can write unit tests to ensure each part of the code behaves as expected and prevents bugs. We also moved complex calculations, like those for completed reviews, into the &amp;lt;code&amp;gt;ReviewService&amp;lt;/code&amp;gt;, freeing the controller from dealing with the implementation details and promoting a cleaner design. The article https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial provides more information on when and how to use service objects in Rails.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Old Code&lt;br /&gt;
! New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
  return unless current_user_id?(@participant.user_id)&lt;br /&gt;
  @assignment = @participant.assignment&lt;br /&gt;
  @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
  # Finding how many reviews have been completed&lt;br /&gt;
  @num_reviews_completed = 0&lt;br /&gt;
  @review_mappings.each do |map|&lt;br /&gt;
    @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # render view for completing reviews after review bidding has been completed&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;white-space: pre-wrap;&amp;quot; |&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def index&lt;br /&gt;
  @assignment = participant_service.assignment&lt;br /&gt;
  unless @assignment.is_a?(Assignment)&lt;br /&gt;
    flash[:error] = 'Assignment not found.'&lt;br /&gt;
    redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  @review_mappings = review_service.review_mappings&lt;br /&gt;
  @num_reviews_completed = review_service.review_counts[:completed]&lt;br /&gt;
&lt;br /&gt;
  render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Optimize &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action functionality ===&lt;br /&gt;
&lt;br /&gt;
In the earlier version of the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action within the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, the server only needed a couple of essential pieces of information: the array of topic IDs (params[:topic]) and the participant ID (params[:id]). From there, the code would figure out the assignment_id using the first topic as a reference point. But this approach could cause some problems, especially if some data is missing or doesn’t align correctly. &lt;br /&gt;
&lt;br /&gt;
With the new implementation, the action now expects well-defined and structured parameters, including participant_id, an array of topic IDs, and assignment_id. This change stops the server from working with potentially incomplete data and ensures that all the necessary information is sent to the server so that the controller action does not have to perform unnecessary lookups and can focus on what it does best: validating the input, like ensuring that the topics in params[:topic] belong to the same assignment as the participant, updating the database, handling errors appropriately, and sending the appropriate response back to the view.&lt;br /&gt;
&lt;br /&gt;
While we have made these initial changes to clean up the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action, the &amp;lt;code&amp;gt;set_priority&amp;lt;/code&amp;gt; action currently is still handling a lot of business logic like validations, updating priorities, and still requires some additional refactoring to move some of that logic to possibly a service class or model method.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | Old Code&lt;br /&gt;
! style=&amp;quot;width: 50%; text-align: left;&amp;quot; | New Code&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    if params[:topic].nil?&lt;br /&gt;
      ReviewBid.where(participant_id: params[:id]).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      participant = AssignmentParticipant.find_by(id: params[:id])&lt;br /&gt;
      assignment_id = SignUpTopic.find(params[:topic].first).assignment.id&lt;br /&gt;
      @bids = ReviewBid.where(participant_id: params[:id])&lt;br /&gt;
      signed_up_topics = ReviewBid.where(participant_id: params[:id]).map(&amp;amp;:signuptopic_id)&lt;br /&gt;
      signed_up_topics -= params[:topic].map(&amp;amp;:to_i)&lt;br /&gt;
      signed_up_topics.each do |topic|&lt;br /&gt;
        ReviewBid.where(signuptopic_id: topic, participant_id: params[:id]).destroy_all&lt;br /&gt;
      end&lt;br /&gt;
      params[:topic].each_with_index do |topic_id, index|&lt;br /&gt;
        bid_existence = ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id])&lt;br /&gt;
        if bid_existence.empty?&lt;br /&gt;
          ReviewBid.create(priority: index + 1, signuptopic_id: topic_id, participant_id: params[:id], assignment_id: assignment_id)&lt;br /&gt;
        else&lt;br /&gt;
          ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id]).update_all(priority: index + 1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to action: 'show', assignment_id: params[:assignment_id], id: params[:id]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def set_priority&lt;br /&gt;
    participant_id = params[:participant_id].to_i&lt;br /&gt;
    selected_topic_ids = params[:topic]&amp;amp;.map(&amp;amp;:to_i) || []&lt;br /&gt;
    assignment_id = params[:assignment_id].to_i&lt;br /&gt;
&lt;br /&gt;
    unless current_user_can_modify_participant?(participant_id)&lt;br /&gt;
      flash[:error] = 'You are not authorized to modify these bids.'&lt;br /&gt;
      respond_to do |format|&lt;br /&gt;
        format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
        format.json { render json: { status: 'unauthorized' }, status: :unauthorized }&lt;br /&gt;
      end&lt;br /&gt;
      return&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    if selected_topic_ids.empty?&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).destroy_all&lt;br /&gt;
    else&lt;br /&gt;
      assignment = Assignment.find_by(id: assignment_id)&lt;br /&gt;
      if assignment.nil?&lt;br /&gt;
        flash[:error] = &amp;quot;Invalid assignment.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_assignment' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      unless SignUpTopic.where(id: selected_topic_ids, assignment_id: assignment_id).count == selected_topic_ids.size&lt;br /&gt;
        flash[:error] = &amp;quot;One or more selected topics are invalid.&amp;quot;&lt;br /&gt;
        respond_to do |format|&lt;br /&gt;
          format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
          format.json { render json: { status: 'invalid_topics' }, status: :unprocessable_entity }&lt;br /&gt;
        end&lt;br /&gt;
        return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.where(participant_id: participant_id).where.not(signuptopic_id: selected_topic_ids).destroy_all&lt;br /&gt;
&lt;br /&gt;
      selected_topic_ids.each_with_index do |topic_id, index|&lt;br /&gt;
        bid = ReviewBid.find_or_initialize_by(signuptopic_id: topic_id, participant_id: participant_id)&lt;br /&gt;
        bid.priority = index + 1&lt;br /&gt;
        bid.assignment_id = assignment_id&lt;br /&gt;
        bid.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_to action: 'show', assignment_id: assignment_id, id: participant_id, notice: 'Review bids updated successfully.' }&lt;br /&gt;
      format.json { render json: { status: 'success' }, status: :ok }&lt;br /&gt;
    end&lt;br /&gt;
  rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
    flash[:error] = &amp;quot;Failed to update priorities: #{e.message}&amp;quot;&lt;br /&gt;
    respond_to do |format|&lt;br /&gt;
      format.html { redirect_back fallback_location: root_path }&lt;br /&gt;
      format.json { render json: { status: 'error', message: e.message }, status: :unprocessable_entity }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-1.-signupsheetcontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; Updates ===&lt;br /&gt;
&lt;br /&gt;
The ReviewBidsController's index method renders the review_bids_others_work action in &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt;. However, the &amp;lt;code&amp;gt;SignUpSheetController&amp;lt;/code&amp;gt; does not include the controller action for this view. We need to find the old implementation of the controller action, add it back, and review and update it for any DRY violations.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # provides variables for reviewing page located at views/review_bids/others_work.html.erb&lt;br /&gt;
  def index&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id])&lt;br /&gt;
    return unless current_user_id?(@participant.user_id)&lt;br /&gt;
&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)&lt;br /&gt;
&lt;br /&gt;
    # Finding how many reviews have been completed&lt;br /&gt;
    @num_reviews_completed = 0&lt;br /&gt;
    @review_mappings.each do |map|&lt;br /&gt;
      @num_reviews_completed += 1 if !map.response.empty? &amp;amp;&amp;amp; map.response.last.is_submitted&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # render view for completing reviews after review bidding has been completed&lt;br /&gt;
    render 'sign_up_sheet/review_bids_others_work'&lt;br /&gt;
  end  &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-2.-reviewbidsotherswork-view-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;review_bids_others_work.html.erb&amp;lt;/code&amp;gt; View Updates ====&lt;br /&gt;
&lt;br /&gt;
The code only contains a view with this name and no associated controller action. Reviewing the code in the view, we noticed some SRP violations where there is much logic in the presentation layer. Set up helper methods in the controller to handle some of that logic or see if the model can handle some of that logic.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-2-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Rename &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; method ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; should be &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt;&lt;br /&gt;
| '''Code Readability and Maintainability:''' Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:''' Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The primary activity here is to refactor the method name &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; and fix the hardcoded URL. However, the match topic bidding algorithm no longer works since Heroku is no longer free, per the professor. The reference link below has detailed information about the web service that we can leverage to gain that understanding.&lt;br /&gt;
&lt;br /&gt;
Reference Link for Webservice Details: [https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;match_topics webservice link details&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to rename the &amp;lt;code&amp;gt;run_bidding_algorithm&amp;lt;/code&amp;gt; method to &amp;lt;code&amp;gt;assign_reviewers&amp;lt;/code&amp;gt; to make the functionality of the method clear. We also plan to verify that the method is working appropriately by mocking up the service call since it is no longer working.&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # Other methods&lt;br /&gt;
&lt;br /&gt;
  # Call webserver for running the assigning algorithm&lt;br /&gt;
  # Passing to webserver:&lt;br /&gt;
  # - student_ids&lt;br /&gt;
  # - topic_ids&lt;br /&gt;
  # - student_preferences&lt;br /&gt;
  # - time_stamps&lt;br /&gt;
  # Webserver returns:&lt;br /&gt;
  # - matched assignments as JSON body&lt;br /&gt;
  def run_bidding_algorithm(bidding_data)&lt;br /&gt;
    url = 'http://app-csc517.herokuapp.com/match_topics' # Hard coding for the time being&lt;br /&gt;
    response = RestClient.post url, bidding_data.to_json, content_type: 'application/json', accept: :json&lt;br /&gt;
    JSON.parse(response.body)&lt;br /&gt;
  rescue StandardError =&amp;gt; e&lt;br /&gt;
    Rails.logger.error &amp;quot;Bidding algorithm failed: #{e.message}&amp;quot;&lt;br /&gt;
    false&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Updated Implementation ====&lt;br /&gt;
We implemented an update using mocked data and a service as placeholders until a new URL is in place. We did not end up refactoring to rename this method as we found that the name describes the functionality quite well.&lt;br /&gt;
&lt;br /&gt;
==== review_bids_controller ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  def run_bidding_algorithm&lt;br /&gt;
    review_bid = ReviewBid.new&lt;br /&gt;
    bidding_data = review_bid.bidding_data&lt;br /&gt;
    matched_topics = BiddingAlgorithmService.new(bidding_data).run&lt;br /&gt;
    raise ArgumentError, 'Failed to assign reviewers. Please try again later.' unless matched_topics&lt;br /&gt;
&lt;br /&gt;
    matched_topics&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== bidding_algorithm_service ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# frozen_string_literal: true&lt;br /&gt;
&lt;br /&gt;
# The `BiddingAlgorithmService` run the bid assignment algorithm&lt;br /&gt;
# Sends student IDs, topic IDs, student preferences, and timestamps to the web service&lt;br /&gt;
# The web service returns the matched assignments in the JSON response body&lt;br /&gt;
class BiddingAlgorithmService&lt;br /&gt;
  SERVICE_URL = 'http://app-csc517.herokuapp.com/match_topics'.freeze&lt;br /&gt;
&lt;br /&gt;
  # MOCK_DATA is based on the documentation detailing how the webservice behaves.&lt;br /&gt;
  # Reference: https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2020_-_E2085._Allow_reviewers_to_bid_on_what_to_review#Webservice&lt;br /&gt;
  MOCK_DATA = {&lt;br /&gt;
    36239 =&amp;gt; [3970, 3972, 3975],&lt;br /&gt;
    36240 =&amp;gt; [3973, 3974, 3972],&lt;br /&gt;
    36241 =&amp;gt; [3969, 3971, 3972],&lt;br /&gt;
    36242 =&amp;gt; [3969, 3971, 3973],&lt;br /&gt;
    36243 =&amp;gt; [3969, 3970, 3971]&lt;br /&gt;
  }.freeze&lt;br /&gt;
&lt;br /&gt;
  def initialize(bidding_data)&lt;br /&gt;
    @bidding_data = bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def run&lt;br /&gt;
    return MOCK_DATA if Rails.application.config.use_mock_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
    perform_request&lt;br /&gt;
  rescue RestClient::ExceptionWithResponse, JSON::ParserError&lt;br /&gt;
    false&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def perform_request&lt;br /&gt;
    response = RestClient.post(&lt;br /&gt;
      SERVICE_URL,&lt;br /&gt;
      @bidding_data.to_json,&lt;br /&gt;
      content_type: :json,&lt;br /&gt;
      accept: :json&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    validate_response(response)&lt;br /&gt;
    JSON.parse(response.body)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def validate_response(response)&lt;br /&gt;
    raise 'Invalid response format' unless response.headers[:content_type] &amp;amp;&amp;amp; response.headers[:content_type].include?('application/json')&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-3-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Resolve Functionality Issues for Non Bidders ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Currently it doesn't work if some student does not bid. In this case, algorithm needs to be fixed to ignore anyone who didn’t bid, and let them choose from what’s left over.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-1.-reviewbidscontroller-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to do several things within this controller. We will set a default state for non-bidders that we can point to to give them options to select topics to review from the leftovers. We will consolidate &amp;lt;code&amp;gt;@sign_up_topics&amp;lt;/code&amp;gt; to resolve the DRY violation. Lastly, we will remove business logic from the show method and push it to a new helper to conform to the SRP.&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBidsController &amp;lt; ApplicationController&lt;br /&gt;
  require 'json'&lt;br /&gt;
  require 'uri'&lt;br /&gt;
  require 'net/http'&lt;br /&gt;
  require 'rest_client'&lt;br /&gt;
&lt;br /&gt;
  # Additional method&lt;br /&gt;
&lt;br /&gt;
  # Provides variables for review bidding page&lt;br /&gt;
  def show&lt;br /&gt;
    @participant = AssignmentParticipant.find(params[:id].to_i)&lt;br /&gt;
    @assignment = @participant.assignment&lt;br /&gt;
    @sign_up_topics = SignUpTopic.where(assignment_id: @assignment.id, private_to: nil)&lt;br /&gt;
    my_topic = SignedUpTeam.topic_id(@participant.parent_id, @participant.user_id)&lt;br /&gt;
    @sign_up_topics -= SignUpTopic.where(assignment_id: @assignment.id, id: my_topic)&lt;br /&gt;
    @num_participants = AssignmentParticipant.where(parent_id: @assignment.id).count&lt;br /&gt;
    @selected_topics = nil # This is used to list the topics assigned to review (i.e., select == assigned)&lt;br /&gt;
    @bids = ReviewBid.where(participant_id: @participant, assignment_id: @assignment.id)&lt;br /&gt;
    signed_up_topics = []&lt;br /&gt;
    &lt;br /&gt;
    @bids.each do |bid|&lt;br /&gt;
      sign_up_topic = SignUpTopic.find_by(id: bid.signuptopic_id)&lt;br /&gt;
      signed_up_topics &amp;lt;&amp;lt; sign_up_topic if sign_up_topic&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    signed_up_topics &amp;amp;= @sign_up_topics&lt;br /&gt;
    @sign_up_topics -= signed_up_topics&lt;br /&gt;
    @bids = signed_up_topics&lt;br /&gt;
    @num_of_topics = @sign_up_topics.size&lt;br /&gt;
    @assigned_review_maps = []&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: @assignment.id, reviewer_id: @participant.id).each do |review_map|&lt;br /&gt;
      @assigned_review_maps &amp;lt;&amp;lt; review_map&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Explicitly render view since it's in the sign up sheet views&lt;br /&gt;
    render 'sign_up_sheet/review_bids_show'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Additional methods&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-2.-reviewbidshelper-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
 # Assigns bidding topics to reviewers&lt;br /&gt;
  def assign_bidding&lt;br /&gt;
      assignment = validate_assignment(params[:assignment_id])&lt;br /&gt;
&lt;br /&gt;
      reviewers = validate_reviewers(assignment.id)&lt;br /&gt;
      reviewer_ids = reviewers.map(&amp;amp;:id)&lt;br /&gt;
&lt;br /&gt;
      matched_topics = run_bidding_algorithm&lt;br /&gt;
&lt;br /&gt;
      if matched_topics.blank?&lt;br /&gt;
        flash[:alert] = 'Topic or assignment is missing'&lt;br /&gt;
        redirect_back fallback_location: root_path &amp;amp;&amp;amp; return&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      leftover_topics = find_leftover_topics(assignment.id, matched_topics)&lt;br /&gt;
      assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
&lt;br /&gt;
      ReviewBid.new.assign_review_topics(matched_topics)&lt;br /&gt;
      assignment.update!(can_choose_topic_to_review: false)&lt;br /&gt;
&lt;br /&gt;
      flash[:notice] = 'Reviewers were successfully assigned to topics.'&lt;br /&gt;
      redirect_back fallback_location: root_path&lt;br /&gt;
    rescue ArgumentError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ArgumentError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: e.message&lt;br /&gt;
    rescue ActiveRecord::RecordInvalid =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::RecordInvalid: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue ActiveRecord::ActiveRecordError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;ActiveRecord::ActiveRecordError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers due to database error. Please try again later.'&lt;br /&gt;
    rescue StandardError =&amp;gt; e&lt;br /&gt;
      Rails.logger.error &amp;quot;StandardError: #{e.message}&amp;quot;&lt;br /&gt;
      redirect_back fallback_location: root_path, alert: 'Failed to assign reviewers. Please try again later.'&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  #service methods here ...&lt;br /&gt;
&lt;br /&gt;
  def find_leftover_topics(assignment_id, matched_topics)&lt;br /&gt;
    all_topic_ids = SignUpTopic.where(assignment_id: assignment_id).pluck(:id)&lt;br /&gt;
    assigned_topic_ids = matched_topics.map { |match| match[:topic_id] }&lt;br /&gt;
&lt;br /&gt;
    # Calculate leftover topics&lt;br /&gt;
    all_topic_ids - assigned_topic_ids&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def assign_leftover_topics(reviewer_ids, matched_topics, leftover_topics)&lt;br /&gt;
    return if leftover_topics.blank?&lt;br /&gt;
&lt;br /&gt;
    # Find non-bidders by excluding already matched reviewers&lt;br /&gt;
    non_bidders = reviewer_ids - matched_topics.keys&lt;br /&gt;
&lt;br /&gt;
    # Assign leftover topics to non-bidders in a round-robin fashion&lt;br /&gt;
    non_bidders.each_with_index do |reviewer_id, index|&lt;br /&gt;
      topic_id = leftover_topics[index % leftover_topics.length]&lt;br /&gt;
      ReviewBid.create(priority: 1, signuptopic_id: topic_id, participant_id: reviewer_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-4-3.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirm Functionality for Single-person Teams ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Make sure your code works on individual assignments, i.e., assignments where participants sign up for topics instead of teams. In this case, a team is created for each participant when they sign up. So the code should work for assignments to which either individuals or teams submit.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We plan to make an update to focus on topic rather than team or have it where teams are updated based on topic id.We will probably utilize sign up topic or a similar object to accomplish this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-5-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bid Status Messaging ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| To make the functionality more intuitive, include a message to say how many students are eligible to submit bids, how many have submitted their bids, when the deadline for submitting bids is.&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
The logic for these messages will go into the helper file. It is effectively the same reasoning as our migration of other business/functionality code from the controller to the helper to conform to the SRP. It will go hand in hand with the SignUpTopic portion of the code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-6-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Refactor &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| Why are the methods in review_bid.rb class methods? Can we change them to instance methods or move it to helpers?&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-1.-reviewbid-model-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; Model Updates ====&lt;br /&gt;
Since the current class methods are tied to &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model instances, we will change them to instance methods. There is no need to move them to helpers.&lt;br /&gt;
&lt;br /&gt;
===== Review Bid Model Flow Before Refactoring =====&lt;br /&gt;
This sequence diagram describes how the &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; interacts with the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model in assigning review topics to reviewers according to their bids. Once the &amp;lt;code&amp;gt;assign_bidding&amp;lt;/code&amp;gt; action is initiated by the user, the controller gathers the IDs of reviewers and calls &amp;lt;code&amp;gt;ReviewBid.bidding_data&amp;lt;/code&amp;gt; to gather bidding information. That is sent out to an external bidding algorithm, and the matched topics that come back are used by &amp;lt;code&amp;gt;ReviewBid.assign_review_topics&amp;lt;/code&amp;gt; to create &amp;lt;code&amp;gt;ReviewResponseMap&amp;lt;/code&amp;gt; entries, assigning topics to reviewers for the assignment.&lt;br /&gt;
&lt;br /&gt;
[[File:Uml sequence 2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
===== Current Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
  belongs_to :assignment, class_name: 'Assignment'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  def self.bidding_data(assignment_id, reviewer_ids)&lt;br /&gt;
    # create basic hash and set basic hash data&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'users' =&amp;gt; {}, 'max_accepted_proposals' =&amp;gt; [] }&lt;br /&gt;
    bidding_data['tid'] = SignUpTopic.where(assignment_id: assignment_id).ids&lt;br /&gt;
    bidding_data['max_accepted_proposals'] = Assignment.where(id: assignment_id).pluck(:num_reviews_allowed).first&lt;br /&gt;
&lt;br /&gt;
    # loop through reviewer_ids to get reviewer specific bidding data&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      bidding_data['users'][reviewer_id] = reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # assigns topics to reviews as matched by the webservice algorithm&lt;br /&gt;
  def self.assign_review_topics(assignment_id, reviewer_ids, matched_topics, _min_num_reviews = 2)&lt;br /&gt;
    # if review response map already created, delete it&lt;br /&gt;
    if ReviewResponseMap.where(reviewed_object_id: assignment_id)&lt;br /&gt;
      ReviewResponseMap.where(reviewed_object_id: assignment_id).destroy_all&lt;br /&gt;
    end&lt;br /&gt;
    # loop through reviewer_ids to assign reviews to each reviewer&lt;br /&gt;
    reviewer_ids.each do |reviewer_id|&lt;br /&gt;
      topics_to_assign = matched_topics[reviewer_id.to_s]&lt;br /&gt;
      topics_to_assign.each do |topic|&lt;br /&gt;
        assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method to assign a single topic to a reviewer&lt;br /&gt;
  def self.assign_topic_to_reviewer(assignment_id, reviewer_id, topic)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic).pluck(:team_id).first&lt;br /&gt;
    team_to_review.nil? ? [] : ReviewResponseMap.create(reviewed_object_id: assignment_id, reviewer_id: reviewer_id, reviewee_id: team_to_review, type: 'ReviewResponseMap')&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # method for getting individual reviewer_ids bidding data&lt;br /&gt;
  # returns user's bidding data hash&lt;br /&gt;
  def self.reviewer_bidding_data(reviewer_id, assignment_id)&lt;br /&gt;
    reviewer_user_id = AssignmentParticipant.find(reviewer_id).user_id&lt;br /&gt;
    self_topic = SignedUpTeam.topic_id(assignment_id, reviewer_user_id)&lt;br /&gt;
    bidding_data = { 'tid' =&amp;gt; [], 'otid' =&amp;gt; self_topic, 'priority' =&amp;gt; [], 'time' =&amp;gt; [] }&lt;br /&gt;
    bids = ReviewBid.where(participant_id: reviewer_id)&lt;br /&gt;
&lt;br /&gt;
    # loop through each bid for a topic to get specific data&lt;br /&gt;
    bids.each do |bid|&lt;br /&gt;
      bidding_data['tid'] &amp;lt;&amp;lt; bid.signuptopic_id&lt;br /&gt;
      bidding_data['priority'] &amp;lt;&amp;lt; bid.priority&lt;br /&gt;
      bidding_data['time'] &amp;lt;&amp;lt; bid.updated_at&lt;br /&gt;
    end&lt;br /&gt;
    bidding_data&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Updated Implementation =====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class ReviewBid &amp;lt; ApplicationRecord&lt;br /&gt;
  belongs_to :topic, class_name: 'SignUpTopic'&lt;br /&gt;
  belongs_to :participant, class_name: 'Participant'&lt;br /&gt;
&lt;br /&gt;
  # method to get bidding data&lt;br /&gt;
  # returns the bidding data needed for the assigning algorithm&lt;br /&gt;
  # student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed&lt;br /&gt;
&lt;br /&gt;
  # Instance method to get bidding data for this review bid&lt;br /&gt;
  def bidding_data&lt;br /&gt;
    {&lt;br /&gt;
      'tid' =&amp;gt; topic_ids,&lt;br /&gt;
      'otid' =&amp;gt; self_topic,&lt;br /&gt;
      'priority' =&amp;gt; priorities,&lt;br /&gt;
      'time' =&amp;gt; updated_times&lt;br /&gt;
    }&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # bid object to limit db touches for similar data acquisitions&lt;br /&gt;
  def bids&lt;br /&gt;
    @bids ||= ReviewBid.where(participant_id: participant_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def topic_ids&lt;br /&gt;
    bids.pluck(:signuptopic_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def priorities&lt;br /&gt;
    bids.pluck(:priority)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def updated_times&lt;br /&gt;
    bids.pluck(:updated_at)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self_topic&lt;br /&gt;
    return nil unless topic &amp;amp;&amp;amp; topic.assignment_id&lt;br /&gt;
&lt;br /&gt;
    SignedUpTeam.topic_id(topic.assignment_id, participant.user_id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign topics to reviews&lt;br /&gt;
  def assign_review_topics(matched_topics)&lt;br /&gt;
    validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.where(reviewed_object_id: topic.assignment_id).destroy_all&lt;br /&gt;
&lt;br /&gt;
    matched_topics.each do |reviewer_id, topics|&lt;br /&gt;
      Array(topics).each { |topic_id| assign_topic_to_reviewer(reviewer_id, topic_id) }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Assign a single topic to a reviewer&lt;br /&gt;
  def assign_topic_to_reviewer(reviewer_id, topic_id)&lt;br /&gt;
    team_to_review = SignedUpTeam.where(topic_id: topic_id).pluck(:team_id).first&lt;br /&gt;
    return if team_to_review.nil?&lt;br /&gt;
&lt;br /&gt;
    ReviewResponseMap.create(&lt;br /&gt;
      reviewed_object_id: topic.assignment_id,&lt;br /&gt;
      reviewer_id: reviewer_id,&lt;br /&gt;
      reviewee_id: team_to_review,&lt;br /&gt;
      type: 'ReviewResponseMap'&lt;br /&gt;
    )&lt;br /&gt;
    # error handling&lt;br /&gt;
  rescue StandardError =&amp;gt; e&lt;br /&gt;
    Rails.logger.error(&amp;quot;Failed to assign topic #{topic_id} to reviewer #{reviewer_id}: #{e.message}&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  def validate_topics_and_assignment!(matched_topics)&lt;br /&gt;
    raise ArgumentError, 'Topic or assignment is missing' if topic.nil? || topic.assignment_id.nil?&lt;br /&gt;
    raise ArgumentError, 'Matched topics must be a Hash' unless matched_topics.is_a?(Hash)&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
Once we change them to instance methods, we’ll need to modify their use in the code to instantiate the object first.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;Placeholder for implementation screenshots (before and after)&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-3.-schema.db-updates&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;Schema.db&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
We also need to remove the foreign key reference to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; model since the &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; model already has access to &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;, and that is the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; we should be referencing. This will also involve modifying the database to remove the foreign key reference to the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Steps required:&lt;br /&gt;
&lt;br /&gt;
# Review the records in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table in the database to see if there are any records and if those records have values for &amp;lt;code&amp;gt;assignment_id&amp;lt;/code&amp;gt;&lt;br /&gt;
# Create a migration file using the command: &amp;lt;code&amp;gt;rails generate migration RemoveAssignmentFromReviewBids&amp;lt;/code&amp;gt;&lt;br /&gt;
# In the generated file, indicate the change that needs to be made using the change method and migration columns &amp;lt;code&amp;gt;remove_foreign_key&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;remove_column&amp;lt;/code&amp;gt;. Migration should be straightforward since there is no cascading behavior (i.e., &amp;lt;code&amp;gt;dependent:destroy&amp;lt;/code&amp;gt;) to worry about.&lt;br /&gt;
# Once comfortable with the updates to the generated file, run the migration using the &amp;lt;code&amp;gt;rails db:migrate&amp;lt;/code&amp;gt; command.&lt;br /&gt;
# Run a query in the DB to ensure no orphaned records are in the &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; table after the updates.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;Placeholder for implementation screenshots (before and after)&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-7-4.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implement Edge Case Testing ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem&lt;br /&gt;
! Design Goals&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot;| In the previous implementation wiki, there are edge cases which are not exhaustively tested. Should test those edge cases thoroughly and add more edge case testing - link&lt;br /&gt;
| '''Code Readability and Maintainability:'''&lt;br /&gt;
&lt;br /&gt;
Refactor and simplify code, especially in places where the code violates the DRY principle, to reduce complexity and make the code base more straightforward to understand and maintain.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''System Stability:'''&lt;br /&gt;
&lt;br /&gt;
Debug the existing bidding algorithm and fine-tune the assignment logic. Also, the existing functionality related to the reviewer bidding feature should work and be enhanced if required so that it does not fail in circumstances such as when a student does not bid.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;section-2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;ReviewBidsHelper&amp;lt;/code&amp;gt; Updates ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span class=&amp;quot;mark&amp;quot;&amp;gt;TODO&amp;lt;/span&amp;gt;:We just got initial test results back. We still need to figure out why tests are failing and what may be slipping through the cracks.[[File:Test expertiza.png|411x459px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vi-8-2.-design-patternsprinciples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design Patterns and Principles ===&lt;br /&gt;
Below are the design patterns and principles we plan to use to address the problem statements outlined in the project requirements:&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
** Controller Responsibility&lt;br /&gt;
*** To address the issue of authentication logic residing in &amp;lt;code&amp;gt;ReviewBidsController&amp;lt;/code&amp;gt;, we will refactor the code to ensure the controller solely handles request and response flows.&lt;br /&gt;
*** Authentication logic will be moved to dedicated methods in &amp;lt;code&amp;gt;AuthorizationHelper&amp;lt;/code&amp;gt;, making the codebase easier to maintain and test.&lt;br /&gt;
** View Simplification&lt;br /&gt;
*** Currently, the &amp;lt;code&amp;gt;review_bids_others_work&amp;lt;/code&amp;gt; view contains excessive business logic, violating SRP.&lt;br /&gt;
*** We will move this logic into helper methods or models, ensuring each component has a single responsibility and improving code readability.&lt;br /&gt;
&lt;br /&gt;
* Separation of Concerns (SoC)&lt;br /&gt;
** By relocating authorization logic out of the controller, we separate authentication from request handling.&lt;br /&gt;
** Adhering to SoC enhances readability and maintainability by allowing each part of the application to focus on its specific role.&lt;br /&gt;
* Don't Repeat Yourself (DRY)&lt;br /&gt;
** We will inspect the existing implementation for code duplication, particularly in the controller actions.&lt;br /&gt;
** Any repetitive code will be refactored or removed to reduce redundancy and improve clarity.&lt;br /&gt;
&lt;br /&gt;
* Model-View-Controller (MVC) Adherence&lt;br /&gt;
** Moving business logic from the view aligns with MVC best practices.&lt;br /&gt;
** By ensuring the view only presents data without processing it, we maintain clear boundaries between the model, view, and controller layers.&lt;br /&gt;
&lt;br /&gt;
* Dependency Inversion Principle (DIP)&lt;br /&gt;
** We plan to update class methods to instance methods, allowing the controller to depend on abstractions rather than concrete implementations.&lt;br /&gt;
** This change leverages Ruby's duck typing, providing flexibility and making the code easier to maintain and test.&lt;br /&gt;
&lt;br /&gt;
* Factory Pattern&lt;br /&gt;
** We will implement a factory pattern for the common processing of &amp;lt;code&amp;gt;ReviewBid&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpBid&amp;lt;/code&amp;gt;, encapsulating object creation and promoting code reuse.&lt;br /&gt;
&lt;br /&gt;
* Law of Demeter (Principle of Least Knowledge)&lt;br /&gt;
** We'll delegate interactions appropriately to prevent tightly coupled code, especially when dealing with topics and teams.&lt;br /&gt;
** Clear and intentional naming during refactoring will enhance code clarity and maintainability.&lt;br /&gt;
&lt;br /&gt;
* Adapter Pattern&lt;br /&gt;
** To add messaging to bid states for users, we'll use the adapter pattern to allow different components to work together without modifying their existing code.&lt;br /&gt;
&lt;br /&gt;
== Files Modified/Added ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-1.-controllers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
review_bids_controller.rb&lt;br /&gt;
&lt;br /&gt;
signup_sheet_controller.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-2.-models&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
review_bid.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;vii-3.-helpers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
&lt;br /&gt;
authorization_helper.rb&lt;br /&gt;
&lt;br /&gt;
Review_bids_helper.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;viii.-test-plan&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
The Excel spreadsheet below details our test plan for testing the code we changed or created to ensure code coverage. The process involved reviewing the code and determining all the possible scenarios that needed to be covered based on what the method was doing, then creating test cases accordingly.&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/spreadsheets/d/1iHzOdSwlTtxOtf5VsNdwOaCP8NzYMmiJ/edit?usp=drive_link Open Test Matrix Spreadsheet]&lt;br /&gt;
&lt;br /&gt;
== Test Coverage ==&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;screenshot of SimpleCov coverage analysis&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x.-team&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-1.-mentor&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Ed Gehringer&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;x-2.-members&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Members ===&lt;br /&gt;
&lt;br /&gt;
* Gavin Teague&lt;br /&gt;
* Janice Uwujaren&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xi.-links&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
Previous Wiki: [https://wiki.expertiza.ncsu.edu/index.php/CSC/ECE_517_Fall_2021_-_E2151._Allow_reviewers_to_bid_on_what_to_review &amp;lt;u&amp;gt;Link&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;xii.-references&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
[https://docs.google.com/document/d/1LHVacylQmB15I6VcYUA5nuC7GRRU9szHzZhjlPcE-PA/edit?tab=t.0#heading=h.u0oycoj6e0ev &amp;lt;u&amp;gt;Project Instructions&amp;lt;/u&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;appendix-a-uml-class-diagram-plantuml-code-for-review-bids&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Juwujar</name></author>
	</entry>
</feed>