Rcov C0 Coverage Information - RCov

app/controllers/submissions_controller.rb

Name Total Lines Lines of Code Total Coverage Code Coverage
app/controllers/submissions_controller.rb 579 472
90.33%
88.14%

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 
3 class SubmissionsController < ApplicationController
4   include SubmissionsHelper
5   include PaginationHelper
6 
7   before_filter :authorize_only_for_admin,
8                 :except => [:server_time,
9                             :populate_file_manager,
10                             :browse,
11                             :index,
12                             :file_manager,
13                             :update_files,
14                             :download,
15                             :s_table_paginate,
16                             :collect_and_begin_grading,
17                             :manually_collect_and_begin_grading,
18                             :collect_ta_submissions,
19                             :repo_browser,
20                             :populate_repo_browser,
21                             :update_converted_pdfs]
22   before_filter :authorize_for_ta_and_admin,
23                 :only => [:browse,
24                           :index,
25                           :s_table_paginate,
26                           :collect_and_begin_grading,
27                           :manually_collect_and_begin_grading,
28                           :collect_ta_submissions,
29                           :repo_browser,
30                           :populate_repo_browser,
31                           :update_converted_pdfs]
32   before_filter :authorize_for_student,
33                 :only => [:file_manager,
34                           :populate_file_manager,
35                           :update_files]
36   before_filter :authorize_for_user, :only => [:download]
37 
38   S_TABLE_PARAMS = {
39     :model => Grouping,
40     :per_pages => [15, 30, 50, 100, 150, 500, 1000],
41     :filters => {
42       'none' => {
43         :display => I18n.t("browse_submissions.show_all"),
44         :proc => lambda { |params, to_include|
45           return params[:assignment].groupings.all(:include => to_include)}},
46       'unmarked' => {
47         :display => I18n.t("browse_submissions.show_unmarked"),
48         :proc => lambda { |params, to_include| return params[:assignment].groupings.all(:include => [to_include]).select{|g| !g.has_submission? || (g.has_submission? && g.current_submission_used.result.marking_state == Result::MARKING_STATES[:unmarked]) } }},
49       'partial' => {
50         :display => I18n.t("browse_submissions.show_partial"),
51         :proc => lambda { |params, to_include| return params[:assignment].groupings.all(:include => [to_include]).select{|g| g.has_submission? && g.current_submission_used.result.marking_state == Result::MARKING_STATES[:partial] } }},
52       'complete' => {
53         :display => I18n.t("browse_submissions.show_complete"),
54         :proc => lambda { |params, to_include| return params[:assignment].groupings.all(:include => [to_include]).select{|g| g.has_submission? && g.current_submission_used.result.marking_state == Result::MARKING_STATES[:complete] } }},
55       'released' => {
56         :display => I18n.t("browse_submissions.show_released"),
57         :proc => lambda { |params, to_include| return params[:assignment].groupings.all(:include => [to_include]).select{|g| g.has_submission? && g.current_submission_used.result.released_to_students} }},
58       'assigned' => {
59         :display => I18n.t("browse_submissions.show_assigned_to_me"),
60         :proc => lambda { |params, to_include| return params[:assignment].ta_memberships.find_all_by_user_id(params[:user_id], :include => [:grouping => to_include]).collect{|m| m.grouping} }}
61     },
62     :sorts => {
63       'group_name' => lambda { |a,b| a.group.group_name.downcase <=> b.group.group_name.downcase},
64       'repo_name' => lambda { |a,b| a.group.repo_name.downcase <=> b.group.repo_name.downcase },
65       'revision_timestamp' => lambda { |a,b|
66         return -1 if !a.has_submission?
67         return 1 if !b.has_submission?
68         return a.current_submission_used.revision_timestamp <=> b.current_submission_used.revision_timestamp
69       },
70       'marking_state' => lambda { |a,b|
71         return -1 if !a.has_submission?
72         return 1 if !b.has_submission?
73         return a.current_submission_used.result.marking_state <=> b.current_submission_used.result.marking_state
74       },
75       'total_mark' => lambda { |a,b|
76         return -1 if !a.has_submission?
77         return 1 if !b.has_submission?
78         return a.current_submission_used.result.total_mark <=> b.current_submission_used.result.total_mark
79       },
80       'grace_credits_used' => lambda { |a,b|
81         return a.grace_period_deduction_sum <=> b.grace_period_deduction_sum
82       },
83       'section' => lambda { |a,b|
84         return -1 if !a.section
85         return 1 if !b.section
86         return a.section <=> b.section
87       }
88     }
89   }
90 
91   def repo_browser
92     @assignment = Assignment.find(params[:assignment_id])
93     @grouping = Grouping.find(params[:id])
94     @assignment = @grouping.assignment
95     @path = params[:path] || '/'
96     @previous_path = File.split(@path).first
97     @repository_name = @grouping.group.repository_name
98     repo = @grouping.group.repo
99     begin
100       if !params[:revision_timestamp].nil?
101         @revision_number = repo.get_revision_by_timestamp(Time.parse(params[:revision_timestamp])).revision_number
102       elsif !params[:revision_number].nil?
103         @revision_number = params[:revision_number].to_i
104       else
105         @revision_number = repo.get_latest_revision.revision_number
106       end
107       @revision = repo.get_revision(@revision_number)
108       @revision_timestamp = @revision.timestamp
109       repo.close
110     rescue Exception => e
111       flash[:error] = e.message
112       @revision_number = repo.get_latest_revision.revision_number
113       @revision_timestamp = repo.get_latest_revision.timestamp
114       repo.close
115     end
116   end
117 
118   def populate_repo_browser
119     @grouping = Grouping.find(params[:id])
120     @assignment = @grouping.assignment
121     @path = params[:path] || '/'
122     @revision_number = params[:revision_number]
123     @previous_path = File.split(@path).first
124     @grouping.group.access_repo do |repo|
125       begin
126         @revision = repo.get_revision(params[:revision_number].to_i)
127         @directories = @revision.directories_at_path(File.join(@assignment.repository_folder, @path))
128         @files = @revision.files_at_path(File.join(@assignment.repository_folder, @path))
129       rescue Exception => @find_revision_error
130         render :template => 'submissions/repo_browser/find_revision_error'
131         return
132       end
133       @table_rows = {}
134       @files.sort.each do |file_name, file|
135         @table_rows[file.id] = construct_repo_browser_table_row(file_name, file)
136       end
137       @directories.sort.each do |directory_name, directory|
138         @table_rows[directory.id] = construct_repo_browser_directory_table_row(directory_name, directory)
139       end
140       render :template => 'submissions/repo_browser/populate_repo_browser'
141     end
142   end
143 
144   def file_manager
145     @assignment = Assignment.find(params[:assignment_id])
146     @grouping = current_user.accepted_grouping_for(@assignment.id)
147 
148     if @grouping.nil?
149       redirect_to :controller => 'assignments',
150                   :action => 'student_interface',
151                   :id => params[:id]
152       return
153     end
154 
155     user_group = @grouping.group
156     @path = params[:path] || '/'
157 
158     # Some vars need to be set in update_files too, so do this in a
159     # helper. See update_files action where this is used as well.
160     set_filebrowser_vars(user_group, @assignment)
161   end
162 
163   def populate_file_manager
164     @assignment = Assignment.find(params[:assignment_id])
165     @grouping = current_user.accepted_grouping_for(@assignment.id)
166     user_group = @grouping.group
167     revision_number= params[:revision_number]
168     @path = params[:path] || '/'
169     @previous_path = File.split(@path).first
170 
171     user_group.access_repo do |repo|
172       if revision_number.nil?
173         @revision = repo.get_latest_revision
174       else
175        @revision = repo.get_revision(revision_number.to_i)
176       end
177       @directories = @revision.directories_at_path(File.join(@assignment.repository_folder, @path))
178       @files = @revision.files_at_path(File.join(@assignment.repository_folder, @path))
179       @table_rows = {}
180       @files.sort.each do |file_name, file|
181         @table_rows[file.id] = construct_file_manager_table_row(file_name, file)
182       end
183       if @grouping.repository_external_commits_only?
184         @directories.sort.each do |directory_name, directory|
185           @table_rows[directory.id] = construct_file_manager_dir_table_row(directory_name, directory)
186         end
187       end
188       render :action => 'file_manager_populate'
189     end
190   end
191 
192   def manually_collect_and_begin_grading
193     @grouping = Grouping.find(params[:id])
194     @revision_number = params[:current_revision_number].to_i
195     SubmissionCollector.instance.manually_collect_submission(@grouping,
196       @revision_number)
197     redirect_to :action => 'update_converted_pdfs', :id => @grouping.id
198   end
199 
200   def collect_and_begin_grading
201     assignment = Assignment.find(params[:assignment_id])
202     grouping = Grouping.find(params[:id])
203     if !assignment.submission_rule.can_collect_now?
204       flash[:error] = I18n.t("browse_submissions.could_not_collect",
205         :group_name => grouping.group.group_name)
206     else
207       #Push grouping to the priority queue
208       SubmissionCollector.instance.push_grouping_to_priority_queue(grouping)
209       flash[:success] = I18n.t("collect_submissions.priority_given")
210     end
211     redirect_to :action => 'browse', :id => assignment.id
212   end
213 
214   def collect_all_submissions
215     assignment = Assignment.find(params[:assignment_id], :include => [:groupings])
216     if !assignment.submission_rule.can_collect_now?
217       flash[:error] = I18n.t("collect_submissions.could_not_collect",
218         :assignment_identifier => assignment.short_identifier)
219     else
220       submission_collector = SubmissionCollector.instance
221       submission_collector.push_groupings_to_queue(assignment.groupings)
222       flash[:success] = I18n.t("collect_submissions.collection_job_started",
223         :assignment_identifier => assignment.short_identifier)
224     end
225     redirect_to :action => 'browse', :id => assignment.id
226   end
227 
228   def collect_ta_submissions
229     assignment = Assignment.find(params[:assignment_id])
230     if !assignment.submission_rule.can_collect_now?
231       flash[:error] = I18n.t("collect_submissions.could_not_collect",
232         :assignment_identifier => assignment.short_identifier)
233     else
234       groupings = assignment.groupings.find(:all, :include => :tas, :conditions => ["users.id = ?", current_user.id])
235       submission_collector = SubmissionCollector.instance
236       submission_collector.push_groupings_to_queue(groupings)
237       flash[:success] = I18n.t("collect_submissions.collection_job_started",
238         :assignment_identifier => assignment.short_identifier)
239     end
240     redirect_to :action => 'browse', :id => assignment.id
241   end
242 
243   def update_converted_pdfs
244     @grouping = Grouping.find(params[:grouping_id])
245     @submission = @grouping.current_submission_used
246     @pdf_count= 0
247     @converted_count = 0
248     if !@submission.nil?
249       @submission.submission_files.each do |file|
250         if file.is_pdf?
251           @pdf_count += 1
252           if file.is_converted
253             @converted_count += 1
254           end
255         end
256       end
257     end
258   end
259 
260   def browse
261     if current_user.ta?
262       params[:filter] = 'assigned'
263     else
264       if params[:filter] == nil or params[:filter].blank?
265         params[:filter] = 'none'
266       end
267     end
268     if params[:sort_by] == nil or params[:sort_by].blank?
269       params[:sort_by] = 'group_name'
270     end
271     @assignment = Assignment.find(params[:assignment_id])
272     @groupings, @groupings_total = handle_paginate_event(
273       S_TABLE_PARAMS,                                     # the data structure to handle filtering and sorting
274         { :assignment => @assignment,                     # the assignment to filter by
275           :user_id => current_user.id},                   # the submissions accessable by the current user
276       params)                                             # additional parameters that affect things like sorting
277 
278     #Eager load all data only for those groupings that will be displayed
279     sorted_groupings = @groupings
280     @groupings = Grouping.find(:all, :conditions => {:id => sorted_groupings},
281       :include => [:assignment, :group, :grace_period_deductions,
282         {:current_submission_used => :result},
283         {:accepted_student_memberships => :user}])
284 
285     #re-sort @groupings by the previous order, because eager loading query
286     #messed up the grouping order
287     @groupings = sorted_groupings.map do |sorted_grouping|
288       @groupings.detect do |unsorted_grouping|
289         unsorted_grouping == sorted_grouping
290       end
291     end
292 
293     @current_page = params[:page].to_i()
294     @per_page = params[:per_page]
295     @filters = get_filters(S_TABLE_PARAMS)
296     @per_pages = S_TABLE_PARAMS[:per_pages]
297     @desc = params[:desc]
298     @filter = params[:filter]
299     @sort_by = params[:sort_by]
300   end
301 
302   def index
303     @assignments = Assignment.all(:order => :id)
304     render :action => 'index', :layout => 'sidebar'
305   end
306 
307   # update_files action handles transactional submission of files.
308   #
309   # Note that you shouldn't use redirect_to in this action. This
310   # is due to @file_manager_errors, which carries over some state
311   # to the file_manager view (via render calls). We need to do
312   # this, because we were storing transaction errors in the flash
313   # hash (i.e. they were stored in the browser's cookie), and in
314   # some circumstances, this produces a cookie overflow error
315   # when the state stored in the cookie exceeds 4k in serialized
316   # form. This was happening prior to the fix of Github issue #30.
317   def update_files
318     # We'll use this hash to carry over some error state to the
319     # file_manager view.
320     @file_manager_errors = Hash.new
321     assignment_id = params[:assignment_id]
322     @assignment = Assignment.find(assignment_id)
323     @path = params[:path] || '/'
324     @grouping = current_user.accepted_grouping_for(assignment_id)
325     if @grouping.repository_external_commits_only?
326       raise I18n.t("student.submission.external_submit_only")
327     end
328     if !@grouping.is_valid?
329       # can't use redirect_to here. See comment of this action for more details.
330       set_filebrowser_vars(@grouping.group, @assignment)
331       render :action => "file_manager", :id => assignment_id
332       return
333     end
334     @grouping.group.access_repo do |repo|
335 
336       assignment_folder = File.join(@assignment.repository_folder, @path)
337 
338       # Get the revision numbers for the files that we've seen - these
339       # values will be the "expected revision numbers" that we'll provide
340       # to the transaction to ensure that we don't overwrite a file that's
341       # been revised since the user last saw it.
342       file_revisions = params[:file_revisions].nil? ? [] : params[:file_revisions]
343 
344       # The files that will be replaced - just give an empty array
345       # if params[:replace_files] is nil
346       replace_files = params[:replace_files].nil? ? {} : params[:replace_files]
347 
348       # The files that will be deleted
349       delete_files = params[:delete_files].nil? ? {} : params[:delete_files]
350 
351       # The files that will be added
352       new_files = params[:new_files].nil? ? {} : params[:new_files]
353 
354       # Create transaction, setting the author.  Timestamp is implicit.
355       txn = repo.get_transaction(current_user.user_name)
356 
357       log_messages = []
358       begin
359         # delete files marked for deletion
360         delete_files.keys.each do |filename|
361           txn.remove(File.join(assignment_folder, filename), file_revisions[filename])
362           log_messages.push("Student '#{current_user.user_name}' deleted file '#{filename}' for assignment '#{@assignment.short_identifier}'.")
363         end
364 
365         # Replace files
366         replace_files.each do |filename, file_object|
367           # Sometimes the file pointer of file_object is at the end of the file.
368           # In order to avoid empty uploaded files, rewind it to be save.
369           file_object.rewind
370           txn.replace(File.join(assignment_folder, filename), file_object.read, file_object.content_type, file_revisions[filename])
371           log_messages.push("Student '#{current_user.user_name}' replaced content of file '#{filename}' for assignment '#{@assignment.short_identifier}'.")
372         end
373 
374         # Add new files
375         new_files.each do |file_object|
376           # sanitize_file_name in SubmissionsHelper
377           if file_object.original_filename.nil?
378             raise I18n.t("student.submission.invalid_file_name")
379           end
380           # Sometimes the file pointer of file_object is at the end of the file.
381           # In order to avoid empty uploaded files, rewind it to be save.
382           file_object.rewind
383           txn.add(File.join(assignment_folder, sanitize_file_name(file_object.original_filename)), file_object.read, file_object.content_type)
384           log_messages.push("Student '#{current_user.user_name}' submitted file '#{file_object.original_filename}' for assignment '#{@assignment.short_identifier}'.")
385         end
386 
387         # finish transaction
388         if !txn.has_jobs?
389           flash[:transaction_warning] = I18n.t("student.submission.no_action_detected")
390           # can't use redirect_to here. See comment of this action for more details.
391           set_filebrowser_vars(@grouping.group, @assignment)
392           render :action => "file_manager", :id => assignment_id
393           return
394         end
395         if !repo.commit(txn)
396           @file_manager_errors[:update_conflicts] = txn.conflicts
397         else
398           flash[:success] = I18n.t('update_files.success')
399           # flush log messages
400           m_logger = MarkusLogger.instance
401           log_messages.each do |msg|
402             m_logger.log(msg)
403           end
404         end
405 
406         # Are we past collection time?
407         if @assignment.submission_rule.can_collect_now?
408           flash[:commit_notice] = @assignment.submission_rule.commit_after_collection_message(@grouping)
409         end
410         # can't use redirect_to here. See comment of this action for more details.
411         set_filebrowser_vars(@grouping.group, @assignment)
412         render :action => "file_manager", :id => assignment_id
413 
414       rescue Exception => e
415         m_logger = MarkusLogger.instance
416         m_logger.log(e.message)
417         # can't use redirect_to here. See comment of this action for more details.
418         @file_manager_errors[:commit_error] = e.message
419         set_filebrowser_vars(@grouping.group, @assignment)
420         render :action => "file_manager", :id => assignment_id
421       end
422     end
423   end
424 
425   def download
426     @assignment = Assignment.find(params[:id])
427     # find_appropriate_grouping can be found in SubmissionsHelper
428 
429     @grouping = find_appropriate_grouping(@assignment.id, params)
430 
431     revision_number = params[:revision_number]
432     path = params[:path] || '/'
433     @grouping.group.access_repo do |repo|
434       if revision_number.nil?
435         @revision = repo.get_latest_revision
436       else
437         @revision = repo.get_revision(revision_number.to_i)
438       end
439 
440       begin
441        file = @revision.files_at_path(File.join(@assignment.repository_folder, path))[params[:file_name]]
442        file_contents = repo.download_as_string(file)
443       rescue Exception => e
444         render :text => I18n.t("student.submission.missing_file", :file_name => params[:file_name], :message => e.message)
445         return
446       end
447 
448       if SubmissionFile.is_binary?(file_contents)
449         # If the file appears to be binary, send it as a download
450         send_data file_contents, :disposition => 'attachment', :filename => params[:file_name]
451       else
452         # Otherwise, blast it out to the screen
453         render :text => file_contents, :layout => 'sanitized_html'
454       end
455     end
456   end
457 
458   def update_submissions
459     return unless request.post?
460     assignment = Assignment.find(params[:id])
461     errors = []
462     groupings = []
463     if params[:ap_select_full] == 'true'
464       # We should have been passed a filter
465       if params[:filter].blank?
466         raise I18n.t("student.submission.expect_filter")
467       end
468       # Get all Groupings for this filter
469       groupings = S_TABLE_PARAMS[:filters][params[:filter]][:proc].call({:assignment => assignment, :user_id => current_user.id}, {})
470     else
471       # User selected particular Grouping IDs
472       if params[:groupings].nil?
473         errors.push(I18n.t('results.must_select_a_group'))
474       else
475         groupings = assignment.groupings.find(params[:groupings])
476       end
477     end
478 
479     log_message = ""
480     if !params[:release_results].nil?
481       changed = set_release_on_results(groupings, true, errors)
482       log_message = "Marks released for assignment '#{assignment.short_identifier}', ID: '" +
483                     "#{assignment.id}' (for #{changed} groups)."
484     elsif !params[:unrelease_results].nil?
485       changed = set_release_on_results(groupings, false, errors)
486       log_message = "Marks unreleased for assignment '#{assignment.short_identifier}', ID: '" +
487                     "#{assignment.id}' (for #{changed} groups)."
488     end
489 
490 
491     if !groupings.empty?
492       assignment.set_results_average
493     end
494 
495     if changed > 0
496       flash[:success] = I18n.t('results.successfully_changed', {:changed => changed})
497       m_logger = MarkusLogger.instance
498       m_logger.log(log_message)
499     end
500     flash[:errors] = errors
501 
502     redirect_to :action => 'browse', :id => params[:id]
503   end
504 
505   def unrelease
506     return unless request.post?
507     if params[:groupings].nil?
508       flash[:release_results] = I18n.t("assignment.group.select_a_group")
509     else
510       params[:groupings].each do |g|
511         g.unrelease_results
512       end
513       m_logger = MarkusLogger.instance
514       assignment = Assignment.find(params[:id])
515       m_logger.log("Marks unreleased for assignment '#{assignment.short_identifier}', ID: '" +
516                    "#{assignment.id}' (for #{params[:groupings].length} groups).")
517     end
518     redirect_to :action => 'browse', :id => params[:id]
519   end
520 
521   # See Assignment.get_simple_csv_report for details
522   def download_simple_csv_report
523     assignment = Assignment.find(params[:assignment_id])
524     send_data assignment.get_simple_csv_report,
525               :disposition => 'attachment',
526               :type => 'application/vnd.ms-excel',
527               :filename => "#{assignment.short_identifier} simple report.csv"
528   end
529 
530   # See Assignment.get_detailed_csv_report for details
531   def download_detailed_csv_report
532     assignment = Assignment.find(params[:assignment_id])
533     send_data assignment.get_detailed_csv_report,
534               :disposition => 'attachment',
535               :type => 'application/vnd.ms-excel',
536               :filename => "#{assignment.short_identifier} detailed report.csv"
537   end
538 
539   # See Assignment.get_svn_export_commands for details
540   def download_svn_export_commands
541     assignment = Assignment.find(params[:assignment_id])
542     svn_commands = assignment.get_svn_export_commands
543     send_data svn_commands.join("\n"),
544               :disposition => 'attachment',
545               :type => 'text/plain',
546               :filename => "#{assignment.short_identifier}_svn_exports"
547   end
548 
549   # See Assignment.get_svn_repo_list for details
550   def download_svn_repo_list
551     assignment = Assignment.find(params[:assignment_id])
552     send_data assignment.get_svn_repo_list,
553               :disposition => 'attachment',
554               :type => 'text/plain',
555               :filename => "#{assignment.short_identifier}_svn_repo_list"
556   end
557 
558   # This action is called periodically from file_manager.
559   def server_time
560     render :partial => 'server_time'
561   end
562 
563   private
564 
565   # Used in update_files and file_manager actions
566   def set_filebrowser_vars(user_group, assignment)
567     user_group.access_repo do |repo|
568       @revision = repo.get_latest_revision
569       @files = @revision.files_at_path(File.join(@assignment.repository_folder, @path))
570       @missing_assignment_files = []
571       assignment.assignment_files.each do |assignment_file|
572         if !@revision.path_exists?(File.join(assignment.repository_folder,
573         assignment_file.filename))
574           @missing_assignment_files.push(assignment_file)
575         end
576       end
577     end
578   end
579 end

Generated on Sun Feb 05 00:08:06 -0500 2012 with rcov 0.9.10