Rcov C0 Coverage Information - RCov

app/models/grade_entry_form.rb

Name Total Lines Lines of Code Total Coverage Code Coverage
app/models/grade_entry_form.rb 281 192
63.35%
57.29%

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 # GradeEntryForm can represent a test, lab, exam, etc.
4 # A grade entry form has many columns which represent the questions and their total
5 # marks (i.e. GradeEntryItems) and many rows which represent students and their
6 # marks on each question (i.e. GradeEntryStudents).
7 class GradeEntryForm < ActiveRecord::Base
8   has_many                  :grade_entry_items, :dependent => :destroy
9   has_many                  :grade_entry_students, :dependent => :destroy  
10   has_many                  :grades, :through => :grade_entry_items
11   
12   validates_presence_of     :short_identifier
13   validates_uniqueness_of   :short_identifier, :case_sensitive => true
14   
15   accepts_nested_attributes_for :grade_entry_items, :allow_destroy => true
16   
17   BLANK_MARK = ""
18   
19   def validate
20     
21     # Check that the date is valid - the date is allowed to be in the past
22     if Time.zone.parse(date.to_s).nil?
23       errors.add :date, I18n.t('grade_entry_forms.invalid_date')
24     end
25   end
26   
27   # The total number of marks for this grade entry form
28   def out_of_total
29     return grade_entry_items.sum('out_of').to_i
30   end
31   
32   # Determine the total mark for a particular student
33   def calculate_total_mark(student_id)
34     # Differentiate between a blank total mark and a total mark of 0
35     total = BLANK_MARK
36     
37     grade_entry_student = self.grade_entry_students.find_by_user_id(student_id)
38     if !grade_entry_student.nil?
39       total = grade_entry_student.grades.sum('grade')
40     end
41     
42     if ((total == 0) && self.all_blank_grades?(grade_entry_student))
43       total = BLANK_MARK
44     end
45     return total
46   end
47   
48   # Determine the total mark for a particular student, as a percentage
49   def calculate_total_percent(student_id)
50     total = self.calculate_total_mark(student_id)
51     percent = BLANK_MARK
52     
53     if total != BLANK_MARK
54       percent = (total / self.out_of_total) * 100
55     end
56     
57     return percent
58   end
59   
60   # Determine the average of all of the students' marks that have been
61   # released so far (return a percentage).
62   def calculate_released_average()
63     totalMarks = 0
64     numReleased = 0
65 
66     grade_entry_students = self.grade_entry_students.find(:all, :conditions => { :released_to_student => true })
67     grade_entry_students.each do |grade_entry_student|
68       totalMark = self.calculate_total_mark(grade_entry_student.user_id)
69       if totalMark != BLANK_MARK
70         totalMarks += totalMark
71         numReleased += 1
72       end
73     end
74 
75     # Watch out for division by 0
76     if (numReleased == 0)
77       return 0
78     end
79   
80     return ((totalMarks / numReleased) / self.out_of_total) * 100
81   end
82   
83   # Return whether or not the given student's grades are all blank
84   # (Needed because ActiveRecord's "sum" method returns 0 even if
85   #  all the grade.grade values are nil and we need to distinguish
86   #  between a total mark of 0 and a blank mark.)
87   def all_blank_grades?(grade_entry_student)
88     grades = grade_entry_student.grades
89     grades_without_nils = grades.select do |grade|
90       !grade.grade.nil?
91     end
92     return grades_without_nils.blank?
93   end
94    
95   # Given two last names, construct an alphabetical category for pagination.
96   # eg. If the input is "Albert" and "Auric", return "Al" and "Au".
97   def construct_alpha_category(last_name1, last_name2, alpha_categories, i)
98     sameSoFar = true
99     index = 0
100     length_of_shorter_name = [last_name1.length, last_name2.length].min
101     
102     # Attempt to find the first character that differs
103     while sameSoFar && (index < length_of_shorter_name)
104       char1 = last_name1[index].chr
105       char2 = last_name2[index].chr
106       
107       sameSoFar = (char1 == char2)
108       index += 1
109     end
110     
111     # Form the category name
112     if sameSoFar and (index < last_name1.length)
113       # There is at least one character remaining in the first name
114       alpha_categories[i] << last_name1[0,index+1]
115       alpha_categories[i+1] << last_name2[0, index]
116     elsif sameSoFar and (index < last_name2.length)
117       # There is at least one character remaining in the second name
118       alpha_categories[i] << last_name1[0,index]
119       alpha_categories[i+1] << last_name2[0, index+1]
120     else
121       alpha_categories[i] << last_name1[0, index]
122       alpha_categories[i+1] << last_name2[0, index]
123     end
124     
125     return alpha_categories
126   end
127   
128   # An algorithm for determining the category names for alphabetical pagination
129   def alpha_paginate(all_grade_entry_students, per_page, total_pages)
130     alpha_categories = Array.new(2 * total_pages){[]}
131     alpha_pagination = []
132 
133     if total_pages == 0
134       return alpha_pagination
135     end
136     
137     i = 0
138     (1..(total_pages - 1)).each do |page|  
139       grade_entry_students1 = all_grade_entry_students.paginate(:per_page => per_page, :page => page)
140       grade_entry_students2 = all_grade_entry_students.paginate(:per_page => per_page, :page => page+1)
141       
142       # To figure out the category names, we need to keep track of the first and last students 
143       # on a particular page and the first student on the next page. For example, if these
144       # names are "Alwyn, Anderson, and Antheil", the category for this page would be:
145       # "Al-And".
146       first_student = grade_entry_students1.first.last_name
147       last_student = grade_entry_students1.last.last_name
148       next_student = grade_entry_students2.first.last_name
149       
150       # Update the possible categories
151       alpha_categories = self.construct_alpha_category(first_student, last_student, 
152                                                        alpha_categories, i)
153       alpha_categories = self.construct_alpha_category(last_student, next_student, 
154                                                        alpha_categories, i+1)
155       
156       i += 2
157     end
158     
159     # Handle the last page
160     page = total_pages
161     grade_entry_students = all_grade_entry_students.paginate(:per_page => per_page, :page => page)
162     first_student = grade_entry_students.first.last_name
163     last_student = grade_entry_students.last.last_name
164     
165     alpha_categories = self.construct_alpha_category(first_student, last_student, alpha_categories, i)
166  
167     # We can now form the category names
168     j=0
169     (1..total_pages).each do |i| 
170       alpha_pagination << (alpha_categories[j].max + "-" + alpha_categories[j+1].max)
171       j += 2
172     end
173       
174     return alpha_pagination
175   end
176   
177   # Get a CSV report of the grades for this grade entry form
178   def get_csv_grades_report
179     students = Student.all(:conditions => {:hidden => false}, :order => "user_name")
180     csv_string = FasterCSV.generate do |csv|
181       
182       # The first row in the CSV file will contain the question names
183       final_result = []
184       final_result.push('')
185       grade_entry_items.each do |grade_entry_item|
186         final_result.push(grade_entry_item.name)
187       end
188       csv << final_result
189       
190       # The second row in the CSV file will contain the question totals
191       final_result = []
192       final_result.push('')
193       grade_entry_items.each do |grade_entry_item|
194         final_result.push(grade_entry_item.out_of)
195       end
196       csv << final_result
197       
198       # The rest of the rows in the CSV file will contain the students' grades 
199       students.each do |student|
200         final_result = []
201         final_result.push(student.user_name)
202         grade_entry_student = self.grade_entry_students.find_by_user_id(student.id)
203 
204         # Check whether or not we have grades recorded for this student
205         if grade_entry_student.nil?
206           self.grade_entry_items.each do |grade_entry_item|
207             # Blank marks for each question
208             final_result.push(BLANK_MARK)
209           end
210           # Blank total percent
211           final_result.push(BLANK_MARK)
212         else
213           self.grade_entry_items.each do |grade_entry_item|
214             grade = grade_entry_student.grades.find_by_grade_entry_item_id(grade_entry_item.id)
215             if grade.nil?
216               final_result.push(BLANK_MARK)
217             else
218               final_result.push(grade.grade || BLANK_MARK)
219             end 
220           end
221           total_percent = self.calculate_total_percent(student.id)
222           final_result.push(total_percent)
223         end
224         csv << final_result
225       end
226     end
227     return csv_string
228   end
229   
230   # Parse a grade entry form CSV file.
231   # grades_file is the CSV file to be parsed
232   # grade_entry_form is the grade entry form that is being updated
233   # invalid_lines will store all problematic lines from the CSV file
234   def self.parse_csv(grades_file, grade_entry_form, invalid_lines)
235     num_updates = 0
236     num_lines_read = 0
237     names = []
238     totals = []
239     
240     # Parse the question names
241     FasterCSV.parse(grades_file.readline) do |row|
242       if !FasterCSV.generate_line(row).strip.empty?
243         names = row
244         num_lines_read += 1
245       end
246     end
247     
248     # Parse the question totals
249     FasterCSV.parse(grades_file.readline) do |row|
250       if !FasterCSV.generate_line(row).strip.empty?
251         totals = row
252         num_lines_read += 1
253       end
254     end
255 
256     # Create/update the grade entry items
257     begin
258       GradeEntryItem.create_or_update_from_csv_rows(names, totals, grade_entry_form)
259       num_updates += 1
260     rescue RuntimeError => e
261       invalid_lines << names.join(',') 
262       invalid_lines << totals.join(',') + ": " + e.message unless invalid_lines.nil?
263     end
264 
265     # Parse the grades 
266     FasterCSV.parse(grades_file.read) do |row|
267       next if FasterCSV.generate_line(row).strip.empty?
268       begin
269         if num_lines_read > 1
270           GradeEntryStudent.create_or_update_from_csv_row(row, grade_entry_form)
271           num_updates += 1
272         end
273         num_lines_read += 1
274       rescue RuntimeError => e
275         invalid_lines << row.join(',') + ": " + e.message unless invalid_lines.nil?
276       end
277     end
278     return num_updates
279   end
280 
281 end

Generated on Wed Sep 08 00:10:30 -0400 2010 with rcov 0.9.8