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 282 194
63.83%
58.25%

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

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