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

MarkUsProject / Markus / 18076943101

28 Sep 2025 04:31PM UTC coverage: 91.843% (+0.005%) from 91.838%
18076943101

Pull #7673

github

web-flow
Merge e4757ec77 into 62d1155ac
Pull Request #7673: Convert "Rename Group" modal to React component

702 of 1491 branches covered (47.08%)

Branch coverage included in aggregate %.

15 of 15 new or added lines in 1 file covered. (100.0%)

155 existing lines in 10 files now uncovered.

42307 of 45338 relevant lines covered (93.31%)

119.56 hits per line

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

80.14
/app/controllers/grade_entry_forms_controller.rb
1
# The actions necessary for managing grade entry forms.
2

3
class GradeEntryFormsController < ApplicationController
1✔
4
  include GradeEntryFormsHelper
1✔
5
  include RoutingHelper
1✔
6

7
  before_action { authorize! }
95✔
8
  layout 'assignment_content'
1✔
9

10
  responders :flash
1✔
11

12
  # Create a new grade entry form
13
  def new
1✔
14
    @grade_entry_form = current_course.grade_entry_forms.new
2✔
15
  end
16

17
  def create
1✔
18
    # Edit params before updating model
19
    new_params = update_grade_entry_form_params(params)
2✔
20

21
    @grade_entry_form = current_course.grade_entry_forms.create(new_params)
2✔
22
    respond_with(@grade_entry_form,
2✔
23
                 location: -> { edit_course_grade_entry_form_path(current_course, @grade_entry_form) })
2✔
24
  end
25

26
  # Edit the properties of a grade entry form
27
  def edit
1✔
28
    @grade_entry_form = record
2✔
29
  end
30

31
  def update
1✔
32
    @grade_entry_form = record
4✔
33
    # Process changes to input properties
34
    new_params = update_grade_entry_form_params(params)
4✔
35

36
    @grade_entry_form.update(new_params)
4✔
37
    respond_with(@grade_entry_form,
4✔
38
                 location: -> { edit_course_grade_entry_form_path current_course, @grade_entry_form })
4✔
39
  end
40

41
  # View/modify the grades for this grade entry form
42
  def grades
1✔
UNCOV
43
    @grade_entry_form = record
×
44
  end
45

46
  def view_summary
1✔
UNCOV
47
    @grade_entry_form = record
×
48
  end
49

50
  # Update a grade in the table
51
  def update_grade
1✔
UNCOV
52
    grade_entry_form = record
×
53
    grade_entry_student =
UNCOV
54
      grade_entry_form.grade_entry_students.find(params[:student_id])
×
55
    grade =
UNCOV
56
      grade_entry_student.grades.find_or_create_by(grade_entry_item_id: params[:grade_entry_item_id])
×
57

58
    grade.update(grade: params[:updated_grade])
×
59
    grade_entry_student.save # Refresh total grade
×
60
    grade_entry_student.reload
×
UNCOV
61
    render plain: grade_entry_student.get_total_grade
×
62
  end
63

64
  # For students
65
  def student_interface
1✔
66
    @grade_entry_form = record
2✔
67
    unless allowed_to?(:see_hidden?)
2✔
68
      render 'shared/http_status',
1✔
69
             formats: [:html],
70
             locals: {
71
               code: '404',
72
               message: HttpStatusHelper::ERROR_CODE['message']['404']
73
             },
74
             status: :not_found,
75
             layout: false
76
      return
1✔
77
    end
78

79
    # Getting the student's marks for each grade entry item
80
    @grade_entry_student = @grade_entry_form.grade_entry_students.find_by(role_id: current_role.id)
1✔
81
    @columns = []
1✔
82
    @data = []
1✔
83
    @item_percentages = []
1✔
84
    @labels = []
1✔
85
    @grade_entry_form.grade_entry_items.each do |grade_entry_item|
1✔
86
      @columns << "#{grade_entry_item.name} (#{grade_entry_item.out_of})"
×
87
      @labels << grade_entry_item.name
×
88
      mark = @grade_entry_student.grades.find_by(grade_entry_item_id: grade_entry_item.id)
×
89
      if !mark.nil? && !mark.grade.nil?
×
90
        @data << mark.grade
×
UNCOV
91
        @item_percentages << ((mark.grade * 100) / grade_entry_item.out_of).round(2)
×
92
      else
93
        @data << t(:not_applicable)
×
UNCOV
94
        @item_percentages << nil
×
95
      end
96
    end
97

98
    # Get data for the total marks column
99
    if @grade_entry_form.show_total
1✔
100
      @columns << "#{GradeEntryForm.human_attribute_name(:total)} (#{@grade_entry_form.max_mark})"
×
101
      total = @grade_entry_student.get_total_grade
×
102
      if !total.nil?
×
UNCOV
103
        @data << total
×
104
      else
UNCOV
105
        @data << t(:not_applicable)
×
106
      end
107
    end
108
  end
109

110
  def get_mark_columns
1✔
111
    grade_entry_form = record
×
UNCOV
112
    data = grade_entry_form.grade_entry_items.map do |column|
×
113
      {
UNCOV
114
        accessor: column.id.to_s,
×
115
        Header: "#{column.name} (#{column.out_of})",
116
        data: { bonus: column.bonus }
117
      }
118
    end
UNCOV
119
    render json: data
×
120
  end
121

122
  def populate_grades_table
1✔
123
    grade_entry_form = record
13✔
124
    student_pluck_attrs = [
125
      Arel.sql('grade_entry_students.id as _id'),
13✔
126
      :released_to_student,
127
      Arel.sql('users.user_name as user_name'),
128
      Arel.sql('users.first_name as first_name'),
129
      Arel.sql('users.last_name as last_name'),
130
      Arel.sql('roles.hidden as hidden'),
131
      Arel.sql('roles.section_id as section_id')
132
    ]
133

134
    if current_role.instructor?
13✔
135
      students = grade_entry_form.grade_entry_students
6✔
136
                                 .joins(:user)
137
                                 .pluck_to_hash(*student_pluck_attrs)
138
      grades = grade_entry_form.grade_entry_students
6✔
139
                               .joins(:grades)
140
                               .pluck(:id, 'grades.grade_entry_item_id', 'grades.grade')
141
                               .group_by { |x| x[0] }
6✔
142
    elsif current_role.ta?
7✔
143
      students = current_role.grade_entry_students
7✔
144
                             .where(grade_entry_form: grade_entry_form)
145
                             .joins(:user)
146
                             .pluck_to_hash(*student_pluck_attrs)
147
      grades = current_role.grade_entry_students
7✔
148
                           .where(grade_entry_form: grade_entry_form)
149
                           .joins(:grades)
150
                           .pluck(:id, 'grades.grade_entry_item_id', 'grades.grade')
151
                           .group_by { |x| x[0] }
5✔
152
    end
153

154
    if grade_entry_form.show_total
13✔
155
      total_grades = GradeEntryStudent.get_total_grades(students.pluck(:_id))
2✔
156
    end
157

158
    student_grades = students.map do |s|
13✔
159
      (grades[s[:_id]] || []).each do |_, grade_entry_item_id, grade|
11✔
160
        s[grade_entry_item_id] = grade
11✔
161
      end
162
      if grade_entry_form.show_total
11✔
163
        total_marks = total_grades[s[:_id]]
2✔
164
        s[:total_marks] = total_marks.nil? ? t(:not_applicable) : total_marks
2✔
165
      end
166
      s
11✔
167
    end
168
    render json: { data: student_grades,
13✔
169
                   sections: current_course.sections.pluck(:id, :name).to_h }
170
  end
171

172
  # Release/unrelease the marks for all the students or for a subset of students
173
  def update_grade_entry_students
1✔
174
    if params[:students].blank?
8✔
UNCOV
175
      flash_message(:warning, I18n.t('grade_entry_forms.grades.select_a_student'))
×
176
    else
177
      grade_entry_form = record
8✔
178
      release = params[:release_results] == 'true'
8✔
179
      GradeEntryStudent.transaction do
8✔
180
        data = record.course
8✔
181
                     .students
182
                     .joins(:grade_entry_students)
183
                     .where('grade_entry_students.assessment_id': grade_entry_form.id,
184
                            'grade_entry_students.id': params[:students])
185
                     .pluck('grade_entry_students.id', 'roles.id')
186
                     .map { |ges_id, r_id| { id: ges_id, role_id: r_id, released_to_student: release } }
14✔
187
        GradeEntryStudent.upsert_all(data)
8✔
188
        num_changed = data.length
8✔
189
        flash_message(:success, I18n.t('grade_entry_forms.grades.successfully_changed',
8✔
190
                                       numGradeEntryStudentsChanged: num_changed))
191
        action = release ? 'released' : 'unreleased'
8✔
192
        log_message = "#{action} #{num_changed} for marks spreadsheet '#{grade_entry_form.short_identifier}'."
8✔
193
        MarkusLogger.instance.log(log_message)
8✔
194
      rescue StandardError => e
195
        flash_message(:error, e.message)
×
UNCOV
196
        raise ActiveRecord::Rollback
×
197
      end
198
      GradeEntryStudent.where(id: params[:students]).includes(:role).find_each do |current_student|
8✔
199
        if current_student.role.receives_results_emails?
14✔
200
          NotificationMailer.with(student: current_student, form: grade_entry_form, course: current_course)
8✔
201
                            .release_spreadsheet_email.deliver_later
202
        end
203
      end
204
    end
205
  end
206

207
  # Download the grades for this grade entry form as a CSV file
208
  def download
1✔
209
    grade_entry_form = record
8✔
210
    send_data grade_entry_form.export_as_csv(current_role),
8✔
211
              disposition: 'attachment',
212
              type: 'text/csv',
213
              filename: "#{grade_entry_form.short_identifier}_grades_report.csv"
214
  end
215

216
  # Upload the grades for this grade entry form using a CSV file
217
  def upload
1✔
218
    @grade_entry_form = record
19✔
219
    begin
220
      data = process_file_upload(['.csv'])
19✔
221
    rescue StandardError => e
222
      flash_message(:error, e.message)
3✔
223
    else
224
      overwrite = params[:overwrite]
16✔
225
      grades_file = data[:contents]
16✔
226
      result = @grade_entry_form.from_csv(grades_file, overwrite)
16✔
227
      flash_csv_result(result)
16✔
228
    end
229
    redirect_to action: 'grades', id: @grade_entry_form.id
19✔
230
  end
231

232
  def grade_distribution
1✔
233
    grade_entry_form = record
8✔
234

235
    intervals = 20
8✔
236
    dict_data = grade_entry_form.grade_entry_items.map do |item|
8✔
237
      { label: item.name, data: item.grade_distribution_array(intervals) }
8✔
238
    end
239
    column_distributions = {
240
      labels: (0..(intervals - 1)).map { |i| "#{5 * i}-#{5 * i + 5}" },
176✔
241
      datasets: dict_data
242
    }
243

244
    grade_distribution = {
245
      labels: (0..(intervals - 1)).map { |i| "#{5 * i}-#{5 * i + 5}" },
176✔
246
      datasets: [{ data: grade_entry_form.grade_distribution_array(intervals) }]
247
    }
248

249
    summary = {
250
      name: "#{grade_entry_form.short_identifier}: #{grade_entry_form.description}",
8✔
251
      average: grade_entry_form.results_average(points: true) || 0,
252
      median: grade_entry_form.results_median(points: true) || 0,
253
      standard_deviation: grade_entry_form.results_standard_deviation || 0,
254
      max_mark: grade_entry_form.max_mark,
255
      num_entries: grade_entry_form.count_non_nil,
256
      groupings_size: grade_entry_form.grade_entry_students.joins(:role).where('roles.hidden': false).count,
257
      num_fails: grade_entry_form.results_fails,
258
      num_zeros: grade_entry_form.results_zeros
259
    }
260

261
    column_summary = grade_entry_form.grade_entry_items.map do |item|
8✔
262
      {
263
        name: item.name,
8✔
264
        average: item.average || 0,
265
        median: item.median || 0,
266
        max_mark: item.out_of || 0,
267
        standard_deviation: item.standard_deviation || 0,
268
        position: item.position,
269
        num_zeros: item.grades_array.count(&:zero?)
270
      }
271
    end
272

273
    render json: {
8✔
274
      grade_distribution: grade_distribution,
275
      column_distributions: column_distributions,
276
      summary: summary,
277
      column_summary: column_summary
278
    }
279
  end
280

281
  def switch
1✔
282
    redirect_options = referer_options
17✔
283

284
    if redirect_options[:controller] == 'grade_entry_forms'
17✔
285
      redirect_options[:id] = params[:id]
3✔
286
      redirect_to redirect_options
3✔
287
    elsif redirect_options[:grade_entry_form_id]
14✔
288
      redirect_options[:grade_entry_form_id] = params[:id]
2✔
289
      redirect_to redirect_options
2✔
290
    elsif current_role.instructor?
12✔
291
      redirect_to edit_course_grade_entry_form_path(current_course, params[:id])
4✔
292
    elsif current_role.ta?
8✔
293
      redirect_to grades_course_grade_entry_form_path(current_course, params[:id])
4✔
294
    else # current_role.student?
295
      redirect_to student_interface_course_grade_entry_form_path(current_course, params[:id])
4✔
296
    end
297
  end
298

299
  def destroy
1✔
300
    @grade_entry_form = record
2✔
301
    begin
302
      @grade_entry_form.destroy!
2✔
303
      redirect_to course_assignments_path(@current_course)
1✔
304
      flash_message(:success, t('grade_entry_forms.successfully_deleted'))
1✔
305
    rescue ActiveRecord::RecordNotDestroyed
306
      redirect_to edit_course_grade_entry_form_path(@current_course, params[:id])
1✔
307
      flash_message(:error, t('grade_entry_forms.failed_deletion'))
1✔
308
    end
309
  end
310
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