Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

E2151. Allow reviewers to bid on what to review #2150

Merged
merged 60 commits into from Jan 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
05399af
Add files and changes from prior review bid implementation
Aeront39 Nov 19, 2021
2444fb2
modify database migrations
Aeront39 Nov 19, 2021
9de8799
Modify names of files for review bid views
Aeront39 Nov 22, 2021
89a4bde
Delete all views and partials explicitly for review_bids
Aeront39 Nov 22, 2021
35a4971
Revert "Delete all views and partials explicitly for review_bids"
Aeront39 Nov 22, 2021
4314983
Create new shared partial used for table line of both reviews and top…
Aeront39 Nov 22, 2021
0495b25
Remove unnecessary portion of base_table_name
Aeront39 Nov 22, 2021
2097d0d
Modify views for review bidding to render as expected
Aeront39 Nov 22, 2021
567a212
Modify view files for review bids to render properly
Aeront39 Nov 22, 2021
e92c4d0
Merge https://github.com/WeiRui-Wang/expertiza into beta
Aeront39 Nov 22, 2021
b670f79
Delete unnecessary view components for review bidding
Aeront39 Nov 22, 2021
534ff34
Merge https://github.com/WeiRui-Wang/expertiza into beta
Aeront39 Nov 22, 2021
03ea8fe
Fix incorrect render of partial in review_bid_table_line view
Aeront39 Nov 22, 2021
bcf8a2e
Merge https://github.com/WeiRui-Wang/expertiza into beta
Aeront39 Nov 22, 2021
a89ad51
Update url for algorithm and test cases
Aeront39 Nov 22, 2021
e6edf75
Merge https://github.com/WeiRui-Wang/expertiza into beta
Aeront39 Nov 23, 2021
81eafe4
Revert "Delete all views and partials explicitly for review_bids"
Aeront39 Nov 22, 2021
6b2b37a
Revert "Revert "Delete all views and partials explicitly for review_b…
Aeront39 Nov 23, 2021
ad0b5a8
Temporary modify to review bids controller to fix apparent bug, needs…
Aeront39 Nov 23, 2021
2979529
Merge branch 'beta-back' into beta
Aeront39 Nov 23, 2021
6c7c1d1
Merge branch 'beta' of https://github.com/WeiRui-Wang/expertiza into …
Aeront39 Nov 23, 2021
2684c45
Add debugging when running algorithm to help figuring out
Aeront39 Nov 23, 2021
fd74ee5
Remove troublesome local variable from controller to allow reviews to…
Aeront39 Nov 24, 2021
14186da
Fix merge issues
Aeront39 Nov 24, 2021
239c521
Revise necessity to be on a team to see reviews
Aeront39 Nov 24, 2021
34803ad
Merge branch 'beta-back' into beta
Aeront39 Nov 24, 2021
58cb3a9
Modify beta to have same fixes implemented in beta-back
Aeront39 Nov 24, 2021
06fea7e
Merge branch 'beta' of https://github.com/WeiRui-Wang/expertiza into …
Aeront39 Nov 24, 2021
4867f6c
Replace bidding debug print with proper functionality, can now create…
Aeront39 Nov 24, 2021
37e6770
Merge branch 'expertiza:beta' into beta
WeiRui-Wang Nov 28, 2021
ef7eb34
Soft rollback to address reported testing issue.
Nov 28, 2021
73f87d3
Merge remote-tracking branch 'origin/beta' into beta
Nov 28, 2021
011114b
Update review_bids_controller to address build issue.
Nov 28, 2021
809f51f
Move review bids views into sign up sheet views.
Aeront39 Nov 28, 2021
966ad96
Adding reviewer_self_topic function call to review big controller
aluthra37 Nov 29, 2021
8bd048a
Update review_bids_controller to address rollback issue.
Nov 29, 2021
f41e549
Update related file to address rollback issue.
Nov 29, 2021
f68ba41
Resolved a long-term syntax error.
Nov 29, 2021
efb0d82
Rollback to version before syntax patch.
Nov 29, 2021
61fb20b
Update controller to use self.topic_id method
aluthra37 Nov 29, 2021
ebf3151
Remove DRY violating method from review bid model
Aeront39 Nov 29, 2021
73a8729
Updating reviewer_bidding_data to use topic_id method
aluthra37 Nov 29, 2021
66eecf4
Remove unnecessary 'create' method from review bids controller
Aeront39 Nov 29, 2021
9b35988
Rename confusing reviewer_id variable in review_bid model
Aeront39 Nov 29, 2021
352591d
Add proper finding of reviewer id for finding original topic
Aeront39 Nov 29, 2021
3534090
Change website url back to proper form
Aeront39 Nov 29, 2021
3f743fe
Remove unhelpful and improper test cases, remove spec test for remove…
Aeront39 Nov 30, 2021
200a058
Remove requiring of rspec gem inside of tests
Aeront39 Nov 30, 2021
d3d97cb
Re-make prior changes that were reverted, including removing unnecces…
Aeront39 Nov 30, 2021
1c74caa
rename method to get current assignment stage
Aeront39 Nov 30, 2021
15bba71
remove DRY violating files no longer needed in review bids view
Aeront39 Nov 30, 2021
e2a11b1
modify details of some views to work properly
Aeront39 Dec 1, 2021
f5a18d7
resolve merge conflicts for review bid controller url
Aeront39 Dec 1, 2021
3001803
Merge branch 'beta' of https://github.com/WeiRui-Wang/expertiza into …
Aeront39 Nov 29, 2021
6cf9356
Revise code to DRY out finding a reviewer's own topic
Aeront39 Dec 1, 2021
aa7cb91
Remove commented out code
Aeront39 Nov 29, 2021
818c4b8
Modify others_work to work with newly renamed methods for getting the…
Aeront39 Dec 5, 2021
60fb36b
Remove calculation of unneccesary variable that led to DRY violation.
Aeront39 Dec 6, 2021
283f58f
Finalized on the DRY refactoring review_bids_controller.
Nov 28, 2021
e45e3d7
Merge branch 'beta' of https://github.com/expertiza/expertiza into pr…
johnbumgardner Jan 26, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
125 changes: 125 additions & 0 deletions app/controllers/review_bids_controller.rb
@@ -0,0 +1,125 @@
class ReviewBidsController < ApplicationController
require "json"
require "uri"
require "net/http"
require "rest_client"

#action allowed function checks the action allowed based on the user working
def action_allowed?
case params[:action]
when 'show', 'set_priority', 'index'
['Instructor',
'Teaching Assistant',
'Administrator',
'Super-Administrator',
'Student'].include? current_role_name and
((%w[list].include? action_name) ? are_needed_authorizations_present?(params[:id], "participant" "reader", "submitter", "reviewer") : true)
else
['Instructor',
'Teaching Assistant',
'Administrator',
'Super-Administrator'].include? current_role_name
end
end

# provides variables for reviewing page located at views/review_bids/others_work.html.erb
def index
@participant = AssignmentParticipant.find(params[:id])
return unless current_user_id?(@participant.user_id)
@assignment = @participant.assignment
@review_mappings = ReviewResponseMap.where(reviewer_id: @participant.id)

# Finding how many reviews have been completed
@num_reviews_completed = 0
@review_mappings.each do |map|
@num_reviews_completed += 1 if !map.response.empty? && map.response.last.is_submitted
end

# render view for completing reviews after review bidding has been completed
render 'sign_up_sheet/review_bids_others_work'
end

# provides vaiables for review bidding page
def show
@participant = AssignmentParticipant.find(params[:id].to_i)
@assignment = @participant.assignment
@sign_up_topics = SignUpTopic.where(assignment_id: @assignment.id, private_to: nil)
my_topic = SignedUpTeam.topic_id(@participant.parent_id, @participant.user_id)
@sign_up_topics -= SignUpTopic.where(assignment_id: @assignment.id, id: my_topic)
@num_participants = AssignmentParticipant.where(parent_id: @assignment.id).count
@selected_topics = nil #this is used to list the topics assigned to review. (ie select == assigned i believe)
@bids = ReviewBid.where(participant_id:@participant,assignment_id:@assignment.id)
signed_up_topics = []
@bids.each do |bid|
sign_up_topic = SignUpTopic.find_by(id: bid.signuptopic_id)
signed_up_topics << sign_up_topic if sign_up_topic
end
signed_up_topics &= @sign_up_topics
@sign_up_topics -= signed_up_topics
@bids = signed_up_topics
@num_of_topics = @sign_up_topics.size
@assigned_review_maps = []
selected_topics = []
ReviewResponseMap.where({:reviewed_object_id => @assignment.id, :reviewer_id => @participant.id}).each do |review_map|
@assigned_review_maps << review_map
end

# explicitly render view since it's in the sign up sheet views
render 'sign_up_sheet/review_bids_show'
end

# function that assigns and updates priorities for review bids
def set_priority
if params[:topic].nil?
ReviewBid.where(participant_id: params[:id]).destroy_all
else
participant = AssignmentParticipant.find_by(id: params[:id])
assignment_id = SignUpTopic.find(params[:topic].first).assignment.id
@bids = ReviewBid.where(participant_id: params[:id])
signed_up_topics = ReviewBid.where(participant_id: params[:id]).map(&:signuptopic_id)
signed_up_topics -= params[:topic].map(&:to_i)
signed_up_topics.each do |topic|
ReviewBid.where(signuptopic_id: topic, participant_id: params[:id]).destroy_all
end
params[:topic].each_with_index do |topic_id, index|
bid_existence = ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id])
if bid_existence.empty?
ReviewBid.create(priority: index + 1, signuptopic_id: topic_id, participant_id: params[:id], assignment_id: assignment_id)
else
ReviewBid.where(signuptopic_id: topic_id, participant_id: params[:id]).update_all(priority: index + 1)
end
end
end
redirect_to action: 'show', assignment_id: params[:assignment_id], id: params[:id]
end

# assign bidding topics to reviewers
def assign_bidding
# sets parameters used for running bidding algorithm
assignment_id = params[:assignment_id].to_i
# list of reviewer id's from a specific assignment
reviewer_ids = AssignmentParticipant.where(parent_id: assignment_id).ids
bidding_data = ReviewBid.get_bidding_data(assignment_id, reviewer_ids)
matched_topics = run_bidding_algorithm(bidding_data)
ReviewBid.assign_review_topics(assignment_id, reviewer_ids, matched_topics)
Assignment.find(assignment_id).update(can_choose_topic_to_review: false) #turns off bidding for students
redirect_to :back

end

# call webserver for running assigning algorthim
# passing webserver: student_ids, topic_ids, student_preferences, time_stamps
# webserver returns:
# returns matched assignments as json body
def run_bidding_algorithm(bidding_data)
# begin
url = WEBSERVICE_CONFIG["review_bidding_webservice_url"] #won't work unless ENV variables are configured
url = 'http://app-csc517.herokuapp.com/match_topics' #hard coding for the time being
response = RestClient.post url, bidding_data.to_json, content_type: 'application/json', accept: :json
return JSON.parse(response.body)
rescue StandardError
return false
# end
end

end
5 changes: 5 additions & 0 deletions app/controllers/student_review_controller.rb
Expand Up @@ -54,5 +54,10 @@ def list
@all_assignments.each do |assignment|
@response_ids << assignment.response_id
end

# Redirect review bidding to the review bid controller if bidding enabled
if @assignment.review_choosing_algorithm == "Bidding"
redirect_to controller: 'review_bids', action: 'index', assignment_id: params[:assignment_id], id: params[:id]
end
end
end
32 changes: 32 additions & 0 deletions app/helpers/review_bids_helper.rb
@@ -0,0 +1,32 @@
module ReviewBidsHelper

#renders the topic row for the topics table
#in review_bids/show.html.erb
def get_intelligent_topic_row_review_bids(topic, selected_topics, num_participants)
row_html = ''
if selected_topics.present?
selected_topics.each do |selected_topic|
row_html = if selected_topic.topic_id == topic.id and !selected_topic.is_waitlisted
'<tr bgcolor="yellow">'
elsif selected_topic.topic_id == topic.id and selected_topic.is_waitlisted
'<tr bgcolor="lightgray">'
else
'<tr id="topic_' + topic.id.to_s + '">'
end
end
else
row_html = '<tr id="topic_' + topic.id.to_s + '" style="background-color:' + get_topic_bg_color_review_bids(topic, num_participants) + '">'
end
row_html.html_safe
end

#gets the background color with respect to number of participants and bid size
#in review_bids/show.html.erb
def get_topic_bg_color_review_bids(topic, num_participants)
num_bids = ReviewBid.where(signuptopic_id:topic.id).count.to_f
green = (400 * (1 - (Math.tanh(2 * (num_bids/num_participants.to_f) - 1) + 1) / 2)).to_i.to_s
red = (400 * (Math.tanh(2 * (num_bids/num_participants.to_f) - 1) + 1) / 2).to_i.to_s
'rgb(' + red + ',' + green + ',0)'
end

end
65 changes: 65 additions & 0 deletions app/models/review_bid.rb
@@ -0,0 +1,65 @@
class ReviewBid < ActiveRecord::Base
belongs_to :topic, class_name: 'SignUpTopic'
belongs_to :participant, class_name: 'Participant'
belongs_to :assignment, class_name: 'Assignment'


# method to get bidding data
# returns the bidding data needed for the assigning algorithm
# student_ids, topic_ids, student_preferences, topic_preferences, max reviews allowed
public
def self.get_bidding_data(assignment_id,reviewer_ids)
# create basic hash and set basic hash data
bidding_data = {'tid'=> [], 'users' => Hash.new, 'max_accepted_proposals' => []}
bidding_data['tid'] = SignUpTopic.where(assignment_id: assignment_id).ids
bidding_data['max_accepted_proposals'] = Assignment.where(id:assignment_id).pluck(:num_reviews_allowed).first

# loop through reviewer_ids to get reviewer specific bidding data
for reviewer_id in reviewer_ids do
bidding_data['users'][reviewer_id] = self.reviewer_bidding_data(reviewer_id,assignment_id)
end

return bidding_data
end

# assigns topics to reviews as matched by the webservice algorithm
def self.assign_review_topics(assignment_id,reviewer_ids,matched_topics,min_num_reviews=2)
# if review response map already created, delete it
if ReviewResponseMap.where(:reviewed_object_id => assignment_id)
ReviewResponseMap.where(:reviewed_object_id => assignment_id).destroy_all
end
# loop through reviewer_ids to assign reviews to each reviewer
reviewer_ids.each do |reviewer_id|
topics_to_assign = matched_topics[reviewer_id.to_s]
topics_to_assign.each do |topic|
assign_topic_to_reviewer(assignment_id,reviewer_id,topic)
end
end
end

# method to assign a single topic to a reviewer
def self.assign_topic_to_reviewer(assignment_id,reviewer_id,topic)
team_to_review = SignedUpTeam.where(topic_id: topic).pluck(:team_id).first
team_to_review.nil? ? [] : ReviewResponseMap.create({:reviewed_object_id => assignment_id, :reviewer_id => reviewer_id, :reviewee_id => team_to_review, :type => "ReviewResponseMap"})
end

# method for getting individual reviewer_ids bidding data
# returns user's bidding data hash
def self.reviewer_bidding_data(reviewer_id,assignment_id)
reviewer_user_id = AssignmentParticipant.find(reviewer_id).user_id
self_topic = SignedUpTeam.topic_id(assignment_id, reviewer_user_id)
bidding_data = {'tid'=> [], 'otid' => self_topic, 'priority' => [], 'time'=>[]}
bids = ReviewBid.where(participant_id: reviewer_id)

# loop through each bid for a topic to get specific data
for bid in bids do
bidding_data['tid'] << bid.signuptopic_id
bidding_data['priority'] << bid.priority
bidding_data['time'] << bid.updated_at
end

return bidding_data
end

end

13 changes: 13 additions & 0 deletions app/views/assignments/edit/_topics.html.erb
Expand Up @@ -20,6 +20,19 @@
<%= check_box_tag('assignment_form[assignment][can_choose_topic_to_review]', 'true', @assignment_form.assignment.can_choose_topic_to_review?)%>
<%= label_tag('assignment_form[assignment][can_choose_topic_to_review]', 'Allow reviewer to choose which topic to review?') %>

<br>
<% if @assignment_form.assignment.can_choose_topic_to_review? == true %>
<input name="assignment_form[assignment][review_choosing_algorithm]" type="hidden" value="Simple Choose"/>
<%= label_tag('assignment_form[assignment][review_choosing_algorithm]', 'Method for reviewer to choose which topic to review:') %>
<%= select('assignment_form[assignment]', 'review_choosing_algorithm',[['Simple Choose', 'Simple Choose'],['Bidding','Bidding']], {:selected => @assignment_form.assignment.review_choosing_algorithm})%>
<img src="/assets/info.png" title="This will dictact how reviewers will be able to choose topics to review:
Simple Choose allows reviews to choose topics on a first come first serve basis
Bidding allows reviewers to bid on topics to review"/>
<% if @assignment_form.assignment.review_choosing_algorithm == "Bidding" %>
<%= button_to 'Run Review Algorithm', :controller=>'review_bids', :action=>'assign_bidding', :assignment_id => @assignment_form.assignment.id %>
<% end %>
<% end %>

<br>
<input name="assignment_form[assignment][use_bookmark]" type="hidden" value="false"/>
<%= check_box_tag('assignment_form[assignment][use_bookmark]', 'true', @assignment_form.assignment.use_bookmark, {:onChange => 'useBookmarkChanged()'})%>
Expand Down
13 changes: 13 additions & 0 deletions app/views/sign_up_sheet/_review_bids_table_header.html.erb
@@ -0,0 +1,13 @@
<th width="5%">Topic #</th>
<th width="85%">Topic name(s)</th>

<% if ['Instructor', 'Teaching Assistant', 'Administrator', 'Super-Administrator'].include? current_user_role?.name %>
<th width="10%">Edit Topics</th>
<% end %>







45 changes: 45 additions & 0 deletions app/views/sign_up_sheet/_review_bids_table_line.html.erb
@@ -0,0 +1,45 @@
<script type="text/javascript">
function addQuestionnaireTableRowInTopics(questionnaire_type, questionnaire, questionnaire_options, round_no, topic_no) {
var table_name = "#topic_questionnaire_table" + topic_no + ">tbody>tr";
var questionnaire_table = jQuery(table_name);
var row_id = 'topic_questionnaire_table_' + topic_no + "_" + questionnaire_type;
var i;
var html = '<tr id=' + row_id + '>';
var t_id = "ti_id" + topic_no

html += '<input name="assignment_form[topic_questionnaire][][topic_id]" type="hidden" value="';
html += '<%= topic.id %>';
html += '">';
//Rubric name
if (round_no == null) {
html += '<td><label for=t_id>' + questionnaire.display_type + ':</label></td>';
} else {
html += '<td><label for=t_id>' + questionnaire.display_type + ' Round '+ round_no + ':</label></td>';
}
//Rubric options
html += '<td align="center">' +
'<select id=t_id name="assignment_form[topic_questionnaire][][questionnaire_id]" style="width:300px" class="form-control">' +
'<option value="">--Default rubric--</option>';
for (i = 0; i < questionnaire_options.length; i++) {
html += '<option value="' + questionnaire_options[i][1] + '">' + questionnaire_options[i][0] + '</option>';
}
html += '</select></td>';
if (round_no == null) {
html += '<td align="center"><input name="assignment_form[topic_questionnaire][][used_in_round]" style="width:30px" type="hidden" value=""' + '"> </td>';
} else {
html += '<td align="center"><input name="assignment_form[topic_questionnaire][][used_in_round]" style="width:30px" type="hidden" value="' + round_no + '"> </td>';
}
questionnaire_table.last().after(html);
jQuery('#t_id').val(questionnaire.id).attr('id', '');
}
</script>

<input name="hidden_topic_id" type="hidden" value=<%=topic.id %> >

<td><%= topic.topic_identifier %></td>
<td><%= render :partial => 'sign_up_sheet/topic_names', :locals => {:i => i, :topic=>topic} %></td>
<% if ['Instructor', 'Teaching Assistant', 'Administrator', 'Super-Administrator'].include? current_user_role?.name %>
<% if @assignment.current_stage_name(@topic_id) != 'Finished' %>
<td align="center"> <%= render :partial => 'sign_up_sheet/all_actions', :locals => {:i=>i,:topic=>topic, :is_suggested_topic=>is_suggested_topic ||= false} %> </td>
<% end %>
<% end %>