• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

expertiza / expertiza / #19892

12 Jun 2026 11:44PM UTC coverage: 9.671% (-5.6%) from 15.224%
#19892

push

web-flow
Merge c64ae4198 into b68bb8f64

1574 of 16276 relevant lines covered (9.67%)

0.1 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/app/controllers/grades_controller.rb
1
class GradesController < ApplicationController
×
2
  helper :file
×
3
  helper :submitted_content
×
4
  helper :penalty
×
5
  include PenaltyHelper
×
6
  include StudentTaskHelper
×
7
  include AssignmentHelper
×
8
  include GradesHelper
×
9
  include AuthorizationHelper
×
10
  include Scoring
×
11

12
  def action_allowed?
×
13
    case params[:action]
×
14
    when 'view_my_scores'
×
15
      current_user_has_student_privileges? &&
×
16
        are_needed_authorizations_present?(params[:id], 'reader', 'reviewer') &&
×
17
        self_review_finished?
×
18
    when 'view_team'
×
19
      return false unless user_logged_in?
×
20
      return false if params[:id].blank?
×
21

22
      participant = AssignmentParticipant.find_by(id: params[:id])
×
23
      return false if participant.nil?
×
24

25
      if current_user_is_a? 'Student' # students can only see the heat map for their own team
×
26
        team = participant.team
×
27
        team.nil? ? current_user_has_id?(participant.user_id) : team.user?(session[:user])
×
28
      else
×
29
        true
×
30
      end
×
31
    else
×
32
      current_user_has_ta_privileges?
×
33
    end
×
34
  end
×
35

36
  def controller_locale
×
37
    locale_for_student
×
38
  end
×
39

40
  # the view grading report provides the instructor with an overall view of all the grades for
41
  # an assignment. It lists all participants of an assignment and all the reviews they received.
42
  # It also gives a final score, which is an average of all the reviews and greatest difference
43
  # in the scores of all the reviews.
44
  def view
×
45
    @assignment = Assignment.find(params[:id])
×
46
    questionnaires = @assignment.questionnaires
×
47
    if @assignment.varying_rubrics_by_round?
×
48
      @questions = retrieve_questions questionnaires, @assignment.id
×
49
    else
×
50
      @questions = {}
×
51
      questionnaires.each do |questionnaire|
×
52
        @questions[questionnaire.symbol] = questionnaire.questions
×
53
      end
×
54
    end
×
55
    @scores = review_grades(@assignment, @questions)
×
56
    @num_reviewers_assigned_scores = @scores[:teams].length # After rejecting nil scores need original length to iterate over hash
×
57
    averages = vector(@scores)
×
58
    @average_chart = bar_chart(averages, 300, 100, 5)
×
59
    @avg_of_avg = mean(averages)
×
60
    penalties(@assignment.id)
×
61
    @show_reputation = false
×
62
  end
×
63

64
  def view_my_scores
×
65
    @participant = AssignmentParticipant.find(params[:id])
×
66
    @team_id = TeamsUser.team_id(@participant.parent_id, @participant.user_id)
×
67
    return if redirect_when_disallowed
×
68

69
    @assignment = @participant.assignment
×
70
    questionnaires = @assignment.questionnaires
×
71
    @questions = retrieve_questions questionnaires, @assignment.id
×
72
    # @pscore has the newest versions of response for each response map, and only one for each response map (unless it is vary rubric by round)
73
    @pscore = participant_scores(@participant, @questions)
×
74
    make_chart
×
75
    @topic_id = SignedUpTeam.topic_id(@participant.assignment.id, @participant.user_id)
×
76
    @stage = @participant.assignment.current_stage(@topic_id)
×
77
    penalties(@assignment.id)
×
78
    # prepare feedback summaries
79
    summary_ws_url = WEBSERVICE_CONFIG['summary_webservice_url']
×
80
    sum = SummaryHelper::Summary.new.summarize_reviews_by_reviewee(@questions, @assignment, @team_id, summary_ws_url, session)
×
81
    @summary = sum.summary
×
82
    @avg_scores_by_round = sum.avg_scores_by_round
×
83
    @avg_scores_by_criterion = sum.avg_scores_by_criterion
×
84
  end
×
85

86
  # method for alternative view
87
  def view_team
×
88
    @participant = AssignmentParticipant.find(params[:id])
×
89
    @assignment = @participant.assignment
×
90
    @team = @participant.team
×
91
    @team_id = @team.id
×
92
    questionnaires = AssignmentQuestionnaire.where(assignment_id: @assignment.id, topic_id: nil).map(&:questionnaire)
×
93
    @questions = retrieve_questions(questionnaires, @assignment.id)
×
94
    @pscore = participant_scores(@participant, @questions)
×
95
    @penalties = calculate_penalty(@participant.id)
×
96
    @vmlist = []
×
97

98
    counter_for_same_rubric = 0
×
99
    if @assignment.vary_by_topic?
×
100
      topic_id = SignedUpTeam.topic_id_by_team_id(@team_id)
×
101
      topic_specific_questionnaire = AssignmentQuestionnaire.where(assignment_id: @assignment.id, topic_id: topic_id).first.questionnaire
×
102
      @vmlist << populate_view_model(topic_specific_questionnaire)
×
103
    end
×
104
    questionnaires.each do |questionnaire|
×
105
      @round = nil
×
106

107
      # Guard clause to skip questionnaires that have already been populated for topic specific reviewing
108
      if @assignment.vary_by_topic? && questionnaire.type == 'ReviewQuestionnaire'
×
109
        next # Assignments with topic specific rubrics cannot have multiple rounds of review
×
110
      end
×
111

112
      if @assignment.varying_rubrics_by_round? && questionnaire.type == 'ReviewQuestionnaire'
×
113
        questionnaires = AssignmentQuestionnaire.where(assignment_id: @assignment.id, questionnaire_id: questionnaire.id)
×
114
        if questionnaires.count > 1
×
115
          @round = questionnaires[counter_for_same_rubric].used_in_round
×
116
          counter_for_same_rubric += 1
×
117
        else
×
118
          @round = questionnaires[0].used_in_round
×
119
          counter_for_same_rubric = 0
×
120
        end
×
121
      end
×
122
      @vmlist << populate_view_model(questionnaire)
×
123
    end
×
124
    @current_role_name = current_role_name
×
125
  end
×
126

127
  def edit
×
128
    @participant = AssignmentParticipant.find(params[:id])
×
129
    @assignment = @participant.assignment
×
130
    @questions = list_questions(@assignment)
×
131
    @scores = participant_scores(@participant, @questions)
×
132
  end
×
133

134
  def instructor_review
×
135
    participant = AssignmentParticipant.find(params[:id])
×
136
    reviewer = AssignmentParticipant.find_or_create_by(user_id: session[:user].id, parent_id: participant.assignment.id)
×
137
    reviewer.set_handle if reviewer.new_record?
×
138
    review_exists = true
×
139
    reviewee = participant.team
×
140
    review_mapping = ReviewResponseMap.find_or_create_by(reviewee_id: reviewee.id, reviewer_id: reviewer.id, reviewed_object_id: participant.assignment.id)
×
141
    if review_mapping.new_record?
×
142
      review_exists = false
×
143
    else
×
144
      review = Response.find_by(map_id: review_mapping.map_id)
×
145
    end
×
146
    if review_exists
×
147
      redirect_to controller: 'response', action: 'edit', id: review.id, return: 'instructor'
×
148
    else
×
149
      redirect_to controller: 'response', action: 'new', id: review_mapping.map_id, return: 'instructor'
×
150
    end
×
151
  end
×
152

153
  # This method is used from edit methods
154
  def list_questions(assignment)
×
155
    questions = {}
×
156
    questionnaires = assignment.questionnaires
×
157
    questionnaires.each do |questionnaire|
×
158
      questions[questionnaire.symbol] = questionnaire.questions
×
159
    end
×
160
    questions
×
161
  end
×
162

163
  def update
×
164
    participant = AssignmentParticipant.find(params[:id])
×
165
    total_score = params[:total_score]
×
166
    unless format('%.2f', total_score) == params[:participant][:grade]
×
167
      participant.update_attribute(:grade, params[:participant][:grade])
×
168
      message = if participant.grade.nil?
×
169
                  'The computed score will be used for ' + participant.user.name + '.'
×
170
                else
×
171
                  'A score of ' + params[:participant][:grade] + '% has been saved for ' + participant.user.name + '.'
×
172
                end
×
173
    end
×
174
    flash[:note] = message
×
175
    redirect_to action: 'edit', id: params[:id]
×
176
  end
×
177

178
  def save_grade_and_comment_for_submission
×
179
    participant = AssignmentParticipant.find_by(id: params[:participant_id])
×
180
    @team = participant.team
×
181
    @team.grade_for_submission = params[:grade_for_submission]
×
182
    @team.comment_for_submission = params[:comment_for_submission]
×
183
    begin
×
184
      @team.save
×
185
      flash[:success] = 'Grade and comment for submission successfully saved.'
×
186
    rescue StandardError
×
187
      flash[:error] = $ERROR_INFO
×
188
    end
×
189
    redirect_to controller: 'grades', action: 'view_team', id: participant.id
×
190
  end
×
191

192
  def bar_chart(scores, width = 100, height = 100, spacing = 1)
×
193
    link = nil
×
194
    GoogleChart::BarChart.new("#{width}x#{height}", ' ', :vertical, false) do |bc|
×
195
      data = scores
×
196
      bc.data 'Line green', data, '990000'
×
197
      bc.axis :y, range: [0, data.max], positions: data.minmax
×
198
      bc.show_legend = false
×
199
      bc.stacked = false
×
200
      bc.width_spacing_options(bar_width: (width - 30) / (data.size + 1), bar_spacing: 1, group_spacing: spacing)
×
201
      bc.data_encoding = :extended
×
202
      link = bc.to_url
×
203
    end
×
204
    link
×
205
  end
×
206

207
  private
×
208

209
  def populate_view_model(questionnaire)
×
210
    vm = VmQuestionResponse.new(questionnaire, @assignment, @round)
×
211
    vmquestions = questionnaire.questions
×
212
    vm.add_questions(vmquestions)
×
213
    vm.add_team_members(@team)
×
214
    qn = AssignmentQuestionnaire.where(assignment_id: @assignment.id, used_in_round: 2).size >= 1
×
215
    vm.add_reviews(@participant, @team, @assignment.varying_rubrics_by_round?)
×
216
    vm.calculate_metrics
×
217
    vm
×
218
  end
×
219

220
  def redirect_when_disallowed
×
221
    # For author feedback, participants need to be able to read feedback submitted by other teammates.
222
    # If response is anything but author feedback, only the person who wrote feedback should be able to see it.
223
    ## This following code was cloned from response_controller.
224

225
    # ACS Check if team count is more than 1 instead of checking if it is a team assignment
226
    if @participant.assignment.max_team_size > 1
×
227
      team = @participant.team
×
228
      unless team.nil? || (team.user? session[:user])
×
229
        flash[:error] = 'You are not on the team that wrote this feedback'
×
230
        redirect_to '/'
×
231
        return true
×
232
      end
×
233
    else
×
234
      reviewer = AssignmentParticipant.where(user_id: session[:user].id, parent_id: @participant.assignment.id).first
×
235
      return true unless current_user_id?(reviewer.try(:user_id))
×
236
    end
×
237
    false
×
238
  end
×
239

240
  def assign_all_penalties(participant, penalties)
×
241
    @all_penalties[participant.id] = {
×
242
      submission: penalties[:submission],
×
243
      review: penalties[:review],
×
244
      meta_review: penalties[:meta_review],
×
245
      total_penalty: @total_penalty
×
246
    }
×
247
  end
×
248

249
  def make_chart
×
250
    @grades_bar_charts = {}
×
251
    participant_score_types = %i[metareview feedback teammate]
×
252
    if @pscore[:review]
×
253
      scores = []
×
254
      if @assignment.varying_rubrics_by_round?
×
255
        (1..@assignment.rounds_of_reviews).each do |round|
×
256
          responses = @pscore[:review][:assessments].select { |response| response.round == round }
×
257
          scores = scores.concat(score_vector(responses, 'review' + round.to_s))
×
258
          scores -= [-1.0]
×
259
        end
×
260
        @grades_bar_charts[:review] = bar_chart(scores)
×
261
      else
×
262
        charts(:review)
×
263
      end
×
264
    end
×
265
    participant_score_types.each { |symbol| charts(symbol) }
×
266
  end
×
267

268
  def self_review_finished?
×
269
    participant = Participant.find(params[:id])
×
270
    assignment = participant.try(:assignment)
×
271
    self_review_enabled = assignment.try(:is_selfreview_enabled)
×
272
    not_submitted = unsubmitted_self_review?(participant.try(:id))
×
273
    if self_review_enabled
×
274
      !not_submitted
×
275
    else
×
276
      true
×
277
    end
×
278
  end
×
279
end
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc