| Class | Assignment |
| In: |
app/models/assignment.rb
|
| Parent: | ActiveRecord::Base |
| MARKING_SCHEME_TYPE | = | { :flexible => 'flexible', :rubric => 'rubric' |
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)
# 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
# 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
# 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
# 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
# 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.
# 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
# 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
# File app/models/assignment.rb, line 132
132: def display_for_note
133: return short_identifier
134: end
# 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
# 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
# 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
# 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
# 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
# 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
# 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
# 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
# File app/models/assignment.rb, line 396
396: def invalid_groupings
397: return groupings - valid_groupings
398: end
# 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
# 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
# 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?
# File app/models/assignment.rb, line 63
63: def past_due_date?
64: return !due_date.nil? && Time.now > due_date
65: end
# 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
# 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
# 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
# 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
# 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
# File app/models/assignment.rb, line 405
405: def unassigned_groupings
406: return groupings - assigned_groupings
407: end
# 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.
# 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
# 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
# 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