| Name | Total Lines | Lines of Code | Total Coverage | Code Coverage |
|---|---|---|---|---|
| app/controllers/groups_controller.rb | 429 | 335 | 89.28%
|
86.87%
|
Code reported as executed by Ruby looks like this...and this: this line is also marked as covered.Lines considered as run by rcov, but not reported by Ruby, look like this,and this: these lines were inferred by rcov (using simple heuristics).Finally, here's a line marked as not executed.
1 require 'fastercsv' |
2 require 'auto_complete' |
3 require 'csv_invalid_line_error' |
4 |
5 # Manages actions relating to editing and modifying |
6 # groups. |
7 class GroupsController < ApplicationController |
8 include GroupsHelper |
9 # Administrator |
10 # - |
11 before_filter :authorize_only_for_admin |
12 |
13 auto_complete_for :student, :user_name |
14 auto_complete_for :assignment, :name |
15 |
16 def note_message |
17 @assignment = Assignment.find(params[:id]) |
18 if params[:success] |
19 flash[:upload_notice] = I18n.t('notes.create.success') |
20 else |
21 flash[:error] = I18n.t('notes.error') |
22 end |
23 end |
24 |
25 # Group administration functions ----------------------------------------- |
26 # Verify that all functions below are included in the authorize filter above |
27 |
28 def new |
29 @assignment = Assignment.find(params[:assignment_id]) |
30 begin |
31 new_grouping_data = @assignment.add_group(params[:new_group_name]) |
32 rescue Exception => e |
33 @error = e.message |
34 render :action => 'error_single' |
35 return |
36 end |
37 @new_grouping = construct_table_row(new_grouping_data, @assignment) |
38 render :add_group |
39 end |
40 |
41 def remove_group |
42 return unless request.delete? |
43 grouping = Grouping.find(params[:grouping_id]) |
44 @assignment = grouping.assignment |
45 @errors = [] |
46 @removed_groupings = [] |
47 if grouping.has_submission? |
48 @errors.push(grouping.group.group_name) |
49 render :action => "delete_groupings" |
50 else |
51 grouping.delete_grouping |
52 @removed_groupings.push(grouping) |
53 render :action => "delete_groupings" |
54 end |
55 end |
56 |
57 def upload_dialog |
58 @assignment = Assignment.find(params[:id]) |
59 render :partial => "groups/modal_dialogs/upload_dialog.rjs" |
60 end |
61 |
62 def download_dialog |
63 @assignment = Assignment.find(params[:id]) |
64 render :partial => "groups/modal_dialogs/download_dialog.rjs" |
65 end |
66 |
67 def rename_group_dialog |
68 @assignment = Assignment.find(params[:assignment_id]) |
69 @grouping_id = params[:grouping_id] |
70 render :partial => "groups/modal_dialogs/rename_group_dialog.rjs" |
71 end |
72 |
73 def rename_group |
74 @assignment = Assignment.find(params[:assignment_id]) |
75 @grouping = Grouping.find(params[:grouping_id]) |
76 @group = @grouping.group |
77 |
78 # Checking if a group with this name already exists |
79 |
80 if (@groups = Group.find(:first, :conditions => {:group_name => |
81 [params[:new_groupname]]})) |
82 existing = true |
83 groupexist_id = @groups.id |
84 end |
85 |
86 if !existing |
87 #We update the group_name |
88 @group.group_name = params[:new_groupname] |
89 @group.save |
90 else |
91 |
92 # We link the grouping to the group already existing |
93 |
94 # We verify there is no other grouping linked to this group on the |
95 # same assignement |
96 params[:groupexist_id] = groupexist_id |
97 params[:assignment_id] = @assignment.id |
98 |
99 if Grouping.find(:all, :conditions => ["assignment_id = |
100 :assignment_id and group_id = :groupexist_id", {:groupexist_id => |
101 groupexist_id, :assignment_id => @assignment.id}]) |
102 flash[:fail_notice] = I18n.t('groups.rename_group.already_in_use') |
103 else |
104 @grouping.update_attribute(:group_id, groupexist_id) |
105 end |
106 end |
107 @grouping_data = construct_table_row(@grouping, @assignment) |
108 end |
109 |
110 def valid_grouping |
111 @assignment = Assignment.find(params[:assignment_id]) |
112 @grouping = Grouping.find(params[:grouping_id]) |
113 @grouping.validate_grouping |
114 @grouping_data = construct_table_row(@grouping, @assignment) |
115 end |
116 |
117 def invalid_grouping |
118 @assignment = Assignment.find(params[:assignment_id]) |
119 @grouping = Grouping.find(params[:grouping_id]) |
120 @grouping.invalidate_grouping |
121 @grouping_data = construct_table_row(@grouping, @assignment) |
122 end |
123 |
124 def populate |
125 @assignment = Assignment.find(params[:assignment_id], |
126 :include => [{ |
127 :groupings => [ |
128 :students, |
129 :non_rejected_student_memberships, |
130 :group]}]) |
131 @groupings = @assignment.groupings |
132 @table_rows = {} |
133 @groupings.each do |grouping| |
134 @table_rows[grouping.id] = construct_table_row(grouping, @assignment) |
135 end |
136 end |
137 |
138 def populate_students |
139 @assignment = Assignment.find(params[:assignment_id], |
140 :include => [:groupings]) |
141 @students = Student.find(:all) |
142 @table_rows = construct_student_table_rows(@students, @assignment) |
143 end |
144 |
145 def index |
146 @all_assignments = Assignment.all(:order => :id) |
147 @assignment = Assignment.find(params[:assignment_id]) |
148 end |
149 |
150 # Allows the user to upload a csv file listing groups. If group_name is equal |
151 # to the only member of a group and the assignment is configured with |
152 # allow_web_subits == false, the student's username will be used as the |
153 # repository name. If MarkUs is not repository admin, the repository name as |
154 # specified by the second field will be used instead. |
155 def csv_upload |
156 flash[:error] = nil # reset from previous errors |
157 flash[:invalid_lines] = nil |
158 @assignment = Assignment.find(params[:assignment_id]) |
159 if request.post? && !params[:group].blank? |
160 # Transaction allows us to potentially roll back if something |
161 # really bad happens. |
162 ActiveRecord::Base.transaction do |
163 # Old groupings get wiped out |
164 if !@assignment.groupings.nil? && @assignment.groupings.length > 0 |
165 @assignment.groupings.destroy_all |
166 end |
167 flash[:invalid_lines] = [] # Store errors of lines in CSV file |
168 begin |
169 # Loop over each row, which lists the members to be added to the group. |
170 FasterCSV.parse(params[:group][:grouplist].read).each_with_index do |row, line_nr| |
171 begin |
172 # Potentially raises CSVInvalidLineError |
173 collision_error = @assignment.add_csv_group(row) |
174 if !collision_error.nil? |
175 flash[:invalid_lines] << I18n.t("csv.line_nr_csv_file_prefix", |
176 { :line_number => line_nr + 1 }) |
177 + " #{collision_error}" |
178 end |
179 rescue CSVInvalidLineError => e |
180 flash[:invalid_lines] << I18n.t("csv.line_nr_csv_file_prefix", |
181 { :line_number => line_nr + 1 }) |
182 + " #{e.message}" |
183 end |
184 end |
185 @assignment.reload # Need to reload to get newly created groupings |
186 number_groupings_added = @assignment.groupings.length |
187 invalid_lines_count = flash[:invalid_lines].length |
188 if invalid_lines_count == 0 |
189 flash[:invalid_lines] = nil |
190 end |
191 if number_groupings_added > 0 |
192 flash[:upload_notice] = I18n.t("csv.groups_added_msg", |
193 { :number_groups => number_groupings_added, |
194 :number_lines => invalid_lines_count }) |
195 end |
196 rescue Exception => e |
197 # We should only get here if something *really* bad/unexpected |
198 # happened. |
199 flash[:error] = I18n.t("csv.groups_unrecoverable_error") |
200 raise ActiveRecord::Rollback |
201 end |
202 end |
203 # Need to reestablish repository permissions. |
204 # This is not handled by the roll back. |
205 @assignment.update_repository_permissions_forall_groupings |
206 end |
207 redirect_to :action => "index", :id => params[:id] |
208 end |
209 |
210 def download_grouplist |
211 assignment = Assignment.find(params[:assignment_id]) |
212 |
213 #get all the groups |
214 groupings = assignment.groupings #FIXME: optimize with eager loading |
215 |
216 file_out = FasterCSV.generate do |csv| |
217 groupings.each do |grouping| |
218 group_array = [grouping.group.group_name, grouping.group.repo_name] |
219 # csv format is group_name, repo_name, user1_name, user2_name, ... etc |
220 grouping.student_memberships.all(:include => :user).each do |member| |
221 group_array.push(member.user.user_name); |
222 end |
223 csv << group_array |
224 end |
225 end |
226 |
227 send_data(file_out, :type => "text/csv", :disposition => "inline") |
228 end |
229 |
230 def use_another_assignment_groups |
231 @target_assignment = Assignment.find(params[:assignment_id]) |
232 source_assignment = Assignment.find(params[:clone_groups_assignment_id]) |
233 |
234 if source_assignment.nil? |
235 flash[:fail_notice] = I18n.t("groups.csv.could_not_find_source") |
236 end |
237 if @target_assignment.nil? |
238 flash[:fail_notice] = I18n.t("groups.csv.could_not_find_target") |
239 end |
240 |
241 # Clone the groupings |
242 @target_assignment.clone_groupings_from(source_assignment.id) |
243 end |
244 |
245 #These actions act on all currently selected students & groups |
246 def global_actions |
247 @assignment = Assignment.find(params[:assignment_id], |
248 :include => [{ |
249 :groupings => [{ |
250 :student_memberships => :user, |
251 :ta_memberships => :user}, |
252 :group]}]) |
253 @tas = Ta.all |
254 grouping_ids = params[:groupings] |
255 student_ids = params[:students] |
256 |
257 if params[:groupings].nil? or params[:groupings].size == 0 |
258 #Just do nothing |
259 render :nothing => true |
260 return |
261 end |
262 |
263 @grouping_data = {} |
264 @groupings = [] |
265 groupings = Grouping.find(grouping_ids) |
266 |
267 case params[:global_actions] |
268 when "delete" |
269 delete_groupings(groupings) |
270 return |
271 when "invalid" |
272 invalidate_groupings(groupings) |
273 return |
274 when "valid" |
275 validate_groupings(groupings) |
276 return |
277 when "assign" |
278 if grouping_ids.length != 1 |
279 @error = I18n.t("assignment.group.select_only_one_group") |
280 render :action => 'error_single' |
281 elsif student_ids |
282 add_members(student_ids, grouping_ids[0], @assignment) |
283 return |
284 else |
285 render :nothing => true |
286 return |
287 end |
288 when "unassign" |
289 remove_members(groupings, params) |
290 return |
291 end |
292 end |
293 |
294 private |
295 #These methods are called through global actions |
296 |
297 # Given a list of grouping, sets their group status to invalid if possible |
298 def invalidate_groupings(groupings) |
299 groupings.each do |grouping| |
300 grouping.invalidate_grouping |
301 end |
302 @groupings_data = construct_table_rows(groupings, @assignment) |
303 render :action => "modify_groupings" |
304 end |
305 |
306 # Given a list of grouping, sets their group status to valid if possible |
307 def validate_groupings(groupings) |
308 groupings.each do |grouping| |
309 grouping.validate_grouping |
310 end |
311 @groupings_data = construct_table_rows(groupings, @assignment) |
312 render :action => "modify_groupings" |
313 end |
314 |
315 # Deletes the given list of groupings if possible |
316 def delete_groupings(groupings) |
317 @removed_groupings = [] |
318 @errors = [] |
319 groupings.each do |grouping| |
320 if grouping.has_submission? |
321 @errors.push(grouping.group.group_name) |
322 else |
323 grouping.delete_grouping |
324 @removed_groupings.push(grouping) |
325 end |
326 end |
327 render :action => "delete_groupings" |
328 end |
329 |
330 # Adds the students given in student_ids to the grouping given in grouping_id |
331 def add_members(student_ids, grouping_id, assignment) |
332 students = Student.find(student_ids) |
333 grouping = Grouping.find(grouping_id) |
334 students.each do |student| |
335 add_member(student, grouping, assignment) |
336 end |
337 @groupings_data = construct_table_rows([grouping], @assignment) |
338 @students_data = construct_student_table_rows(students, @assignment) |
339 |
340 # Generate warning if the number of people assigned to a group exceeds |
341 # the maximum size of a group |
342 students_in_group = grouping.student_membership_number |
343 group_name = grouping.group.group_name |
344 if students_in_group > assignment.group_max |
345 @warning = I18n.t("assignment.group.assign_over_limit", |
346 :group => group_name) |
347 end |
348 |
349 render :action => "add_members" |
350 return |
351 end |
352 |
353 # Adds the student given in student_id to the grouping given in grouping |
354 def add_member (student, grouping, assignment) |
355 set_membership_status = grouping.student_memberships.empty? ? |
356 StudentMembership::STATUSES[:inviter] : |
357 StudentMembership::STATUSES[:accepted] |
358 @messages = [] |
359 @bad_user_names = [] |
360 @error = false |
361 |
362 begin |
363 if student.hidden |
364 raise I18n.t('add_student.fail.hidden', student.user_name) |
365 end |
366 if student.has_accepted_grouping_for?(@assignment.id) |
367 raise I18n.t('add_student.fail.already_grouped', |
368 :user_name => student.user_name) |
369 end |
370 membership_count = grouping.student_memberships.length |
371 grouping.invite(student.user_name, set_membership_status, true) |
372 grouping.reload |
373 |
374 # report success only if # of memberships increased |
375 if membership_count < grouping.student_memberships.length |
376 @messages.push(I18n.t('add_student.success', |
377 :user_name => student.user_name)) |
378 else # something clearly went wrong |
379 raise I18n.t('add_student.fail.general', |
380 :user_name => student.user_name) |
381 end |
382 |
383 # only the first student should be the "inviter" |
384 # (and only update this if it succeeded) |
385 set_membership_status = StudentMembership::STATUSES[:accepted] |
386 rescue Exception => e |
387 @error = true |
388 @messages.push(e.message) |
389 end |
390 |
391 grouping.reload |
392 @grouping = construct_table_row(grouping, assignment) |
393 @group_name = grouping.group.group_name |
394 end |
395 |
396 # Removes the students contained in params from the groupings given |
397 # in groupings. |
398 # This is meant to be called with the params from global_actions, and for |
399 # each student to delete it will have a parameter |
400 # of the form "groupid_studentid" |
401 def remove_members(groupings, params) |
402 all_members = [] |
403 groupings.each do |grouping| |
404 members = grouping.students.delete_if do |student| |
405 !params["#{grouping.id}_#{student.user_name}"] |
406 end |
407 memberships = members.map do |member| |
408 grouping.student_memberships.find_by_user_id(member.id) |
409 end |
410 memberships.each do |membership| |
411 remove_member(membership, grouping, @assignment) |
412 end |
413 all_members = all_members.concat(members) |
414 end |
415 @students_data = construct_student_table_rows(all_members, @assignment) |
416 @groupings_data = construct_table_rows(groupings, @assignment) |
417 render :action => "remove_members" |
418 end |
419 |
420 #Removes the given student membership from the given grouping |
421 def remove_member(membership, grouping, assignment) |
422 @grouping = grouping |
423 grouping.remove_member(membership.id) |
424 grouping.reload |
425 if !grouping.inviter.nil? |
426 @inviter = grouping.accepted_student_memberships.find_by_user_id(grouping.inviter.id) |
427 end |
428 end |
429 end |
Generated on Tue Feb 07 00:07:35 -0500 2012 with rcov 0.9.10