Rcov C0 Coverage Information - RCov

app/controllers/assignments_controller.rb

Name Total Lines Lines of Code Total Coverage Code Coverage
app/controllers/assignments_controller.rb 553 442
95.84%
95.93%

Key

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.

Coverage Details

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