Rcov C0 Coverage Information - RCov

app/models/rubric_criterion.rb

Name Total Lines Lines of Code Total Coverage Code Coverage
app/models/rubric_criterion.rb 250 170
64.00%
49.41%

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 require 'csv'
3 
4 class RubricCriterion < ActiveRecord::Base
5   before_save :round_weight
6   set_table_name "rubric_criteria" # set table name correctly
7   belongs_to  :assignment, :counter_cache => true
8   has_many    :marks, :as => :markable, :dependent => :destroy
9   has_many :criterion_ta_associations, :as => :criterion, :dependent => :destroy
10   has_many :tas, :through => :criterion_ta_associations
11   validates_associated  :assignment, :on => :create
12   validates_uniqueness_of :rubric_criterion_name, :scope => :assignment_id
13   validates_presence_of :rubric_criterion_name, :weight, :assignment_id,
14     :assigned_groups_count
15   validates_numericality_of :assignment_id, :only_integer => true, :greater_than => 0
16   validates_numericality_of :weight, :assigned_groups_count
17   validate_on_update :validate_total_weight
18 
19   before_validation :update_assigned_groups_count
20 
21   def update_assigned_groups_count
22     result = []
23     criterion_ta_associations.each do |cta|
24       result = result.concat(cta.ta.get_groupings_by_assignment(assignment))
25     end
26     self.assigned_groups_count = result.uniq.length
27   end
28 
29   def validate_total_weight
30     errors.add(:assignment, I18n.t("rubric_criteria.error_total")) if self.assignment.total_mark + (4 * (self.weight - self.weight_was)) <= 0
31   end
32 
33   # Just a small effort here to remove magic numbers...
34   RUBRIC_LEVELS = 5
35   DEFAULT_WEIGHT = 1.0
36   DEFAULT_LEVELS = [
37     {'name'=> I18n.t("rubric_criteria.defaults.level_0"), 'description'=> I18n.t("rubric_criteria.defaults.description_0")},
38     {'name'=> I18n.t("rubric_criteria.defaults.level_1"), 'description'=> I18n.t("rubric_criteria.defaults.description_1")},
39     {'name'=> I18n.t("rubric_criteria.defaults.level_2"), 'description'=> I18n.t("rubric_criteria.defaults.description_2")},
40     {'name'=> I18n.t("rubric_criteria.defaults.level_3"), 'description'=> I18n.t("rubric_criteria.defaults.description_3")},
41     {'name'=> I18n.t("rubric_criteria.defaults.level_4"), 'description'=> I18n.t("rubric_criteria.defaults.description_4")}
42   ]
43 
44   def mark_for(result_id)
45     return marks.find_by_result_id(result_id)
46   end
47 
48   def set_default_levels
49     DEFAULT_LEVELS.each_with_index do |level, index|
50       self['level_' + index.to_s + '_name'] = level['name']
51       self['level_' + index.to_s + '_description'] = level['description']
52     end
53   end
54 
55   # Set all the level names at once and saves the object.
56   #
57   # ===Params:
58   #
59   # levels::  An array containing every level name. A rubric criterion contains
60   #           RUBRIC_LEVELS levels. If the array is smaller, only the first levels
61   #           are set. If the array is bigger, higher indexes are ignored.
62   #
63   # ===Returns:
64   #
65   # Wether the save operation was successful or not.
66   def set_level_names(levels)
67     levels.each_with_index do |level, index|
68       self['level_' + index.to_s + '_name'] = level
69     end
70     save
71   end
72 
73   # Create a CSV string from all the rubric criteria related to an assignment.
74   #
75   # ===Returns:
76   #
77   # A string. See create_or_update_from_csv_row for format reference.
78   def self.create_csv(assignment)
79     csv_string = FasterCSV.generate do |csv|
80       assignment.rubric_criteria.each do |criterion|
81         criterion_array = [criterion.rubric_criterion_name,criterion.weight]
82         (0..RUBRIC_LEVELS - 1).each do |i|
83           criterion_array.push(criterion['level_' + i.to_s + '_name'])
84         end
85         (0..RUBRIC_LEVELS - 1).each do |i|
86           criterion_array.push(criterion['level_' + i.to_s + '_description'])
87         end
88         csv << criterion_array
89       end
90     end
91     return csv_string
92   end
93 
94   # Instantiate a RubricCriterion from a CSV row and attach it to the supplied
95   # assignment.
96   #
97   # ===Params:
98   #
99   # row::         An array representing one CSV file row. Should be in the following
100   #               format: [name, weight, _names_, _descriptions_] where the _names_ part
101   #               must contain RUBRIC_LEVELS elements representing the name of each
102   #               level and the _descriptions_ part (optional) can contain up to
103   #               RUBRIC_LEVELS description (one for each level).
104   # assignment::  The assignment to which the newly created criterion should belong.
105   #
106   # ===Raises:
107   #
108   # RuntimeError If the row does not contains enough information, if the weight value
109   #                           is zero (or doesn't evaluate to a float)
110   def self.create_or_update_from_csv_row(row, assignment)
111     if row.length < RUBRIC_LEVELS + 2
112       raise I18n.t('criteria_csv_error.incomplete_row')
113     end
114     working_row = row.clone
115     rubric_criterion_name = working_row.shift
116     # If a RubricCriterion of the same name exits, load it up.  Otherwise,
117     # create a new one.
118     criterion = assignment.rubric_criteria.find_or_create_by_rubric_criterion_name(rubric_criterion_name)
119     #Check that the weight is not a string.
120     begin
121       criterion.weight = Float(working_row.shift)
122     rescue ArgumentError => e
123       raise I18n.t('criteria_csv_error.weight_not_number')
124     end
125     # Only set the position if this is a new record.
126     if criterion.new_record?
127       criterion.position = assignment.next_criterion_position
128     end
129     # next comes the level names.
130     (0..RUBRIC_LEVELS-1).each do |i|
131       criterion['level_' + i.to_s + '_name'] = working_row.shift
132     end
133     # the rest of the values are level descriptions.
134     working_row.each_with_index do |desc, i|
135       criterion['level_' + i.to_s + '_description'] = desc
136     end
137     if !criterion.save
138       raise RuntimeError.new(criterion.errors)
139     end
140     return criterion
141   end
142 
143   # Parse a rubric criteria CSV file.
144   #
145   # ===Params:
146   #
147   # file::          A file object which will be tried for parsing.
148   # assignment::    The assignment to which the new criteria should belong to.
149   # invalid_lines:: An object to recieve all encountered _invalid_ lines.
150   #                 Strings representing the faulty line followed by
151   #                 a human readable error message are appended to the object
152   #                 via the << operator.
153   #
154   #                 *Hint*: An array allows for an easy
155   #                 access of single invalid lines.
156   # ===Returns:
157   #
158   # The number of successfully created criteria.
159   def self.parse_csv(file, assignment, invalid_lines)
160     nb_updates = 0
161     FasterCSV.parse(file.read) do |row|
162       next if FasterCSV.generate_line(row).strip.empty?
163       begin
164         RubricCriterion.create_or_update_from_csv_row(row, assignment)
165         nb_updates += 1
166       rescue RuntimeError => e
167         invalid_lines << row.join(',') + ": " + e.message unless invalid_lines.nil?
168       end
169     end
170     return nb_updates
171   end
172 
173   def get_weight
174     return self.weight
175   end
176 
177   def round_weight
178     factor = 10.0 ** 3
179     self.weight = (self.weight * factor).round.to_f / factor
180   end
181 
182   def all_assigned_groups
183     result = []
184     tas.each do |ta|
185       result = result.concat(ta.get_groupings_by_assignment(assignment))
186     end
187     return result.uniq
188   end
189 
190   def add_tas(ta_array)
191     ta_array = Array(ta_array)
192     associations = criterion_ta_associations.all(:conditions => {:ta_id => ta_array})
193     ta_array.each do |ta|
194       # & is the mathematical set intersection operator between two arrays
195       if (ta.criterion_ta_associations & associations).size < 1
196         criterion_ta_associations.create(:ta => ta, :criterion => self, :assignment => self.assignment)
197       end
198     end
199   end
200 
201 
202   def get_name
203     return rubric_criterion_name
204   end
205 
206   def remove_tas(ta_array)
207     ta_array = Array(ta_array)
208     associations_for_criteria = criterion_ta_associations.all(:conditions => {:ta_id => ta_array})
209     ta_array.each do |ta|
210       # & is the mathematical set intersection operator between two arrays
211       assoc_to_remove = (ta.criterion_ta_associations & associations_for_criteria)
212       if assoc_to_remove.size > 0
213         criterion_ta_associations.delete(assoc_to_remove)
214         assoc_to_remove.first.destroy
215       end
216     end
217   end
218 
219   def get_ta_names
220     return criterion_ta_associations.collect {|association| association.ta.user_name}
221   end
222 
223   def has_associated_ta?(ta)
224     if !ta.ta?
225       return false
226     end
227     return !(criterion_ta_associations.find_by_ta_id(ta.id) == nil)
228   end
229 
230   def add_tas_by_user_name_array(ta_user_name_array)
231     result = ta_user_name_array.map{|ta_user_name|
232       Ta.find_by_user_name(ta_user_name)}.compact
233     add_tas(result)
234   end
235   
236   # Returns an array containing the criterion names that didn't exist
237   def self.assign_tas_by_csv(csv_file_contents, assignment_id)
238     failures = []
239     FasterCSV.parse(csv_file_contents) do |row|
240       criterion_name = row.shift # Knocks the first item from array
241       criterion = RubricCriterion.find_by_assignment_id_and_rubric_criterion_name(assignment_id, criterion_name)
242       if criterion.nil?
243         failures.push(criterion_name)
244       else
245         criterion.add_tas_by_user_name_array(row) # The rest of the array
246       end
247     end
248     return failures
249   end
250 end

Generated on Thu Sep 09 00:10:35 -0400 2010 with rcov 0.9.8