| Name | Total Lines | Lines of Code | Total Coverage | Code Coverage |
|---|---|---|---|---|
| app/models/flexible_criterion.rb | 211 | 141 | 96.21%
|
94.33%
|
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.
1 require 'fastercsv' |
2 require 'csv' |
3 # Represents a flexible criterion used to mark an assignment that |
4 # has the marking_scheme_type attribute set to 'flexible'. |
5 class FlexibleCriterion < ActiveRecord::Base |
6 |
7 set_table_name "flexible_criteria" # set table name correctly |
8 belongs_to :assignment, :counter_cache => true |
9 |
10 has_many :marks, :as => :markable, :dependent => :destroy |
11 |
12 has_many :criterion_ta_associations, |
13 :as => :criterion, |
14 :dependent => :destroy |
15 |
16 has_many :tas, :through => :criterion_ta_associations |
17 |
18 validates_associated :assignment, |
19 :message => 'association is not strong with an assignment' |
20 validates_uniqueness_of :flexible_criterion_name, |
21 :scope => :assignment_id, |
22 :message => 'is already taken' |
23 validates_presence_of :flexible_criterion_name, :assignment_id, :max |
24 validates_numericality_of :assignment_id, |
25 :only_integer => true, |
26 :greater_than => 0, |
27 :message => "can only be whole number greater than 0" |
28 validates_numericality_of :max, |
29 :message => "must be a number greater than 0.0", |
30 :greater_than => 0.0 |
31 |
32 # before_save :update_assigned_groups_count |
33 |
34 DEFAULT_MAX = 1 |
35 |
36 def update_assigned_groups_count |
37 result = [] |
38 tas.each do |ta| |
39 result = result.concat(ta.get_groupings_by_assignment(assignment)) |
40 end |
41 self.assigned_groups_count = result.uniq.length |
42 end |
43 |
44 # Creates a CSV string from all the flexible criteria related to an assignment. |
45 # |
46 # ===Returns: |
47 # |
48 # A string. see new_from_csv_row for format reference. |
49 def self.create_csv(assignment) |
50 csv_string = FasterCSV.generate do |csv| |
51 # TODO temporary until Assignment gets its criteria method |
52 criteria = FlexibleCriterion.find_all_by_assignment_id(assignment.id, :order => :position) |
53 criteria.each do |c| |
54 criterion_array = [c.flexible_criterion_name, c.max, c.description] |
55 csv << criterion_array; |
56 end |
57 end |
58 return csv_string |
59 end |
60 |
61 # Instantiate a FlexibleCriterion from a CSV row and attach it to the supplied |
62 # assignment. |
63 # |
64 # ===Params: |
65 # |
66 # row:: An array representing one CSV file row. Should be in the following |
67 # format: [name, max, description] where description is optional. |
68 # assignment:: The assignment to which the newly created criterion should belong. |
69 # |
70 # ===Raises: |
71 # |
72 # CSV::IllegalFormatError:: If the row does not contains enough information, if the max value |
73 # is zero (or doesn't evaluate to a float) or if the |
74 # supplied name is not unique. |
75 def self.new_from_csv_row(row, assignment) |
76 if row.length < 2 |
77 raise CSV::IllegalFormatError.new(I18n.t('criteria_csv_error.incomplete_row')) |
78 end |
79 criterion = FlexibleCriterion.new |
80 criterion.assignment = assignment |
81 criterion.flexible_criterion_name = row[0] |
82 # assert that no other criterion uses the same name for the same assignment. |
83 if (FlexibleCriterion.find_all_by_assignment_id_and_flexible_criterion_name(assignment.id, criterion.flexible_criterion_name).size != 0) |
84 raise CSV::IllegalFormatError.new(I18n.t('criteria_csv_error.name_not_unique')) |
85 end |
86 criterion.max = row[1] |
87 if (criterion.max == 0) |
88 raise CSV::IllegalFormatError.new(I18n.t('criteria_csv_error.max_zero')) |
89 end |
90 criterion.description = row[2] if !row[2].nil? |
91 criterion.position = next_criterion_position(assignment) |
92 if !criterion.save |
93 raise CSV::IllegalFormatError.new(criterion.errors) |
94 end |
95 return criterion |
96 end |
97 |
98 # Parse a flexible criteria CSV file. |
99 # |
100 # ===Params: |
101 # |
102 # file:: A file object which will be tried for parsing. |
103 # assignment:: The assignment to which the new criteria should belong to. |
104 # invalid_lines:: An object to recieve all encountered _invalid_ lines. |
105 # Strings representing the faulty line followed by |
106 # a human readable error message are appended to the object |
107 # via the << operator. |
108 # |
109 # *Hint*: An array allows for easy |
110 # access to single invalid lines. |
111 # |
112 # ===Returns: |
113 # |
114 # The number of successfully created criteria. |
115 def self.parse_csv(file, assignment, invalid_lines = nil) |
116 nb_updates = 0 |
117 FasterCSV.parse(file.read) do |row| |
118 next if FasterCSV.generate_line(row).strip.empty? |
119 begin |
120 FlexibleCriterion.new_from_csv_row(row, assignment) |
121 nb_updates += 1 |
122 rescue CSV::IllegalFormatError => e |
123 invalid_lines << row.join(',') + ": " + e.message unless invalid_lines.nil? |
124 end |
125 end |
126 return nb_updates |
127 end |
128 |
129 # ===Returns: |
130 # |
131 # The position that should receive the next criterion for an assignment. |
132 def self.next_criterion_position(assignment) |
133 # TODO temporary, until Assignment gets its criteria method |
134 # nevermind the fact that this computation should really belong in assignment |
135 last_criterion = FlexibleCriterion.find_last_by_assignment_id(assignment.id, :order => :position) |
136 return last_criterion.position + 1 unless last_criterion.nil? |
137 return 1 |
138 end |
139 |
140 def get_weight |
141 return 1 |
142 end |
143 |
144 def all_assigned_groups |
145 result = [] |
146 tas.each do |ta| |
147 result = result.concat(ta.get_groupings_by_assignment(assignment)) |
148 end |
149 return result.uniq |
150 end |
151 |
152 def add_tas(ta_array) |
153 ta_array = Array(ta_array) |
154 associations = criterion_ta_associations.all(:conditions => {:ta_id => ta_array}) |
155 ta_array.each do |ta| |
156 if (ta.criterion_ta_associations & associations).size < 1 |
157 criterion_ta_associations.create(:ta => ta, :criterion => self, :assignment => self.assignment) |
158 end |
159 end |
160 end |
161 |
162 def get_name |
163 return flexible_criterion_name |
164 end |
165 |
166 def remove_tas(ta_array) |
167 ta_array = Array(ta_array) |
168 associations_for_criteria = criterion_ta_associations.all(:conditions => {:ta_id => ta_array}) |
169 ta_array.each do |ta| |
170 # & is the mathematical set intersection operator between two arrays |
171 assoc_to_remove = (ta.criterion_ta_associations & associations_for_criteria) |
172 if assoc_to_remove.size > 0 |
173 criterion_ta_associations.delete(assoc_to_remove) |
174 assoc_to_remove.first.destroy |
175 end |
176 end |
177 end |
178 |
179 def get_ta_names |
180 return criterion_ta_associations.collect {|association| association.ta.user_name} |
181 end |
182 |
183 def has_associated_ta?(ta) |
184 if !ta.ta? |
185 return false |
186 end |
187 return !(criterion_ta_associations.find_by_ta_id(ta.id) == nil) |
188 end |
189 |
190 def add_tas_by_user_name_array(ta_user_name_array) |
191 result = ta_user_name_array.map{|ta_user_name| |
192 Ta.find_by_user_name(ta_user_name)}.compact |
193 add_tas(result) |
194 end |
195 |
196 # Returns an array containing the criterion names that didn't exist |
197 def self.assign_tas_by_csv(csv_file_contents, assignment_id) |
198 failures = [] |
199 FasterCSV.parse(csv_file_contents) do |row| |
200 criterion_name = row.shift # Knocks the first item from array |
201 criterion = FlexibleCriterion.find_by_assignment_id_and_flexible_criterion_name(assignment_id, criterion_name) |
202 if criterion.nil? |
203 failures.push(criterion_name) |
204 else |
205 criterion.add_tas_by_user_name_array(row) # The rest of the array |
206 end |
207 end |
208 return failures |
209 end |
210 |
211 end |
Generated on Tue Feb 07 00:07:36 -0500 2012 with rcov 0.9.10