| Name | Total Lines | Lines of Code | Total Coverage | Code Coverage |
|---|---|---|---|---|
| app/controllers/assignments_controller.rb | 553 | 442 | 95.84%
|
95.93%
|
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 class AssignmentsController < ApplicationController |
3 before_filter :authorize_only_for_admin, |
4 :except => [:deletegroup, |
5 :delete_rejected, |
6 :disinvite_member, |
7 :invite_member, |
8 :creategroup, |
9 :join_group, |
10 :decline_invitation, |
11 :index, |
12 :student_interface, |
13 :update_collected_submissions] |
14 |
15 before_filter :authorize_for_student, |
16 :only => [:student_interface, |
17 :deletegroup, |
18 :delete_rejected, |
19 :disinvite_member, |
20 :invite_member, |
21 :creategroup, |
22 :join_group, |
23 :decline_invitation] |
24 |
25 before_filter :authorize_for_user, |
26 :only => [:index] |
27 |
28 auto_complete_for :assignment, |
29 :name |
30 # Publicly accessible actions --------------------------------------- |
31 |
32 def student_interface |
33 @assignment = Assignment.find(params[:id]) |
34 @student = current_user |
35 @grouping = @student.accepted_grouping_for(@assignment.id) |
36 |
37 if @student.section && |
38 !@student.section.section_due_date_for(@assignment.id).nil? |
39 @due_date = |
40 @student.section.section_due_date_for(@assignment.id).due_date |
41 end |
42 if @due_date.nil? |
43 @due_date = @assignment.due_date |
44 end |
45 if @student.has_pending_groupings_for?(@assignment.id) |
46 @pending_grouping = @student.pending_groupings_for(@assignment.id) |
47 end |
48 if @grouping.nil? |
49 if @assignment.group_max == 1 |
50 begin |
51 @student.create_group_for_working_alone_student(@assignment.id) |
52 rescue RuntimeError => @error |
53 render 'shared/generic_error', :layout => 'error' |
54 return |
55 end |
56 redirect_to :action => 'student_interface', :id => @assignment.id |
57 else |
58 render :action => 'student_interface', :layout => 'no_menu_header' |
59 return |
60 end |
61 else |
62 # We look for the information on this group... |
63 # The members |
64 @studentmemberships = @grouping.student_memberships |
65 # The group name |
66 @group = @grouping.group |
67 # The inviter |
68 @inviter = @grouping.inviter |
69 |
70 # Look up submission information |
71 repo = @grouping.group.repo |
72 @revision = repo.get_latest_revision |
73 @revision_number = @revision.revision_number |
74 |
75 # For running tests |
76 if params[:collect] |
77 @result = manually_collect_and_prepare_test(@grouping, @revision.revision_number) |
78 else |
79 @result = automatically_collect_and_prepare_test(@grouping, @revision.revision_number) |
80 end |
81 #submission = @grouping.submissions.find_by_submission_version_used(true) |
82 if @result |
83 @test_result_files = @result.submission.test_results |
84 else |
85 @test_result_files = nil |
86 end |
87 @token = Token.find_by_grouping_id(@grouping.id) |
88 if @token |
89 @token.reassign_tokens_if_new_day() |
90 end |
91 @last_modified_date = @grouping.assignment_folder_last_modified_date |
92 @num_submitted_files = @grouping.number_of_submitted_files |
93 @num_missing_assignment_files = @grouping.missing_assignment_files.length |
94 repo.close |
95 end |
96 end |
97 |
98 # Displays "Manage Assignments" page for creating and editing |
99 # assignment information |
100 def index |
101 @assignments = Assignment.all(:order => :id) |
102 @grade_entry_forms = GradeEntryForm.all(:order => :id) |
103 if current_user.student? |
104 # get results for assignments for the current user |
105 @a_id_results = Hash.new() |
106 @assignments.each do |a| |
107 if current_user.has_accepted_grouping_for?(a) |
108 grouping = current_user.accepted_grouping_for(a) |
109 if grouping.has_submission? |
110 submission = grouping.current_submission_used |
111 if submission.has_remark? && submission.remark_result.released_to_students |
112 @a_id_results[a.id] = submission.remark_result |
113 elsif submission.has_result? && submission.result.released_to_students |
114 @a_id_results[a.id] = submission.result |
115 end |
116 end |
117 end |
118 end |
119 |
120 # Get the grades for grade entry forms for the current user |
121 @g_id_entries = Hash.new() |
122 @grade_entry_forms.each do |g| |
123 grade_entry_student = g.grade_entry_students.find_by_user_id( |
124 current_user.id ) |
125 if !grade_entry_student.nil? && |
126 grade_entry_student.released_to_student |
127 @g_id_entries[g.id] = grade_entry_student |
128 end |
129 end |
130 |
131 render :action => "student_assignment_list" |
132 return |
133 elsif current_user.ta? |
134 render :action => "grader_index" |
135 else |
136 render :action => 'index' |
137 end |
138 end |
139 |
140 # Called on editing assignments (GET) |
141 def edit |
142 @assignment = Assignment.find_by_id(params[:id]) |
143 |
144 @assignments = Assignment.all |
145 @sections = Section.all |
146 |
147 # build section_due_dates for each section that doesn't already have a due date |
148 Section.all.each do |s| |
149 unless SectionDueDate.find_by_assignment_id_and_section_id(@assignment.id, s.id) |
150 @assignment.section_due_dates.build(:section => s) |
151 end |
152 end |
153 end |
154 |
155 # Called when editing assignments form is submitted (PUT). |
156 def update |
157 @assignment = Assignment.find_by_id(params[:id]) |
158 @assignments = Assignment.all |
159 @sections = Section.all |
160 |
161 if !params[:assignment].nil? |
162 @oldcriteria = @assignment.marking_scheme_type |
163 @newcriteria = params[:assignment][:marking_scheme_type] |
164 if @oldcriteria != @newcriteria and !@assignment.get_criteria.nil? |
165 #TODO use @assignment.criteria.destroy_all when the refactor of |
166 # criteria structure finished |
167 @assignment.get_criteria.each do |criterion| |
168 criterion.destroy |
169 end |
170 end |
171 end |
172 |
173 begin |
174 @assignment = process_assignment_form(@assignment, params) |
175 rescue Exception, RuntimeError => e |
176 @assignment.errors.add(:base, I18n.t("assignment.error", |
177 :message => e.message)) |
178 redirect_to :action => 'edit', :id => @assignment.id |
179 return |
180 end |
181 |
182 if @assignment.save |
183 flash[:success] = I18n.t("assignment.update_success") |
184 redirect_to :action => 'edit', :id => params[:id] |
185 return |
186 else |
187 redirect_to :action => 'edit', :id => @assignment.id |
188 end |
189 end |
190 |
191 # Called in order to generate a form for creating a new assignment. |
192 # i.e. GET request on assignments/new |
193 def new |
194 @assignments = Assignment.all |
195 @assignment = Assignment.new |
196 @sections = Section.all |
197 @assignment.build_submission_rule |
198 @assignment.build_assignment_stat |
199 |
200 # build section_due_dates for each section |
201 Section.all.each { |s| @assignment.section_due_dates.build(:section => s)} |
202 |
203 # set default value if web submits are allowed |
204 @assignment.allow_web_submits = |
205 !MarkusConfigurator.markus_config_repository_external_submits_only? |
206 render :action => 'new' |
207 end |
208 |
209 # Called after a new assignment form is submitted. |
210 def create |
211 @assignment = Assignment.new |
212 @assignment.build_assignment_stat |
213 @assignment.transaction do |
214 begin |
215 @assignment = process_assignment_form(@assignment, params) |
216 rescue Exception, RuntimeError => e |
217 @assignment.errors.add(:base, e.message) |
218 end |
219 if !@assignment.save |
220 @assignments = Assignment.all |
221 @sections = Section.all |
222 render :action => :new |
223 return |
224 end |
225 if params[:persist_groups_assignment] |
226 @assignment.clone_groupings_from(params[:persist_groups_assignment]) |
227 end |
228 if @assignment.save |
229 flash[:success] = I18n.t("assignment.create_success") |
230 end |
231 end |
232 |
233 redirect_to :action => "edit", :id => @assignment.id |
234 |
235 end |
236 |
237 def update_group_properties_on_persist |
238 @assignment = Assignment.find(params[:assignment_id]) |
239 end |
240 |
241 def download_csv_grades_report |
242 assignments = Assignment.all(:order => 'id') |
243 students = Student.all |
244 csv_string = FasterCSV.generate do |csv| |
245 students.each do |student| |
246 row = [] |
247 row.push(student.user_name) |
248 assignments.each do |assignment| |
249 out_of = assignment.total_mark |
250 grouping = student.accepted_grouping_for(assignment.id) |
251 if grouping.nil? |
252 row.push('') |
253 else |
254 submission = grouping.current_submission_used |
255 if submission.nil? |
256 row.push('') |
257 else |
258 total_mark_percentage = submission.result.total_mark / out_of * 100 |
259 if total_mark_percentage.nan? |
260 row.push('') |
261 else |
262 row.push(total_mark_percentage) |
263 end |
264 end |
265 end |
266 end |
267 csv << row |
268 end |
269 end |
270 send_data csv_string, :disposition => "attachment", |
271 :filename => "#{COURSE_NAME} grades report.csv" |
272 end |
273 |
274 |
275 # Methods for the student interface |
276 |
277 def join_group |
278 @assignment = Assignment.find(params[:id]) |
279 @grouping = Grouping.find(params[:grouping_id]) |
280 @user = Student.find(session[:uid]) |
281 @user.join(@grouping.id) |
282 m_logger = MarkusLogger.instance |
283 m_logger.log("Student '#{@user.user_name}' joined group '#{@grouping.group.group_name}'" + |
284 "(accepted invitation).") |
285 redirect_to :action => 'student_interface', :id => params[:id] |
286 end |
287 |
288 def decline_invitation |
289 @assignment = Assignment.find(params[:id]) |
290 @grouping = Grouping.find(params[:grouping_id]) |
291 @user = Student.find(session[:uid]) |
292 @grouping.decline_invitation(@user) |
293 m_logger = MarkusLogger.instance |
294 m_logger.log("Student '#{@user.user_name}' declined invitation for group '" + |
295 "#{@grouping.group.group_name}'.") |
296 redirect_to :action => 'student_interface', :id => params[:id] |
297 end |
298 |
299 def creategroup |
300 @assignment = Assignment.find(params[:id]) |
301 @student = @current_user |
302 m_logger = MarkusLogger.instance |
303 |
304 begin |
305 # We do not allow group creations by students after the due date |
306 # and the grace period for an assignment |
307 if @assignment.past_collection_date? |
308 raise I18n.t('create_group.fail.due_date_passed') |
309 end |
310 if !@assignment.student_form_groups || |
311 @assignment.invalid_override |
312 raise I18n.t('create_group.fail.not_allow_to_form_groups') |
313 end |
314 if @student.has_accepted_grouping_for?(@assignment.id) |
315 raise I18n.t('create_group.fail.already_have_a_group') |
316 end |
317 if params[:workalone] |
318 if @assignment.group_min != 1 |
319 raise I18n.t('create_group.fail.can_not_work_alone', |
320 :group_min => @assignment.group_min) |
321 end |
322 @student.create_group_for_working_alone_student(@assignment.id) |
323 else |
324 @student.create_autogenerated_name_group(@assignment.id) |
325 end |
326 m_logger.log("Student '#{@student.user_name}' created group.", |
327 MarkusLogger::INFO) |
328 rescue RuntimeError => e |
329 flash[:fail_notice] = e.message |
330 m_logger.log("Failed to create group. User: '#{@student.user_name}', Error: '" + |
331 "#{e.message}'.", MarkusLogger::ERROR) |
332 end |
333 redirect_to :action => 'student_interface', :id => @assignment.id |
334 end |
335 |
336 def deletegroup |
337 @assignment = Assignment.find(params[:id]) |
338 @grouping = @current_user.accepted_grouping_for(@assignment.id) |
339 m_logger = MarkusLogger.instance |
340 begin |
341 if @grouping.nil? |
342 raise I18n.t('create_group.fail.do_not_have_a_group') |
343 end |
344 # If grouping is not deletable for @current_user for whatever reason, fail. |
345 if !@grouping.deletable_by?(@current_user) |
346 raise I18n.t('groups.cant_delete') |
347 end |
348 if @grouping.has_submission? |
349 raise I18n.t('groups.cant_delete_already_submitted') |
350 end |
351 @grouping.student_memberships.all(:include => :user).each do |member| |
352 member.destroy |
353 end |
354 # update repository permissions |
355 @grouping.update_repository_permissions |
356 @grouping.destroy |
357 flash[:edit_notice] = I18n.t('assignment.group.deleted') |
358 m_logger.log("Student '#{current_user.user_name}' deleted group '" + |
359 "#{@grouping.group.group_name}'.", MarkusLogger::INFO) |
360 |
361 rescue RuntimeError => e |
362 flash[:fail_notice] = e.message |
363 if @grouping.nil? |
364 m_logger.log( |
365 "Failed to delete group, since no accepted group for this user existed." + |
366 "User: '#{current_user.user_name}', Error: '#{e.message}'.", MarkusLogger::ERROR) |
367 else |
368 m_logger.log("Failed to delete group '#{@grouping.group.group_name}'. User: '" + |
369 "#{current_user.user_name}', Error: '#{e.message}'.", MarkusLogger::ERROR) |
370 end |
371 end |
372 redirect_to :action => 'student_interface', :id => params[:id] |
373 end |
374 |
375 def invite_member |
376 return unless request.post? |
377 @assignment = Assignment.find(params[:id]) |
378 # if instructor formed group return |
379 return if @assignment.invalid_override |
380 |
381 @student = @current_user |
382 @grouping = @student.accepted_grouping_for(@assignment.id) |
383 if @grouping.nil? |
384 raise I18n.t('invite_student.fail.need_to_create_group') |
385 end |
386 |
387 to_invite = params[:invite_member].split(',') |
388 flash[:fail_notice] = [] |
389 flash[:success] = [] |
390 m_logger = MarkusLogger.instance |
391 @grouping.invite(to_invite) |
392 flash[:fail_notice] = @grouping.errors["base"] |
393 if flash[:fail_notice].blank? |
394 flash[:success] = I18n.t('invite_student.success') |
395 end |
396 redirect_to :action => 'student_interface', :id => @assignment.id |
397 end |
398 |
399 # Called by clicking the cancel link in the student's interface |
400 # i.e. cancels invitations |
401 def disinvite_member |
402 @assignment = Assignment.find(params[:id]) |
403 membership = StudentMembership.find(params[:membership]) |
404 disinvited_student = membership.user |
405 membership.delete |
406 membership.save |
407 # update repository permissions |
408 grouping = current_user.accepted_grouping_for(@assignment.id) |
409 grouping.update_repository_permissions |
410 m_logger = MarkusLogger.instance |
411 m_logger.log("Student '#{current_user.user_name}' cancelled invitation for " + |
412 "'#{disinvited_student.user_name}'.") |
413 flash[:edit_notice] = I18n.t('student.member_disinvited') |
414 end |
415 |
416 # Deletes memberships which have been declined by students |
417 def delete_rejected |
418 @assignment = Assignment.find(params[:id]) |
419 membership = StudentMembership.find(params[:membership]) |
420 grouping = membership.grouping |
421 if current_user != grouping.inviter |
422 raise I18n.t('invite_student.fail.only_inviter') |
423 end |
424 membership.delete |
425 membership.save |
426 redirect_to :action => 'student_interface', :id => params[:id] |
427 end |
428 |
429 def update_collected_submissions |
430 @assignments = Assignment.all |
431 end |
432 |
433 # Refreshes the grade distribution graphs and reloads the page |
434 def refresh_graph |
435 assignment = Assignment.find(params[:id]) |
436 assignment.assignment_stat.refresh_grade_distribution |
437 redirect_to :controller => 'main' |
438 end |
439 |
440 private |
441 |
442 def process_assignment_form(assignment, params) |
443 assignment.attributes = params[:assignment] |
444 # Work around rails' (v2.3.8 and potentially v2.3.9) |
445 # accept_nested_attributes_for bug, which do not allow |
446 # us to remove assignment files. We do not support rails |
447 # < 2.3.8. |
448 if ["2.3.8", "2.3.9"].include?(Rails.version) && !params[:assignment][:assignment_files_attributes].nil? |
449 params[:assignment][:assignment_files_attributes].each do |key,assignment_file| |
450 if assignment_file[:_destroy] == "1" |
451 file_to_destroy = assignment.assignment_files.find_by_id(assignment_file[:id]) |
452 file_to_destroy.destroy |
453 end |
454 end |
455 end |
456 |
457 # if there are no section due dates, destroy the objects that were created |
458 if params[:assignment][:section_due_dates_type] == "0" |
459 assignment.section_due_dates.each { |s| s.destroy } |
460 assignment.section_due_dates_type = false |
461 assignment.section_groups_only = false |
462 else |
463 assignment.section_due_dates_type = true |
464 assignment.section_groups_only = true |
465 end |
466 |
467 # Was the SubmissionRule changed? If so, wipe out any existing |
468 # Periods, and switch the type of the SubmissionRule. |
469 # This little conditional has to do some hack-y workarounds, since |
470 # accepts_nested_attributes_for is a little...dumb. |
471 if assignment.submission_rule.attributes['type'] != |
472 params[:assignment][:submission_rule_attributes][:type] |
473 # Some protective measures here to make sure we haven't been duped... |
474 potential_rule = |
475 Module.const_get(params[:assignment][:submission_rule_attributes][:type]) |
476 if !potential_rule.ancestors.include?(SubmissionRule) |
477 raise I18n.t("assignment.not_valid_submission_rule", |
478 :type => params[:assignment][:submission_rule_attributes][:type]) |
479 end |
480 |
481 assignment.submission_rule.destroy |
482 submission_rule = SubmissionRule.new |
483 # A little hack to get around Rails' protection of the "type" |
484 # attribute |
485 submission_rule.type = |
486 params[:assignment][:submission_rule_attributes][:type] |
487 assignment.submission_rule = submission_rule |
488 # For some reason, when we create new rule, we can't just apply |
489 # the params[:assignment] hash to @assignment.attributes...we have |
490 # to create any new periods manually, like this: |
491 if !params[:assignment][:submission_rule_attributes][:periods_attributes].nil? |
492 assignment.submission_rule.periods_attributes = |
493 params[:assignment][:submission_rule_attributes][:periods_attributes] |
494 end |
495 end |
496 |
497 if params[:is_group_assignment] == "true" |
498 # Is the instructor forming groups? |
499 if params[:assignment][:student_form_groups] == "0" |
500 assignment.invalid_override = true |
501 else |
502 assignment.student_form_groups = true |
503 assignment.invalid_override = false |
504 assignment.group_name_autogenerated = true |
505 end |
506 else |
507 assignment.student_form_groups = false; |
508 assignment.invalid_override = false; |
509 assignment.group_min = 1; |
510 assignment.group_max = 1; |
511 end |
512 return assignment |
513 end |
514 |
515 def find_submission_for_test(grouping_id, revision_number) |
516 return Submission.find_by_grouping_id_and_revision_number(grouping_id, revision_number) |
517 end |
518 |
519 # Used every time a student access to the assignment page |
520 # It checks if the due date is passed, and if not, it |
521 # collect the last submission revision |
522 def automatically_collect_and_prepare_test(grouping, revision_number) |
523 # if there is no result for this grouping, |
524 # do nothing, because a student of the grouping |
525 # must run collec_and_test manually first |
526 return if grouping.submissions.empty? |
527 # Once it is time to collect files, student should'nt start to do tests |
528 if !grouping.assignment.submission_rule.can_collect_now? |
529 current_submission_used = grouping.submissions.find_by_submission_version_used(true) |
530 if current_submission_used.revision_number < revision_number |
531 new_submission = Submission.create_by_revision_number(grouping, revision_number) |
532 result = new_submission.result |
533 else |
534 result = current_submission_used.result |
535 end |
536 end |
537 return result |
538 end |
539 |
540 # Used the first time a student from a grouping wanted |
541 # to do test on his code |
542 def manually_collect_and_prepare_test(grouping, revision_number) |
543 # We check if it not the time to collect files |
544 # Once it is time to collect files, student should'nt start to do tests |
545 # And we create a submission with the latest revision of the svn |
546 if !grouping.assignment.submission_rule.can_collect_now? |
547 new_submission = Submission.create_by_revision_number(grouping, revision_number) |
548 result = new_submission.result |
549 end |
550 return result |
551 end |
552 |
553 end |
Generated on Sun Feb 05 00:08:06 -0500 2012 with rcov 0.9.10