Class Assignment
In: app/models/assignment.rb
Parent: ActiveRecord::Base

Methods

Constants

MARKING_SCHEME_TYPE = { :flexible => 'flexible', :rubric => 'rubric'

Public Instance methods

Add a group and corresponding grouping as provided in the passed in Array. Format: [ groupname, repo_name, member, member, etc ] Any member names that do not exist in the database will simply be ignored (This makes it possible to have empty groups created from a bad csv row)

[Source]

     # File app/models/assignment.rb, line 292
292:   def add_csv_group(row)
293:     return if row.length == 0
294: 
295:     # Note: We cannot use find_or_create_by here, because it has its own

296:     # save semantics. We need to set and save attributes in a very particular

297:     # order, so that everything works the way we want it to.

298:     group = Group.find_by_group_name(row[0])
299:     if group.nil?
300:       group = Group.new
301:       group.group_name = row[0]
302:     end
303: 
304:     # Since repo_name of "group" will be set before the first save call, the

305:     # set repo_name will be used instead of the autogenerated name. See

306:     # set_repo_name and build_repository in the groups model. Also, see

307:     # create_group_for_working_alone_student in the students model for

308:     # similar semantics.

309:     if is_candidate_for_setting_custom_repo_name?(row)
310:       # Do this only if user_name exists and is a student.

311:       if !Student.find_by_user_name(row[2]).nil?
312:         group.repo_name = row[0]
313:       else
314:         # Student name does not exist, use provided repo_name

315:         group.repo_name = row[1].strip # remove whitespace

316:       end
317:     end
318: 
319:     # If we are not repository admin, set the repository name as provided

320:     # in the csv upload file

321:     if !group.repository_admin?
322:       group.repo_name = row[1].strip # remove whitespace

323:     end
324:     # Note: after_create hook build_repository might raise

325:     # Repository::RepositoryCollision. If it does, it adds the colliding

326:     # repo_name to errors.on_base. This is how we can detect repo

327:     # collisions here. Unfortunately, we can't raise an exception

328:     # here, because we still want the grouping to be created. This really

329:     # shouldn't happen anyway, because the lookup earlier should prevent

330:     # repo collisions e.g. when uploading the same CSV file twice.

331:     group.save
332:     if !group.errors.on_base.nil?
333:       collision_error = I18n.t("csv.repo_collision_warning",
334:                           { :repo_name => group.errors.on_base,
335:                             :group_name => row[0] })
336:     end
337: 
338:     # Create a new Grouping for this assignment and the newly

339:     # crafted group

340:     grouping = Grouping.new(:assignment => self, :group => group)
341:     grouping.save
342: 
343:     # Form groups

344:     start_index_group_members = 2 # first field is the group-name, second the repo name, so start at field 3

345:     (start_index_group_members..(row.length - 1)).each do |i|
346:       student = Student.find_by_user_name(row[i].strip) # remove whitespace

347:       if !student.nil?
348:         if (grouping.student_membership_number == 0)
349:           # Add first valid member as inviter to group.

350:           grouping.group_id = group.id
351:           grouping.save # grouping has to be saved, before we can add members

352:           grouping.add_member(student, StudentMembership::STATUSES[:inviter])
353:         else
354:           grouping.add_member(student)
355:         end
356:       end
357:       
358:     end
359:     return collision_error
360:   end

[Source]

     # File app/models/assignment.rb, line 205
205:   def add_group(new_group_name=nil)
206:     if self.group_name_autogenerated
207:       group = Group.new
208:       group.save(false)
209:       group.group_name = group.get_autogenerated_group_name
210:       group.save
211:     else
212:       return nil if new_group_name.nil?
213:       if Group.find(:first, :conditions => {:group_name => new_group_name})
214:         group = Group.find(:first, :conditions => {:group_name =>       new_group_name})
215:         if !self.groupings.find_by_group_id(group.id).nil?
216:           raise "Group #{new_group_name} already exists"
217:         end
218:       else
219:         group = Group.new
220:         group.group_name = new_group_name
221:         group.save
222:       end
223:     end
224:     grouping = Grouping.new
225:     grouping.group = group
226:     grouping.assignment = self
227:     grouping.save
228:     return grouping
229:   end

[Source]

     # File app/models/assignment.rb, line 400
400:   def assigned_groupings
401:     return groupings.all(:joins => :ta_memberships, :include => [{:ta_memberships => :user}]).uniq
402: 
403:   end

Make a list of the students an inviter can invite for his grouping TODO check if this method is ever used anywhere [Not used anywhere as of 2010/03/30] TODO unit tests

[Source]

     # File app/models/assignment.rb, line 139
139:   def can_invite_for(gid)
140:     grouping = Grouping.find(gid)
141:     students = self.no_grouping_students_list
142:     students_list = []
143:     students.each do |s|
144:       if !grouping.pending?(s)
145:         # if assignment doesn't restrict groups member per sections

146:         if !self.section_groups_only
147:           students_list.push(s)
148:         else
149:           # if assignment restricts groupmembers per section

150:           if student.section == grouping.inviter.section
151:             students_list.push(s)
152:           end
153:         end
154:       end
155:     end
156:     return students_list
157:   end

Clones the Groupings from the assignment with id assignment_id into self. Destroys any previously existing Groupings associated with this Assignment

[Source]

     # File app/models/assignment.rb, line 246
246:   def clone_groupings_from(assignment_id)
247:     original_assignment = Assignment.find(assignment_id)
248:     self.transaction do
249:       self.group_min = original_assignment.group_min
250:       self.group_max = original_assignment.group_max
251:       self.student_form_groups = original_assignment.student_form_groups
252:       self.group_name_autogenerated = original_assignment.group_name_autogenerated
253:       self.group_name_displayed = original_assignment.group_name_displayed
254:       self.groupings.destroy_all
255:       self.save
256:       self.reload
257:       original_assignment.groupings.each do |g|
258:         unhidden_student_memberships = g.accepted_student_memberships.select do |m|
259:           !m.user.hidden
260:         end
261:         unhidden_ta_memberships = g.ta_memberships.select do |m|
262:           !m.user.hidden
263:         end
264:         #create the memberships for any user that is not hidden

265:         if !unhidden_student_memberships.empty?
266:           #create the groupings

267:           grouping = Grouping.new
268:           grouping.group_id = g.group_id
269:           grouping.assignment_id = self.id
270:           grouping.admin_approved = g.admin_approved
271:           raise "Could not save grouping" if !grouping.save
272:           all_memberships = unhidden_student_memberships + unhidden_ta_memberships
273:           all_memberships.each do |m|
274:             membership = Membership.new
275:             membership.user_id = m.user_id
276:             membership.type = m.type
277:             membership.membership_status = m.membership_status
278:             raise "Could not save membership" if !(grouping.memberships << membership)
279:           end
280:           # Ensure all student members have permissions on their group repositories

281:           grouping.update_repository_permissions
282:         end
283:       end
284:     end
285:   end

Create all the groupings for an assignment where students don‘t work in groups.

[Source]

     # File app/models/assignment.rb, line 234
234:   def create_groupings_when_students_work_alone
235:      @students = Student.find(:all)
236:      for student in @students do
237:        if !student.has_accepted_grouping_for?(self.id)
238:         student.create_group_for_working_alone_student(self.id)
239:        end
240:      end
241:   end

[Source]

     # File app/models/assignment.rb, line 517
517:   def criteria_count
518:     if self.marking_scheme_type == 'rubric'
519:        return self.rubric_criteria.size
520:     else
521:        return self.flexible_criteria.size
522:     end
523:   end

[Source]

     # File app/models/assignment.rb, line 132
132:   def display_for_note
133:     return short_identifier
134:   end

[Source]

     # File app/models/assignment.rb, line 509
509:   def get_criteria
510:     if self.marking_scheme_type == 'rubric'
511:        return self.rubric_criteria
512:     else
513:        return self.flexible_criteria
514:     end
515:   end

Get a detailed CSV report of marks (includes each criterion) for this assignment

[Source]

     # File app/models/assignment.rb, line 452
452:   def get_detailed_csv_report
453:     out_of = self.total_mark
454:     students = Student.all
455:     rubric_criteria = self.rubric_criteria
456:     csv_string = FasterCSV.generate do |csv|
457:       students.each do |student|
458:         final_result = []
459:         final_result.push(student.user_name)
460:         grouping = student.accepted_grouping_for(self.id)
461:         if grouping.nil? || !grouping.has_submission?
462:           final_result.push('')
463:           rubric_criteria.each do |rubric_criterion|
464:             final_result.push('')
465:             final_result.push(rubric_criterion.weight)
466:           end
467:           final_result.push('')
468:           final_result.push('')
469:         else
470:           submission = grouping.current_submission_used
471:           final_result.push(submission.result.total_mark / out_of * 100)
472:           rubric_criteria.each do |rubric_criterion|
473:             mark = submission.result.marks.find_by_markable_id_and_markable_type(rubric_criterion.id, "RubricCriterion")
474:             if mark.nil?
475:               final_result.push('')
476:             else
477:               final_result.push(mark.mark || '')
478:             end
479:             final_result.push(rubric_criterion.weight)
480:           end
481:           final_result.push(submission.result.get_total_extra_points)
482:           final_result.push(submission.result.get_total_extra_percentage)
483:         end
484:         # push grace credits info

485:         grace_credits_data = student.remaining_grace_credits.to_s + "/" + student.grace_credits.to_s
486:         final_result.push(grace_credits_data)
487: 
488:         csv << final_result
489:       end
490:     end
491:     return csv_string
492:   end

Get a simple CSV report of marks for this assignment

[Source]

     # File app/models/assignment.rb, line 431
431:   def get_simple_csv_report
432:     students = Student.all
433:     out_of = self.total_mark
434:     csv_string = FasterCSV.generate do |csv|
435:        students.each do |student|
436:          final_result = []
437:          final_result.push(student.user_name)
438:          grouping = student.accepted_grouping_for(self.id)
439:          if grouping.nil? || !grouping.has_submission?
440:            final_result.push('')
441:          else
442:            submission = grouping.current_submission_used
443:            final_result.push(submission.result.total_mark / out_of * 100)
444:          end
445:          csv << final_result
446:        end
447:     end
448:     return csv_string
449:   end

Get a list of subversion client commands to be used for scripting

[Source]

     # File app/models/assignment.rb, line 410
410:   def get_svn_export_commands
411:     svn_commands = [] # the commands to be exported

412:     self.submissions.each do |submission|
413:       grouping = submission.grouping
414:       svn_commands.push("svn export -r #{submission.revision_number} #{grouping.group.repository_external_access_url}/#{self.repository_folder} \"#{grouping.group.group_name}\"")
415:     end
416:     return svn_commands
417:   end

Get a list of group_name, repo-url pairs

[Source]

     # File app/models/assignment.rb, line 420
420:   def get_svn_repo_list
421:     string = FasterCSV.generate do |csv|
422:       self.groupings.each do |grouping|
423:         group = grouping.group
424:         csv << [group.group_name,group.repository_external_access_url]
425:       end
426:     end
427:     return string
428:   end

Return true if this is a group assignment; false otherwise

[Source]

     # File app/models/assignment.rb, line 99
 99:   def group_assignment?
100:     instructor_form_groups || group_min != 1 || group_max > 1
101:   end

Returns the group by the user for this assignment. If pending=true, it will return the group that the user has a pending invitation to. Returns nil if user does not have a group for this assignment, or if it is not a group assignment

[Source]

     # File app/models/assignment.rb, line 107
107:   def group_by(uid, pending=false)
108:     return nil unless group_assignment?
109: 
110:     # condition = "memberships.user_id = ?"

111:     # condition += " and memberships.status != 'rejected'"

112:     # add non-pending status clause to condition

113:     # condition += " and memberships.status != 'pending'" unless pending

114:     # groupings.find(:first, :include => :memberships, :conditions => [condition, uid]) #FIXME: needs schema update

115: 
116:     #FIXME: needs to be rewritten using a proper query...

117:     return User.find(uid).accepted_grouping_for(self.id)
118:   end

[Source]

     # File app/models/assignment.rb, line 374
374:   def grouped_students
375:     result_students = []
376:     student_memberships.each do |student_membership|
377:       result_students.push(student_membership.user)
378:     end
379:     return result_students
380:   end

[Source]

     # File app/models/assignment.rb, line 396
396:   def invalid_groupings
397:     return groupings - valid_groupings
398:   end

[Source]

     # File app/models/assignment.rb, line 505
505:   def next_criterion_position
506:     return self.rubric_criteria.size + 1
507:   end

Make a list of students without any groupings

[Source]

     # File app/models/assignment.rb, line 121
121:   def no_grouping_students_list
122:    @students = Student.all(:order => :last_name, :conditions => {:hidden => false})
123:    @students_list = []
124:    @students.each do |s|
125:      if !s.has_accepted_grouping_for?(self.id)
126:        @students_list.push(s)
127:       end
128:    end
129:    return @students_list
130:   end

[Source]

    # File app/models/assignment.rb, line 67
67:   def past_collection_date?
68:     return Time.now > submission_rule.calculate_collection_time
69:   end

Are we past the due date for this assignment?

[Source]

    # File app/models/assignment.rb, line 63
63:   def past_due_date?
64:     return !due_date.nil? && Time.now > due_date
65:   end

[Source]

     # File app/models/assignment.rb, line 494
494:   def replace_submission_rule(new_submission_rule)
495:     if self.submission_rule.nil?
496:       self.submission_rule = new_submission_rule
497:       self.save
498:     else
499:       self.submission_rule.destroy
500:       self.submission_rule = new_submission_rule
501:       self.save
502:     end
503:   end

calculates the average of released results for this assignment

[Source]

     # File app/models/assignment.rb, line 172
172:   def set_results_average
173:     groupings = Grouping.find_all_by_assignment_id(self.id)
174:     results_count = 0
175:     results_sum = 0
176:     groupings.each do |grouping|
177:       submission = grouping.current_submission_used
178:       if !submission.nil? && submission.has_result?
179:         result = submission.result
180:         if result.released_to_students
181:           results_sum += result.total_mark
182:           results_count += 1
183:         end
184:       end
185:     end
186:     if results_count == 0
187:       return false # no marks released for this assignment

188:     end
189:     # Need to avoid divide by zero

190:     if results_sum == 0
191:       self.results_average = 0
192:       return self.save
193:     end
194:     avg_quantity = results_sum / results_count
195:     # compute average in percent

196:     self.results_average = (avg_quantity * 100 / self.total_mark)
197:     self.save
198:   end

Returns a Submission instance for this user depending on whether this assignment is a group or individual assignment

[Source]

    # File app/models/assignment.rb, line 73
73:   def submission_by(user) #FIXME: needs schema updates

74: 
75:     # submission owner is either an individual (user) or a group

76:     owner = self.group_assignment? ? self.group_by(user.id) : user
77:     return nil unless owner
78: 
79:     # create a new submission for the owner

80:     # linked to this assignment, if it doesn't exist yet

81: 
82:     # submission = owner.submissions.find_or_initialize_by_assignment_id(id)

83:     # submission.save if submission.new_record?

84:     # return submission

85: 
86: 
87:     assignment_groupings = user.active_groupings.delete_if {|grouping|
88:       grouping.assignment.id != self.id
89:     }
90: 
91:     unless assignment_groupings.empty?
92:       return assignment_groupings.first.submissions.first
93:     else
94:       return nil
95:     end
96:   end

[Source]

     # File app/models/assignment.rb, line 200
200:   def total_criteria_weight
201:     factor = 10.0 ** 2
202:     return (rubric_criteria.sum('weight') * factor).floor / factor
203:   end

[Source]

     # File app/models/assignment.rb, line 159
159:   def total_mark
160:     total = 0
161:     if self.marking_scheme_type == 'rubric'
162:       rubric_criteria.each do |criterion|
163:         total = total + criterion.weight * 4
164:       end
165:     else
166:       total = flexible_criteria.sum('max')
167:     end
168:     return total
169:   end

[Source]

     # File app/models/assignment.rb, line 405
405:   def unassigned_groupings
406:     return groupings - assigned_groupings
407:   end

[Source]

     # File app/models/assignment.rb, line 382
382:   def ungrouped_students
383:     Student.all(:conditions => {:hidden => false}) - grouped_students
384:   end

Updates repository permissions for all groupings of an assignment. This is a handy method, if for example grouping creation/deletion gets rolled back. The rollback does not reestablish proper repository permissions.

[Source]

     # File app/models/assignment.rb, line 366
366:   def update_repository_permissions_forall_groupings
367:     # IMPORTANT: need to reload from DB

368:     self.reload
369:     groupings.each do |grouping|
370:       grouping.update_repository_permissions
371:     end
372:   end

[Source]

     # File app/models/assignment.rb, line 386
386:   def valid_groupings
387:     result = []
388:     groupings.all(:include => [{:student_memberships => :user}]).each do |grouping|
389:       if grouping.admin_approved || grouping.student_memberships.count >= group_min
390:         result.push(grouping)
391:       end
392:     end
393:     return result
394:   end

[Source]

    # File app/models/assignment.rb, line 53
53:   def validate
54:     if (group_max && group_min) && group_max < group_min
55:       errors.add(:group_max, "must be greater than the minimum number of groups")
56:     end
57:     if Time.zone.parse(due_date.to_s).nil?
58:       errors.add :due_date, 'is not a valid date'
59:     end
60:   end

[Validate]