forked from expertiza/expertiza
-
Notifications
You must be signed in to change notification settings - Fork 1
/
review_response_map.rb
274 lines (253 loc) · 12.7 KB
/
review_response_map.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
class ReviewResponseMap < ResponseMap
belongs_to :reviewee, class_name: 'Team', foreign_key: 'reviewee_id', inverse_of: false
belongs_to :contributor, class_name: 'Team', foreign_key: 'reviewee_id', inverse_of: false
belongs_to :assignment, class_name: 'Assignment', foreign_key: 'reviewed_object_id', inverse_of: false
# Added for E1973:
# http://wiki.expertiza.ncsu.edu/index.php/CSC/ECE_517_Fall_2019_-_Project_E1973._Team_Based_Reviewing
# ReviewResponseMap was created in so many places, I thought it best to add this here as a catch-all
def after_initialize
# If an assignment supports team reviews, it is marked in each mapping
reviewer_is_team = assignment.reviewer_is_team
end
# Find a review questionnaire associated with this review response map's assignment
def questionnaire(round_number = nil, topic_id = nil)
Questionnaire.find(self.assignment.review_questionnaire_id(round_number, topic_id))
end
def get_title
"Review"
end
def delete(_force = nil)
fmaps = FeedbackResponseMap.where(reviewed_object_id: self.response.response_id)
fmaps.each(&:destroy)
maps = MetareviewResponseMap.where(reviewed_object_id: self.id)
maps.each(&:destroy)
self.destroy
end
def self.export_fields(_options)
["contributor", "reviewed by"]
end
def self.export(csv, parent_id, _options)
mappings = where(reviewed_object_id: parent_id).to_a
mappings.sort! {|a, b| a.reviewee.name <=> b.reviewee.name }
mappings.each do |map|
csv << [
map.reviewee.name,
map.reviewer.name
]
end
end
def self.import(row_hash, _session, assignment_id)
reviewee_user_name = row_hash[:reviewee].to_s
reviewee_user = User.find_by(name: reviewee_user_name)
raise ArgumentError, "Cannot find reviewee user." unless reviewee_user
reviewee_participant = AssignmentParticipant.find_by(user_id: reviewee_user.id, parent_id: assignment_id)
raise ArgumentError, "Reviewee user is not a participant in this assignment." unless reviewee_participant
reviewee_team = AssignmentTeam.team(reviewee_participant)
if reviewee_team.nil? # lazy team creation: if the reviewee does not have team, create one.
reviewee_team = AssignmentTeam.create(name: 'Team' + '_' + rand(1000).to_s,
parent_id: assignment_id, type: 'AssignmentTeam')
t_user = TeamsUser.create(team_id: reviewee_team.id, user_id: reviewee_user.id)
team_node = TeamNode.create(parent_id: assignment_id, node_object_id: reviewee_team.id)
TeamUserNode.create(parent_id: team_node.id, node_object_id: t_user.id)
end
row_hash[:reviewers].each do |reviewer|
reviewer_user_name = reviewer.to_s
reviewer_user = User.find_by(name: reviewer_user_name)
raise ArgumentError, "Cannot find reviewer user." unless reviewer_user
next if reviewer_user_name.empty?
reviewer_participant = AssignmentParticipant.find_by(user_id: reviewer_user.id, parent_id: assignment_id)
raise ArgumentError, "Reviewer user is not a participant in this assignment." unless reviewer_participant
ReviewResponseMap.find_or_create_by(reviewed_object_id: assignment_id,
reviewer_id: reviewer_participant.get_reviewer.id,
reviewee_id: reviewee_team.id,
calibrate_to: false)
end
end
def show_feedback(response)
return unless self.response.any? and response
map = FeedbackResponseMap.find_by(reviewed_object_id: response.id)
map.response.last.display_as_html if map and map.response.any?
end
def metareview_response_maps
responses = Response.where(map_id: self.id)
metareview_list = []
responses.each do |response|
metareview_response_maps = MetareviewResponseMap.where(reviewed_object_id: response.id)
metareview_response_maps.each {|metareview_response_map| metareview_list << metareview_response_map }
end
metareview_list
end
# return the responses for specified round, for varying rubric feature -Yang
def self.get_responses_for_team_round(team, round)
responses = []
if team.id
maps = ResponseMap.where(reviewee_id: team.id, type: "ReviewResponseMap")
maps.each do |map|
if map.response.any? and map.response.reject {|r| (r.round != round || !r.is_submitted) }.any?
responses << map.response.reject {|r| (r.round != round || !r.is_submitted) }.last
end
end
responses.sort! {|a, b| a.map.reviewer.fullname <=> b.map.reviewer.fullname }
end
responses
end
#E-1973 - returns the reviewer of the response, either a participant or a team
def get_reviewer
ReviewResponseMap.get_reviewer_with_id(assignment.id, reviewer_id)
end
# E-1973 - gets the reviewer of the response, given the assignment and the reviewer id
# the assignment is used to determine if the reviewer is a participant or a team
def self.get_reviewer_with_id(assignment_id, reviewer_id)
assignment = Assignment.find(assignment_id)
if assignment.reviewer_is_team
return AssignmentTeam.find(reviewer_id)
else
return AssignmentParticipant.find(reviewer_id)
end
end
# wrap lastest version of responses in each response map, together withe the questionnaire_id
# will be used to display the reviewer summary
def self.final_versions_from_reviewer(assignment_id, reviewer_id)
reviewer = ReviewResponseMap.get_reviewer_with_id(assignment_id, reviewer_id)
maps = ReviewResponseMap.where(reviewer_id: reviewer_id)
assignment = Assignment.find(reviewer.parent_id)
prepare_final_review_versions(assignment, maps)
end
def self.review_response_report(id, assignment, type, review_user)
if review_user.nil?
# This is not a search, so find all reviewers for this assignment
response_maps_with_distinct_participant_id =
ResponseMap.select("DISTINCT reviewer_id").where('reviewed_object_id = ? and type = ? and calibrate_to = ?', id, type, 0)
@reviewers = []
response_maps_with_distinct_participant_id.each do |reviewer_id_from_response_map|
@reviewers << ReviewResponseMap.get_reviewer_with_id(assignment.id, reviewer_id_from_response_map.reviewer_id)
end
# we sort the reviewer by name here, using whichever class it is an instance of
unless assignment.reviewer_is_team
@reviewers = Participant.sort_by_name(@reviewers)
else
@reviewers = Team.sort_by_name(@reviewers)
end
else
# This is a search, so find reviewers by user's full name
user_ids = User.select("DISTINCT id").where('fullname LIKE ?', '%' + review_user[:fullname] + '%')
#E1973 - we use a separate query depending on if the reviewer is a team or participant
unless assignment.reviewer_is_team
@reviewers = AssignmentParticipant.where('user_id IN (?) and parent_id = ?', user_ids, assignment.id)
else
reviewer_participants = AssignmentTeam.where('id IN (?) and parent_id = ?', team_ids, assignment.id)
@reviewers = []
reviewer_participants.each do |participant|
unless @reviewers.include? participant.team
@reviewers << participant.team
end
end
end
end
# @review_scores[reveiwer_id][reviewee_id] = score for assignments not using vary_rubric_by_rounds feature
# @review_scores[reviewer_id][round][reviewee_id] = score for assignments using vary_rubric_by_rounds feature
end
def email(defn, _participant, assignment)
defn[:body][:type] = "Peer Review"
AssignmentTeam.find(reviewee_id).users.each do |user|
defn[:body][:obj_name] = assignment.name
defn[:body][:first_name] = User.find(user.id).fullname
defn[:to] = User.find(user.id).email
Mailer.sync_message(defn).deliver_now
end
end
def self.prepare_final_review_versions(assignment, maps)
review_final_versions = {}
rounds_num = assignment.rounds_of_reviews
if rounds_num and rounds_num > 1
(1..rounds_num).each do |round|
prepare_review_response(assignment, maps, review_final_versions, round)
end
else
prepare_review_response(assignment, maps, review_final_versions, nil)
end
review_final_versions
end
def self.prepare_review_response(assignment, maps, review_final_versions, round)
symbol = if round.nil?
:review
else
("review round" + " " + round.to_s).to_sym
end
review_final_versions[symbol] = {}
# TODO E1936 (future work)
# review_questionnaire_id method signature has changed
# need to change call to review_questionnaire_id here
# cannot do this as part of this project's scope
# the structure of the output (assumes only 1 questionnaire per round) has to change
# and this change has to bubble all the way up to tone analysis, heatmaps, and review scores pop-up
# this is a vary-by-topic redesign project all on its own
review_final_versions[symbol][:questionnaire_id] = assignment.review_questionnaire_id(round)
response_ids = []
maps.each do |map|
where_map = {map_id: map.id}
where_map[:round] = round unless round.nil?
responses = Response.where(where_map)
response_ids << responses.last.id unless responses.empty?
end
review_final_versions[symbol][:response_ids] = response_ids
end
#Build a hash for storing response ids of authors in each team
#
# feedback_final_versions[symbol][team.name][:feedback_response_ids] will have the list of author response_ids for each team
def self.final_feedbacks_for_reviewer(assignment_id, reviewer_id)
feedback_final_versions = {}
review_rounds = Assignment.find(assignment_id).rounds_of_reviews
@response_maps = ResponseMap.where('reviewed_object_id = ? && type = ? && reviewer_id = ?', assignment_id, 'ReviewResponseMap', reviewer_id)
(1..review_rounds).each do |round|
# Loop over every review that this reviewer completed
@response_maps.each do |response_map|
feedback_response_ids = []
response = Response.where('map_id = ?', response_map.id)
# filter the response corresponding to each round.
response = response.select {|response| response.round == round }
# each reviewee id in response map corresponds to the team which the reviewer has reviewed.
team = Team.find(response_map.reviewee_id) unless response_map.reviewee_id.nil?
# populate the hash map with corresponding author response_ids of each team.
prepare_final_feedback_versions(response, team, round, feedback_final_versions, feedback_response_ids)
end
end
feedback_final_versions
end
def self.prepare_final_feedback_versions(response, team, round, feedback_final_versions, feedback_response_ids)
# symbol: key in the hash determining the round number
symbol = ("review round" + " " + round.to_s).to_sym
unless response.empty?
# Retrieve the author feedback response maps by all the teammates reviewing the review of their work.
author_feedback_response_maps = ResponseMap.where('reviewed_object_id = ? && type = ?', response.first.id, 'FeedbackResponseMap')
#Loop over each author responses save the corresponding response id in the feedback_response_ids list.
author_feedback_response_maps.each do |author_feedback_response_map|
corresponding_response = Response.where('map_id = ?', author_feedback_response_map.id)
next if corresponding_response.empty?
feedback_final_versions[symbol] = {} if feedback_final_versions[symbol].nil?
# save the response_ids in hash map only if the author response is valid.
unless corresponding_response.empty?
# save the questionnaire_id only once per round.
if feedback_final_versions[symbol][:questionnaire_id].nil?
feedback_final_versions[symbol][:questionnaire_id] = feedback_questionnaire_id(corresponding_response)
end
# store the response id of authors in the list
feedback_response_ids << corresponding_response.first.id
end
end
end
# save the response_ids list only if the team exists.
unless team.nil?
feedback_final_versions[symbol] = {} if feedback_final_versions[symbol].nil?
feedback_final_versions[symbol][team.name] = {}
# save the response_ids list under the team_name key within hash.
feedback_final_versions[symbol][team.name][:feedback_response_ids] = feedback_response_ids
end
end
# Collect the feedback questionnaire id for each round.
def self.feedback_questionnaire_id(feedback_response)
feedback_answer = Answer.where(response_id: feedback_response.first.id)
question = Question.find(feedback_answer.first.question_id)
question.questionnaire_id
end
end