| Name | Total Lines | Lines of Code | Total Coverage | Code Coverage |
|---|---|---|---|---|
| app/models/assignment.rb | 763 | 569 | 99.74%
|
99.65%
|
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 'csv_invalid_line_error' |
2 class Assignment < ActiveRecord::Base |
3 |
4 MARKING_SCHEME_TYPE = { |
5 :flexible => 'flexible', |
6 :rubric => 'rubric' |
7 } |
8 |
9 has_many :rubric_criteria, |
10 :class_name => "RubricCriterion", |
11 :order => :position |
12 has_many :flexible_criteria, |
13 :class_name => "FlexibleCriterion", |
14 :order => :position |
15 has_many :assignment_files |
16 has_many :test_files |
17 has_many :criterion_ta_associations |
18 has_one :submission_rule |
19 has_one :assignment_stat |
20 accepts_nested_attributes_for :submission_rule, :allow_destroy => true |
21 accepts_nested_attributes_for :assignment_files, :allow_destroy => true |
22 accepts_nested_attributes_for :test_files, :allow_destroy => true |
23 accepts_nested_attributes_for :assignment_stat, :allow_destroy => true |
24 |
25 has_many :annotation_categories |
26 |
27 has_many :groupings |
28 has_many :ta_memberships, |
29 :class_name => "TaMembership", |
30 :through => :groupings |
31 has_many :student_memberships, :through => :groupings |
32 has_many :tokens, :through => :groupings |
33 |
34 has_many :submissions, :through => :groupings |
35 has_many :groups, :through => :groupings |
36 |
37 has_many :notes, :as => :noteable, :dependent => :destroy |
38 |
39 has_many :section_due_dates |
40 accepts_nested_attributes_for :section_due_dates |
41 |
42 validates_associated :assignment_files |
43 |
44 validates_presence_of :repository_folder |
45 validates_presence_of :short_identifier, :group_min |
46 validates_uniqueness_of :short_identifier, :case_sensitive => true |
47 |
48 validates_numericality_of :group_min, |
49 :only_integer => true, |
50 :greater_than => 0 |
51 validates_numericality_of :group_max, :only_integer => true |
52 validates_numericality_of :tokens_per_day, |
53 :only_integer => true, |
54 :greater_than_or_equal_to => 0 |
55 |
56 validates_associated :submission_rule |
57 validates_associated :assignment_stat |
58 validates_presence_of :submission_rule |
59 |
60 validates_presence_of :marking_scheme_type |
61 |
62 # since allow_web_submits is a boolean, validates_presence_of does not work: |
63 # see the Rails API documentation for validates_presence_of (Model |
64 # validations) |
65 validates_inclusion_of :allow_web_submits, :in => [true, false] |
66 validates_inclusion_of :display_grader_names_to_students, :in => [true, false] |
67 validates_inclusion_of :enable_test, :in => [true, false] |
68 validates_inclusion_of :assign_graders_to_criteria, :in => [true, false] |
69 |
70 before_save :reset_collection_time |
71 validate :minimum_number_of_groups, :check_timezone |
72 after_save :update_assigned_tokens |
73 |
74 # Export a YAML formatted string created from the assignment rubric criteria. |
75 def export_rubric_criteria_yml |
76 criteria = self.rubric_criteria |
77 final = Hash.new |
78 criteria.each do |criterion| |
79 inner = ActiveSupport::OrderedHash.new |
80 inner["weight"] = criterion["weight"] |
81 inner["level_0"] = { |
82 "name"=> criterion["level_0_name"] , |
83 "description"=> criterion["level_0_description"] |
84 } |
85 inner["level_1"] = { |
86 "name"=> criterion["level_1_name"] , |
87 "description"=> criterion["level_1_description"] |
88 } |
89 inner["level_2"] = { |
90 "name"=> criterion["level_2_name"] , |
91 "description"=> criterion["level_2_description"] |
92 } |
93 inner["level_3"] = { |
94 "name"=> criterion["level_3_name"] , |
95 "description"=> criterion["level_3_description"] |
96 } |
97 inner["level_4"] = { |
98 "name"=> criterion["level_4_name"] , |
99 "description"=> criterion["level_4_description"] |
100 } |
101 criteria_yml = {"#{criterion["rubric_criterion_name"]}" => inner} |
102 final = final.merge(criteria_yml) |
103 end |
104 return final.to_yaml |
105 end |
106 |
107 def minimum_number_of_groups |
108 if (group_max && group_min) && group_max < group_min |
109 errors.add(:group_max, "must be greater than the minimum number of groups") |
110 return false |
111 end |
112 end |
113 |
114 def check_timezone |
115 if Time.zone.parse(due_date.to_s).nil? |
116 errors.add :due_date, 'is not a valid date' |
117 return false |
118 end |
119 end |
120 |
121 # Are we past all the due dates for this assignment? |
122 def past_due_date? |
123 # If no section due dates |
124 if !self.section_due_dates_type && self.section_due_dates.empty? |
125 return !due_date.nil? && Time.now > due_date |
126 # If section due dates |
127 else |
128 self.section_due_dates.each do |d| |
129 if !d.due_date.nil? && Time.now > d.due_date |
130 return true |
131 end |
132 end |
133 return false |
134 end |
135 end |
136 |
137 # Are we past the due date for this assignment, for this grouping ? |
138 def section_past_due_date?(grouping) |
139 if self.section_due_dates_type and !grouping.inviter.section.nil? |
140 section_due_date = |
141 SectionDueDate.due_date_for(grouping.inviter.section, self) |
142 return !section_due_date.nil? && Time.now > section_due_date |
143 else |
144 self.past_due_date? |
145 end |
146 end |
147 |
148 # return the due date for a section |
149 def section_due_date(section) |
150 if self.section_due_dates_type |
151 if !section.nil? |
152 return SectionDueDate.due_date_for(section, self) |
153 end |
154 end |
155 return self.due_date |
156 end |
157 |
158 # Calculate the latest due date. Used to calculate the collection time |
159 def latest_due_date |
160 if !self.section_due_dates_type |
161 return self.due_date |
162 else |
163 due_date = self.due_date |
164 self.section_due_dates.each do |d| |
165 if !d.due_date.nil? && due_date < d.due_date |
166 due_date = d.due_date |
167 end |
168 end |
169 return due_date |
170 end |
171 end |
172 |
173 def past_collection_date? |
174 return Time.now > submission_rule.calculate_collection_time |
175 end |
176 |
177 def past_remark_due_date? |
178 return !remark_due_date.nil? && Time.now > remark_due_date |
179 end |
180 |
181 # Returns a Submission instance for this user depending on whether this |
182 # assignment is a group or individual assignment |
183 def submission_by(user) #FIXME: needs schema updates |
184 |
185 # submission owner is either an individual (user) or a group |
186 owner = self.group_assignment? ? self.group_by(user.id) : user |
187 return nil unless owner |
188 |
189 # create a new submission for the owner |
190 # linked to this assignment, if it doesn't exist yet |
191 |
192 # submission = owner.submissions.find_or_initialize_by_assignment_id(id) |
193 # submission.save if submission.new_record? |
194 # return submission |
195 |
196 assignment_groupings = user.active_groupings.delete_if {|grouping| |
197 grouping.assignment.id != self.id |
198 } |
199 |
200 unless assignment_groupings.empty? |
201 return assignment_groupings.first.submissions.first |
202 else |
203 return nil |
204 end |
205 end |
206 |
207 # Return true if this is a group assignment; false otherwise |
208 def group_assignment? |
209 invalid_override || group_min != 1 || group_max > 1 |
210 end |
211 |
212 # Returns the group by the user for this assignment. If pending=true, |
213 # it will return the group that the user has a pending invitation to. |
214 # Returns nil if user does not have a group for this assignment, or if it is |
215 # not a group assignment |
216 def group_by(uid, pending=false) |
217 return nil unless group_assignment? |
218 |
219 # condition = "memberships.user_id = ?" |
220 # condition += " and memberships.status != 'rejected'" |
221 # add non-pending status clause to condition |
222 # condition += " and memberships.status != 'pending'" unless pending |
223 # groupings.find(:first, :include => :memberships, :conditions => [condition, uid]) #FIXME: needs schema update |
224 |
225 #FIXME: needs to be rewritten using a proper query... |
226 return User.find(uid).accepted_grouping_for(self.id) |
227 end |
228 |
229 # Make a list of students without any groupings |
230 def no_grouping_students_list |
231 @students = Student.all(:order => :last_name, :conditions => {:hidden => false}) |
232 @students_list = [] |
233 @students.each do |s| |
234 if !s.has_accepted_grouping_for?(self.id) |
235 @students_list.push(s) |
236 end |
237 end |
238 return @students_list |
239 end |
240 |
241 def display_for_note |
242 return short_identifier |
243 end |
244 |
245 def total_mark |
246 total = 0 |
247 if self.marking_scheme_type == 'rubric' |
248 rubric_criteria.each do |criterion| |
249 total = total + criterion.weight * 4 |
250 end |
251 else |
252 total = flexible_criteria.sum('max') |
253 end |
254 return total |
255 end |
256 |
257 # calculates the average of released results for this assignment |
258 def set_results_average |
259 groupings = Grouping.find_all_by_assignment_id(self.id) |
260 results_count = 0 |
261 results_sum = 0 |
262 groupings.each do |grouping| |
263 submission = grouping.current_submission_used |
264 if !submission.nil? |
265 if submission.has_result? && submission.remark_submitted? |
266 result = submission.remark_result |
267 elsif submission.has_result? |
268 result = submission.result |
269 end |
270 if result.released_to_students |
271 results_sum += result.total_mark |
272 results_count += 1 |
273 end |
274 end |
275 end |
276 if results_count == 0 |
277 return false # no marks released for this assignment |
278 end |
279 # Need to avoid divide by zero |
280 if results_sum == 0 |
281 self.results_average = 0 |
282 return self.save |
283 end |
284 avg_quantity = results_sum / results_count |
285 # compute average in percent |
286 self.results_average = (avg_quantity * 100 / self.total_mark) |
287 self.save |
288 end |
289 |
290 def total_criteria_weight |
291 factor = 10.0 ** 2 |
292 return (rubric_criteria.sum('weight') * factor).floor / factor |
293 end |
294 |
295 def add_group(new_group_name=nil) |
296 if self.group_name_autogenerated |
297 group = Group.new |
298 group.save(:validate => false) |
299 group.group_name = group.get_autogenerated_group_name |
300 group.save |
301 else |
302 return nil if new_group_name.nil? |
303 if Group.find(:first, :conditions => {:group_name => new_group_name}) |
304 group = Group.find(:first, :conditions => {:group_name => new_group_name}) |
305 if !self.groupings.find_by_group_id(group.id).nil? |
306 raise "Group #{new_group_name} already exists" |
307 end |
308 else |
309 group = Group.new |
310 group.group_name = new_group_name |
311 group.save |
312 end |
313 end |
314 grouping = Grouping.new |
315 grouping.group = group |
316 grouping.assignment = self |
317 grouping.save |
318 return grouping |
319 end |
320 |
321 |
322 # Create all the groupings for an assignment where students don't work |
323 # in groups. |
324 def create_groupings_when_students_work_alone |
325 @students = Student.find(:all) |
326 for student in @students do |
327 if !student.has_accepted_grouping_for?(self.id) |
328 student.create_group_for_working_alone_student(self.id) |
329 end |
330 end |
331 end |
332 |
333 # Clones the Groupings from the assignment with id assignment_id |
334 # into self. Destroys any previously existing Groupings associated |
335 # with this Assignment |
336 def clone_groupings_from(assignment_id) |
337 original_assignment = Assignment.find(assignment_id) |
338 self.transaction do |
339 self.group_min = original_assignment.group_min |
340 self.group_max = original_assignment.group_max |
341 self.student_form_groups = original_assignment.student_form_groups |
342 self.group_name_autogenerated = original_assignment.group_name_autogenerated |
343 self.group_name_displayed = original_assignment.group_name_displayed |
344 self.groupings.destroy_all |
345 self.save |
346 self.reload |
347 original_assignment.groupings.each do |g| |
348 unhidden_student_memberships = g.accepted_student_memberships.select do |m| |
349 !m.user.hidden |
350 end |
351 unhidden_ta_memberships = g.ta_memberships.select do |m| |
352 !m.user.hidden |
353 end |
354 #create the memberships for any user that is not hidden |
355 if !unhidden_student_memberships.empty? |
356 #create the groupings |
357 grouping = Grouping.new |
358 grouping.group_id = g.group_id |
359 grouping.assignment_id = self.id |
360 grouping.admin_approved = g.admin_approved |
361 raise "Could not save grouping" if !grouping.save |
362 all_memberships = unhidden_student_memberships + unhidden_ta_memberships |
363 all_memberships.each do |m| |
364 membership = Membership.new |
365 membership.user_id = m.user_id |
366 membership.type = m.type |
367 membership.membership_status = m.membership_status |
368 raise "Could not save membership" if !(grouping.memberships << membership) |
369 end |
370 # Ensure all student members have permissions on their group repositories |
371 grouping.update_repository_permissions |
372 end |
373 end |
374 end |
375 end |
376 |
377 # Add a group and corresponding grouping as provided in |
378 # the passed in Array. |
379 # Format: [ groupname, repo_name, member, member, etc ] |
380 # Any member names that do not exist in the database will simply be ignored |
381 # (This makes it possible to have empty groups created from a bad csv row) |
382 def add_csv_group(row) |
383 return if row.length == 0 |
384 |
385 # Note: We cannot use find_or_create_by here, because it has its own |
386 # save semantics. We need to set and save attributes in a very particular |
387 # order, so that everything works the way we want it to. |
388 group = Group.find_by_group_name(row[0]) |
389 if group.nil? |
390 group = Group.new |
391 group.group_name = row[0] |
392 end |
393 |
394 # Since repo_name of "group" will be set before the first save call, the |
395 # set repo_name will be used instead of the autogenerated name. See |
396 # set_repo_name and build_repository in the groups model. Also, see |
397 # create_group_for_working_alone_student in the students model for |
398 # similar semantics. |
399 if is_candidate_for_setting_custom_repo_name?(row) |
400 # Do this only if user_name exists and is a student. |
401 if !Student.find_by_user_name(row[2]).nil? |
402 group.repo_name = row[0] |
403 else |
404 # Student name does not exist, use provided repo_name |
405 group.repo_name = row[1].strip # remove whitespace |
406 end |
407 end |
408 |
409 # If we are not repository admin, set the repository name as provided |
410 # in the csv upload file |
411 if !group.repository_admin? |
412 group.repo_name = row[1].strip # remove whitespace |
413 end |
414 # Note: after_create hook build_repository might raise |
415 # Repository::RepositoryCollision. If it does, it adds the colliding |
416 # repo_name to errors.on_base. This is how we can detect repo |
417 # collisions here. Unfortunately, we can't raise an exception |
418 # here, because we still want the grouping to be created. This really |
419 # shouldn't happen anyway, because the lookup earlier should prevent |
420 # repo collisions e.g. when uploading the same CSV file twice. |
421 group.save |
422 if !group.errors[:base].blank? |
423 collision_error = I18n.t("csv.repo_collision_warning", |
424 { :repo_name => group.errors.on_base, |
425 :group_name => row[0] }) |
426 end |
427 |
428 # Create a new Grouping for this assignment and the newly |
429 # crafted group |
430 grouping = Grouping.new(:assignment => self, :group => group) |
431 grouping.save |
432 |
433 # Form groups |
434 start_index_group_members = 2 # first field is the group-name, second the repo name, so start at field 3 |
435 (start_index_group_members..(row.length - 1)).each do |i| |
436 student = Student.find_by_user_name(row[i].strip) # remove whitespace |
437 if !student.nil? |
438 if (grouping.student_membership_number == 0) |
439 # Add first valid member as inviter to group. |
440 grouping.group_id = group.id |
441 grouping.save # grouping has to be saved, before we can add members |
442 grouping.add_member(student, StudentMembership::STATUSES[:inviter]) |
443 else |
444 grouping.add_member(student) |
445 end |
446 end |
447 |
448 end |
449 return collision_error |
450 end |
451 |
452 # Updates repository permissions for all groupings of |
453 # an assignment. This is a handy method, if for example grouping |
454 # creation/deletion gets rolled back. The rollback does not |
455 # reestablish proper repository permissions. |
456 def update_repository_permissions_forall_groupings |
457 # IMPORTANT: need to reload from DB |
458 self.reload |
459 groupings.each do |grouping| |
460 grouping.update_repository_permissions |
461 end |
462 end |
463 |
464 def grouped_students |
465 result_students = [] |
466 student_memberships.each do |student_membership| |
467 result_students.push(student_membership.user) |
468 end |
469 return result_students |
470 end |
471 |
472 def ungrouped_students |
473 Student.all(:conditions => {:hidden => false}) - grouped_students |
474 end |
475 |
476 def valid_groupings |
477 result = [] |
478 groupings.all(:include => [{:student_memberships => :user}]).each do |grouping| |
479 if grouping.admin_approved || grouping.student_memberships.count >= group_min |
480 result.push(grouping) |
481 end |
482 end |
483 return result |
484 end |
485 |
486 def invalid_groupings |
487 return groupings - valid_groupings |
488 end |
489 |
490 def assigned_groupings |
491 return groupings.all(:joins => :ta_memberships, :include => [{:ta_memberships => :user}]).uniq |
492 end |
493 |
494 def unassigned_groupings |
495 return groupings - assigned_groupings |
496 end |
497 |
498 # Get a list of subversion client commands to be used for scripting |
499 def get_svn_export_commands |
500 svn_commands = [] # the commands to be exported |
501 self.submissions.each do |submission| |
502 grouping = submission.grouping |
503 svn_commands.push("svn export -r #{submission.revision_number} #{grouping.group.repository_external_access_url}/#{self.repository_folder} \"#{grouping.group.group_name}\"") |
504 end |
505 return svn_commands |
506 end |
507 |
508 # Get a list of group_name, repo-url pairs |
509 def get_svn_repo_list |
510 string = FasterCSV.generate do |csv| |
511 self.groupings.each do |grouping| |
512 group = grouping.group |
513 csv << [group.group_name,group.repository_external_access_url] |
514 end |
515 end |
516 return string |
517 end |
518 |
519 # Get a simple CSV report of marks for this assignment |
520 def get_simple_csv_report |
521 students = Student.all |
522 out_of = self.total_mark |
523 csv_string = FasterCSV.generate do |csv| |
524 students.each do |student| |
525 final_result = [] |
526 final_result.push(student.user_name) |
527 grouping = student.accepted_grouping_for(self.id) |
528 if grouping.nil? || !grouping.has_submission? |
529 final_result.push('') |
530 else |
531 submission = grouping.current_submission_used |
532 final_result.push(submission.result.total_mark / out_of * 100) |
533 end |
534 csv << final_result |
535 end |
536 end |
537 return csv_string |
538 end |
539 |
540 # Get a detailed CSV report of marks (includes each criterion) |
541 # for this assignment. Produces slightly different reports, depending |
542 # on which criteria type has been used the this assignment. |
543 def get_detailed_csv_report |
544 # which marking scheme do we have? |
545 if self.marking_scheme_type == MARKING_SCHEME_TYPE[:flexible] |
546 return get_detailed_csv_report_flexible |
547 else |
548 # default to rubric |
549 return get_detailed_csv_report_rubric |
550 end |
551 end |
552 |
553 # Get a detailed CSV report of rubric based marks |
554 # (includes each criterion) for this assignment. |
555 # Produces CSV rows such as the following: |
556 # student_name,95.22222,3,4,2,5,5,4,0/2 |
557 # Criterion values should be read in pairs. I.e. 2,3 means |
558 # a student scored 2 for a criterion with weight 3. |
559 # Last column are grace-credits. |
560 def get_detailed_csv_report_rubric |
561 out_of = self.total_mark |
562 students = Student.all |
563 rubric_criteria = self.rubric_criteria |
564 csv_string = FasterCSV.generate do |csv| |
565 students.each do |student| |
566 final_result = [] |
567 final_result.push(student.user_name) |
568 grouping = student.accepted_grouping_for(self.id) |
569 if grouping.nil? || !grouping.has_submission? |
570 # No grouping/no submission |
571 final_result.push('') # total percentage |
572 rubric_criteria.each do |rubric_criterion| |
573 final_result.push('') # mark |
574 final_result.push(rubric_criterion.weight) # weight |
575 end |
576 final_result.push('') # extra-mark |
577 final_result.push('') # extra-percentage |
578 else |
579 submission = grouping.current_submission_used |
580 final_result.push(submission.result.total_mark / out_of * 100) |
581 rubric_criteria.each do |rubric_criterion| |
582 mark = submission.result.marks.find_by_markable_id_and_markable_type(rubric_criterion.id, "RubricCriterion") |
583 if mark.nil? |
584 final_result.push('') |
585 else |
586 final_result.push(mark.mark || '') |
587 end |
588 final_result.push(rubric_criterion.weight) |
589 end |
590 final_result.push(submission.result.get_total_extra_points) |
591 final_result.push(submission.result.get_total_extra_percentage) |
592 end |
593 # push grace credits info |
594 grace_credits_data = student.remaining_grace_credits.to_s + "/" + student.grace_credits.to_s |
595 final_result.push(grace_credits_data) |
596 |
597 csv << final_result |
598 end |
599 end |
600 return csv_string |
601 end |
602 |
603 # Get a detailed CSV report of flexible criteria based marks |
604 # (includes each criterion, with it's out-of value) for this assignment. |
605 # Produces CSV rows such as the following: |
606 # student_name,95.22222,3,4,2,5,5,4,0/2 |
607 # Criterion values should be read in pairs. I.e. 2,3 means 2 out-of 3. |
608 # Last column are grace-credits. |
609 def get_detailed_csv_report_flexible |
610 out_of = self.total_mark |
611 students = Student.all |
612 flexible_criteria = self.flexible_criteria |
613 csv_string = FasterCSV.generate do |csv| |
614 students.each do |student| |
615 final_result = [] |
616 final_result.push(student.user_name) |
617 grouping = student.accepted_grouping_for(self.id) |
618 if grouping.nil? || !grouping.has_submission? |
619 # No grouping/no submission |
620 final_result.push('') # total percentage |
621 flexible_criteria.each do |criterion| ## empty criteria |
622 final_result.push('') # mark |
623 final_result.push(criterion.max) # out-of |
624 end |
625 final_result.push('') # extra-marks |
626 final_result.push('') # extra-percentage |
627 else |
628 # Fill in actual values, since we have a grouping |
629 # and a submission. |
630 submission = grouping.current_submission_used |
631 final_result.push(submission.result.total_mark / out_of * 100) |
632 flexible_criteria.each do |criterion| |
633 mark = submission.result.marks.find_by_markable_id_and_markable_type(criterion.id, "FlexibleCriterion") |
634 if mark.nil? |
635 final_result.push('') |
636 else |
637 final_result.push(mark.mark || '') |
638 end |
639 final_result.push(criterion.max) |
640 end |
641 final_result.push(submission.result.get_total_extra_points) |
642 final_result.push(submission.result.get_total_extra_percentage) |
643 end |
644 # push grace credits info |
645 grace_credits_data = student.remaining_grace_credits.to_s + "/" + student.grace_credits.to_s |
646 final_result.push(grace_credits_data) |
647 |
648 csv << final_result |
649 end |
650 end |
651 return csv_string |
652 end |
653 |
654 def replace_submission_rule(new_submission_rule) |
655 if self.submission_rule.nil? |
656 self.submission_rule = new_submission_rule |
657 self.save |
658 else |
659 self.submission_rule.destroy |
660 self.submission_rule = new_submission_rule |
661 self.save |
662 end |
663 end |
664 |
665 def next_criterion_position |
666 # We're using count here because this fires off a DB query, thus |
667 # grabbing the most up-to-date count of the rubric criteria. |
668 return self.rubric_criteria.count + 1 |
669 end |
670 |
671 def get_criteria |
672 if self.marking_scheme_type == 'rubric' |
673 return self.rubric_criteria |
674 else |
675 return self.flexible_criteria |
676 end |
677 end |
678 |
679 def criteria_count |
680 if self.marking_scheme_type == 'rubric' |
681 return self.rubric_criteria.size |
682 else |
683 return self.flexible_criteria.size |
684 end |
685 end |
686 |
687 # Returns an array with the number of groupings who scored between |
688 # certain percentage ranges [0-5%, 6-10%, ...] |
689 # intervals defaults to 20 |
690 def grade_distribution_as_percentage(intervals=20) |
691 distribution = Array.new(intervals, 0) |
692 out_of = self.total_mark |
693 |
694 if out_of == 0 |
695 return distribution |
696 end |
697 |
698 steps = 100 / intervals # number of percentage steps in each interval |
699 groupings = self.groupings.all(:include => [{:current_submission_used => :result}]) |
700 |
701 groupings.each do |grouping| |
702 submission = grouping.current_submission_used |
703 if !submission.nil? && submission.has_result? |
704 result = submission.result |
705 if result.marking_state == Result::MARKING_STATES[:complete] |
706 percentage = (result.total_mark / out_of * 100).ceil |
707 if percentage == 0 |
708 distribution[0] += 1 |
709 elsif percentage >= 100 |
710 distribution[intervals - 1] += 1 |
711 elsif (percentage % steps) == 0 |
712 distribution[percentage / steps - 1] += 1 |
713 else |
714 distribution[percentage / steps] += 1 |
715 end |
716 end |
717 end |
718 end # end of groupings loop |
719 |
720 return distribution |
721 end |
722 |
723 # Returns all the TAs associated with the assignment |
724 def tas |
725 ids = self.ta_memberships.map { |m| m.user_id } |
726 return Ta.find(ids) |
727 end |
728 |
729 # Returns all the submissions that have been graded |
730 def graded_submissions |
731 return self.submissions.select { |submission| submission.result.marking_state == Result::MARKING_STATES[:complete] } |
732 end |
733 |
734 private |
735 |
736 # Returns true if we are safe to set the repository name |
737 # to a non-autogenerated value. Called by add_csv_group. |
738 def is_candidate_for_setting_custom_repo_name?(row) |
739 # Repository name can be customized if |
740 # - this assignment is set up to allow external submits only |
741 # - group_max = 1 |
742 # - there's only one student member in this row of the csv and |
743 # - the group name is equal to the only group member |
744 if MarkusConfigurator.markus_config_repository_admin? && |
745 self.allow_web_submits == false && |
746 row.length == 3 && self.group_max == 1 && |
747 !row[2].blank? && row[0] == row[2] |
748 return true |
749 else |
750 return false |
751 end |
752 end |
753 |
754 def reset_collection_time |
755 submission_rule.reset_collection_time |
756 end |
757 |
758 def update_assigned_tokens |
759 self.tokens.each do |t| |
760 t.update_tokens(self.tokens_per_day_was, self.tokens_per_day) |
761 end |
762 end |
763 end |
Generated on Sun Feb 05 00:08:07 -0500 2012 with rcov 0.9.10