def setMark(self, grade, entered_by, details=True): """ Set the mark of the group members """ super(GroupActivityMark, self).setMark(grade) #assign mark for each member in the group group_members = GroupMember.objects.filter( group=self.group, activity=self.numeric_activity, confirmed=True) entered_by = get_entry_person(entered_by) for g_member in group_members: try: ngrade = NumericGrade.objects.get( activity=self.numeric_activity, member=g_member.student) except NumericGrade.DoesNotExist: ngrade = NumericGrade(activity=self.numeric_activity, member=g_member.student) ngrade.value = grade or decimal.Decimal(0) if grade is None: ngrade.flag = 'NOGR' else: ngrade.flag = 'GRAD' if details: ngrade.save(entered_by=entered_by, mark=self, group=self.group) else: # this is just a placeholder for a number-only mark ngrade.save(entered_by=entered_by, mark=None, group=self.group)
def setMark(self, grade, entered_by, details=True): """ Set the mark of the group members """ super(GroupActivityMark, self).setMark(grade) #assign mark for each member in the group group_members = GroupMember.objects.filter(group=self.group, activity=self.numeric_activity, confirmed=True) entered_by = get_entry_person(entered_by) for g_member in group_members: try: ngrade = NumericGrade.objects.get(activity=self.numeric_activity, member=g_member.student) except NumericGrade.DoesNotExist: ngrade = NumericGrade(activity=self.numeric_activity, member=g_member.student) ngrade.value = grade ngrade.flag = 'GRAD' if details: ngrade.save(entered_by=entered_by, mark=self, group=self.group) else: # this is just a placeholder for a number-only mark ngrade.save(entered_by=entered_by, mark=None, group=self.group)
def automark_all(self, user: User) -> int: """ Fill in marking for any QuestionVersions that support it. Return number marked. """ versions = QuestionVersion.objects.filter( question__quiz=self, question__status='V').select_related('question') activity_components = self.activitycomponents_by_question() member_component_results = [ ] # : List[Tuple[Member, ActivityComponentMark]] for v in versions: member_component_results.extend( v.automark_all(activity_components=activity_components)) # Now the ugly work: combine the just-automarked components with any existing manual marking, and save... old_sam_lookup = { # dict to find old StudentActivityMarks sam.numeric_grade.member: sam for sam in StudentActivityMark.objects.filter( activity=self.activity).order_by('created_at').select_related( 'numeric_grade__member').prefetch_related( 'activitycomponentmark_set') } # dict to find old ActivityComponentMarks old_acm_by_component_id = defaultdict( dict) # : Dict[int, Dict[Member, ActivityComponentMark]] old_sam = StudentActivityMark.objects.filter(activity=self.activity).order_by('created_at') \ .select_related('numeric_grade__member').prefetch_related('activitycomponentmark_set') for sam in old_sam: for acm in sam.activitycomponentmark_set.all(): old_acm_by_component_id[acm.activity_component_id][ sam.numeric_grade.member] = acm numeric_grade_lookup = { # dict to find existing NumericGrades ng.member: ng for ng in NumericGrade.objects.filter( activity=self.activity).select_related('member') } all_components = set( ActivityComponent.objects.filter( numeric_activity_id=self.activity_id, deleted=False)) member_component_results.sort( key=lambda pair: pair[0].id) # ... get Members grouped together n_marked = 0 for member, member_acms in itertools.groupby(member_component_results, lambda pair: pair[0]): # Get a NumericGrade to work with try: ngrade = numeric_grade_lookup[member] except KeyError: ngrade = NumericGrade(activity_id=self.activity_id, member=member, flag='NOGR') ngrade.save(newsitem=False, entered_by=None, is_temporary=True) # Create ActivityMark to save under am = StudentActivityMark(numeric_grade=ngrade, activity_id=self.activity_id, created_by=user.username) old_am = old_sam_lookup.get(member) if old_am: am.overall_comment = old_am.overall_comment am.late_penalty = old_am.late_penalty am.mark_adjustment = old_am.mark_adjustment am.mark_adjustment_reason = old_am.mark_adjustment_reason am.save() # Find/create ActivityComponentMarks for each component auto_acm_lookup = { acm.activity_component: acm for _, acm in member_acms } any_missing = False acms = [] for c in all_components: # For each ActivityComponent, find one of # (1) just-auto-marked ActivityComponentMark, # (2) ActivityComponentMark from previous manual marking, # (3) nothing. if c in auto_acm_lookup: # (1) acm = auto_acm_lookup[c] acm.activity_mark = am n_marked += 1 elif c.id in old_acm_by_component_id and member in old_acm_by_component_id[ c.id]: # (2) old_acm = old_acm_by_component_id[c.id][member] acm = ActivityComponentMark(activity_mark=am, activity_component=c, value=old_acm.value, comment=old_acm.comment) else: # (3) acm = ActivityComponentMark(activity_mark=am, activity_component=c, value=None, comment=None) any_missing = True acm.save() acms.append(acm) if not any_missing: total = am.calculated_mark(acms) ngrade.value = total ngrade.flag = 'GRAD' am.mark = total else: ngrade.value = 0 ngrade.flag = 'NOGR' am.mark = None ngrade.save(newsitem=False, entered_by=user.username) am.save() return n_marked
def activity_marks_from_JSON(activity, userid, data, save=False): """ Build ActivityMark and ActivityComponentMark objects from imported JSON data. Since validating the input involves almost all of the work of saving the data, this function handles both. It is called once from is_valid with save==False to check everything, and again with save==True to actually do the work. Redundant yes, but it lets is_valid actually do its job without side effects. """ if not isinstance(data, dict): raise ValidationError('Outer JSON data structure must be an object.') if 'marks' not in data: raise ValidationError('Outer JSON data object must contain key "marks".') if not isinstance(data['marks'], list): raise ValidationError('Value for "marks" must be a list.') # All the ActivityMark and ActivityComponentMark objects get built here: # we basically have to do this work to validate anyway. components = ActivityComponent.objects.filter(numeric_activity_id=activity.id, deleted=False) components = dict((ac.slug, ac) for ac in components) found = set() not_found = set() combine = False # are we combining these marks with existing (as opposed to overwriting)? if 'combine' in data and bool(data['combine']): combine = True for markdata in data['marks']: if not isinstance(markdata, dict): raise ValidationError('Elements of array must be JSON objects.') # build the ActivityMark object and populate as much as possible for now. if activity.group and 'group' in markdata: # GroupActivityMark try: group = Group.objects.get(slug=markdata['group'], courseoffering=activity.offering) except Group.DoesNotExist: not_found.add(markdata['group']) continue am = GroupActivityMark(activity_id=activity.id, numeric_activity_id=activity.id, group=group, created_by=userid) recordid = markdata['group'] elif 'userid' in markdata: # StudentActivityMark try: member = Member.objects.get(person__userid=markdata['userid'], offering=activity.offering, role="STUD") except Member.DoesNotExist: not_found.add(markdata['userid']) continue am = StudentActivityMark(activity_id=activity.id, created_by=userid) recordid = markdata['userid'] else: raise ValidationError('Must specify "userid" or "group" for mark.') # check for duplicates in import if recordid in found: raise ValidationError('Duplicate marks for "%s".' % (recordid)) found.add(recordid) if combine: # if we're being asked to combine with old marks, get the old one (if exists) try: if activity.group: old_am = get_group_mark(activity, group) else: old_am = get_activity_mark_for_student(activity, member) except NumericGrade.DoesNotExist: old_am = None acms = [] # ActivityComponentMarks we will create for am # build ActivityComponentMarks found_comp_slugs = set() mark_total = 0 late_percent = decimal.Decimal(0) mark_penalty = decimal.Decimal(0) mark_penalty_reason = "" overall_comment = "" file_filename = None file_data = None file_mediatype = None # Added for the special case where we have a numeric mark only, without components. This can happen when # using the "mark for all groups/users" form. the_mark = decimal.Decimal(0) if combine and old_am: late_percent = old_am.late_penalty mark_penalty = old_am.mark_adjustment mark_penalty_reason = old_am.mark_adjustment_reason overall_comment = old_am.overall_comment for slug in markdata: # handle special-case slugs (that don't represent MarkComponents) if slug in ['userid', 'group']: continue elif slug == 'the_mark': try: the_mark = decimal.Decimal(str(markdata[slug])) except decimal.InvalidOperation: pass continue elif slug=="late_percent": try: late_percent = decimal.Decimal(str(markdata[slug])) except decimal.InvalidOperation: raise ValidationError('Value for "late_percent" must be numeric in record for "%s".' % (recordid)) continue elif slug=="mark_penalty": try: mark_penalty = decimal.Decimal(str(markdata[slug])) except decimal.InvalidOperation: raise ValidationError('Value for "mark_penalty" must be numeric in record for "%s".' % (recordid)) continue elif slug=="mark_penalty_reason": mark_penalty_reason = str(markdata[slug]) continue elif slug=="overall_comment": overall_comment = str(markdata[slug]) continue elif slug=="attach_type": file_mediatype = str(markdata[slug]) continue elif slug=="attach_filename": file_filename = str(markdata[slug]) continue elif slug=="attach_data": try: file_data = base64.b64decode(markdata[slug]) except TypeError: raise ValidationError('Invalid base64 file data for "%s"' % (recordid)) continue # handle MarkComponents if slug in components and slug not in found_comp_slugs: comp = components[slug] found_comp_slugs.add(slug) elif slug in components: # shouldn't happen because JSON lib forces unique keys, but let's be extra safe... raise ValidationError('Multiple values given for "%s" in record for "%s".' % (slug, recordid)) else: raise ValidationError('Mark component "%s" not found in record for "%s".' % (slug, recordid)) cm = ActivityComponentMark(activity_component=comp) acms.append(cm) # can't set activity_mark yet since it doesn't have an id componentdata = markdata[slug] if not isinstance(componentdata, dict): raise ValidationError('Mark component data must be JSON object (in "%s" for "%s").' % (slug, recordid)) if 'mark' not in componentdata: raise ValidationError('Must give "mark" for "%s" in record for "%s".' % (comp.title, recordid)) try: value = decimal.Decimal(str(componentdata['mark'])) except decimal.InvalidOperation: raise ValidationError('Value for "mark" must be numeric for "%s" in record for "%s".' % (comp.title, recordid)) cm.value = value mark_total += float(componentdata['mark']) if 'comment' in componentdata and save: cm.comment = str(componentdata['comment']) if 'display_raw' in componentdata and save: cm.set_display_raw(bool(componentdata['display_raw'])) # In the case of combined gradings, we have to get the value from old components to add to it. if combine: for slug in set(components.keys()) - found_comp_slugs: # handle missing components cm = ActivityComponentMark(activity_component=components[slug]) acms.append(cm) # can't set activity_mark yet since it doesn't have an id if old_am: old_cm = ActivityComponentMark.objects.get(activity_mark=old_am, activity_component=components[slug]) if old_cm.value is not None: mark_total += float(old_cm.value) cm.value = old_cm.value cm.comment = old_cm.comment cm.set_display_raw(old_cm.display_raw()) # handle file attachment if file_filename or file_data or file_mediatype: # new attachment if not (file_filename and file_data and file_mediatype): raise ValidationError('Must specify all or none of "attach_type", "attach_filename", "attach_data" in record for "%s"' % (recordid)) am.file_mediatype = file_mediatype if save: am.file_attachment.save(name=file_filename, content=ContentFile(file_data), save=False) elif combine and old_am: # recycle old am.file_attachment = old_am.file_attachment am.file_mediatype = old_am.file_mediatype else: # none am.file_attachment = None am.file_mediatype = None am.late_penalty = late_percent am.mark_adjustment = mark_penalty am.mark_adjustment_reason = mark_penalty_reason am.overall_comment = overall_comment mark_total = the_mark or ((1-late_percent/decimal.Decimal(100)) * (decimal.Decimal(str(mark_total)) - mark_penalty)) # put the total mark and numeric grade objects in place am.mark = mark_total value = mark_total if isinstance(am, StudentActivityMark): grades = NumericGrade.objects.filter(activity_id=activity.id, member=member) if grades: numeric_grade = grades[0] numeric_grade.flag = "GRAD" else: numeric_grade = NumericGrade(activity_id=activity.id, member=member, flag="GRAD") numeric_grade.value = value if save: numeric_grade.save(entered_by=userid) am.numeric_grade = numeric_grade else: group_members = GroupMember.objects.filter(group=group, activity_id=activity.id, confirmed=True) for g_member in group_members: try: ngrade = NumericGrade.objects.get(activity_id=activity.id, member=g_member.student) except NumericGrade.DoesNotExist: ngrade = NumericGrade(activity_id=activity.id, member=g_member.student) ngrade.value = value ngrade.flag = 'GRAD' if save: ngrade.save(entered_by=userid) if save: am.save() for cm in acms: cm.activity_mark = am cm.save() return found, not_found
def activity_marks_from_JSON(activity, userid, data): """ Build ActivityMark and ActivityComponentMark objects from imported JSON data. Return three lists: all ActivityMarks and all ActivityComponentMark and all NumericGrades *all not yet saved*. """ if not isinstance(data, dict): raise ValidationError(u'Outer JSON data structure must be an object.') if 'marks' not in data: raise ValidationError( u'Outer JSON data object must contain key "marks".') if not isinstance(data['marks'], list): raise ValidationError(u'Value for "marks" must be a list.') # All the ActivityMark and ActivityComponentMark objects get built here: # we basically have to do this work to validate anyway. components = ActivityComponent.objects.filter(numeric_activity=activity, deleted=False) components = dict((ac.slug, ac) for ac in components) activity_marks = [] activity_component_marks = [] numeric_grades = [] found = set() combine = False # are we combining these marks with existing (as opposed to overwriting)? if 'combine' in data and bool(data['combine']): combine = True for markdata in data['marks']: if not isinstance(markdata, dict): raise ValidationError(u'Elements of array must be JSON objects.') # build the ActivityMark object and populate as much as possible for now. if activity.group and 'group' in markdata: # GroupActivityMark try: group = Group.objects.get(slug=markdata['group'], courseoffering=activity.offering) except Group.DoesNotExist: raise ValidationError(u'Group with id "%s" not found.' % (markdata['group'])) am = GroupActivityMark(activity=activity, numeric_activity=activity, group=group, created_by=userid) recordid = markdata['group'] elif 'userid' in markdata: # StudentActivityMark try: member = Member.objects.get(person__userid=markdata['userid'], offering=activity.offering, role="STUD") except Member.DoesNotExist: raise ValidationError(u'Userid %s not in course.' % (markdata['userid'])) am = StudentActivityMark(activity=activity, created_by=userid) recordid = markdata['userid'] else: raise ValidationError( u'Must specify "userid" or "group" for mark.') # check for duplicates in import if recordid in found: raise ValidationError(u'Duplicate marks for "%s".' % (recordid)) found.add(recordid) if combine: # if we're being asked to combine with old marks, get the old one (if exists) try: if activity.group: old_am = get_group_mark(activity, group) else: old_am = get_activity_mark_for_student(activity, member) except NumericGrade.DoesNotExist: old_am = None activity_marks.append(am) # build ActivityComponentMarks found_comp_slugs = set() mark_total = 0 late_percent = decimal.Decimal(0) mark_penalty = decimal.Decimal(0) mark_penalty_reason = "" overall_comment = "" file_filename = None file_data = None file_mediatype = None if combine and old_am: late_percent = old_am.late_penalty mark_penalty = old_am.mark_adjustment mark_penalty_reason = old_am.mark_adjustment_reason overall_comment = old_am.overall_comment for slug in markdata: # handle special-case slugs (that don't represent MarkComponents) if slug in ['userid', 'group']: continue elif slug == "late_percent": try: late_percent = decimal.Decimal(str(markdata[slug])) except decimal.InvalidOperation: raise ValidationError( u'Value for "late_percent" must be numeric in record for "%s".' % (recordid)) continue elif slug == "mark_penalty": try: mark_penalty = decimal.Decimal(str(markdata[slug])) except decimal.InvalidOperation: raise ValidationError( u'Value for "mark_penalty" must be numeric in record for "%s".' % (recordid)) continue elif slug == "mark_penalty_reason": mark_penalty_reason = unicode(markdata[slug]) continue elif slug == "overall_comment": overall_comment = unicode(markdata[slug]) continue elif slug == "attach_type": file_mediatype = str(markdata[slug]) continue elif slug == "attach_filename": file_filename = unicode(markdata[slug]) continue elif slug == "attach_data": try: file_data = base64.b64decode(markdata[slug]) except TypeError: raise ValidationError('Invalid base64 file data for "%s"' % (recordid)) continue # handle MarkComponents if slug in components and slug not in found_comp_slugs: comp = components[slug] found_comp_slugs.add(slug) elif slug in components: # shouldn't happend because JSON lib forces unique keys, but let's be extra safe... raise ValidationError( u'Multiple values given for "%s" in record for "%s".' % (slug, recordid)) else: raise ValidationError( u'Mark component "%s" not found in record for "%s".' % (slug, recordid)) cm = ActivityComponentMark(activity_mark=am, activity_component=comp) activity_component_marks.append(cm) componentdata = markdata[slug] if not isinstance(componentdata, dict): raise ValidationError( u'Mark component data must be JSON object (in "%s" for "%s").' % (slug, recordid)) if 'mark' not in componentdata: raise ValidationError( u'Must give "mark" for "%s" in record for "%s".' % (comp.title, recordid)) try: value = decimal.Decimal(str(componentdata['mark'])) except decimal.InvalidOperation: raise ValidationError( u'Value for "mark" must be numeric for "%s" in record for "%s".' % (comp.title, recordid)) cm.value = value mark_total += float(componentdata['mark']) if 'comment' in componentdata: cm.comment = unicode(componentdata['comment']) for slug in set(components.keys()) - found_comp_slugs: # handle missing components cm = ActivityComponentMark(activity_mark=am, activity_component=components[slug]) activity_component_marks.append(cm) if combine and old_am: old_cm = ActivityComponentMark.objects.get( activity_mark=old_am, activity_component=components[slug]) cm.value = old_cm.value cm.comment = old_cm.comment mark_total += float(cm.value) else: cm.value = decimal.Decimal(0) cm.comment = '' # handle file attachment if file_filename or file_data or file_mediatype: # new attachment if not (file_filename and file_data and file_mediatype): raise ValidationError( u'Must specify all or none of "attach_type", "attach_filename", "attach_data" in record for "%s"' % (recordid)) am.file_attachment.save(name=file_filename, content=ContentFile(file_data), save=False) am.file_mediatype = file_mediatype elif combine and old_am: # recycle old am.file_attachment = old_am.file_attachment am.file_mediatype = old_am.file_mediatype else: # none am.file_attachment = None am.file_mediatype = None am.late_penalty = late_percent am.mark_adjustment = mark_penalty am.mark_adjustment_reason = mark_penalty_reason am.overall_comment = overall_comment mark_total = (1-late_percent/decimal.Decimal(100)) * \ (decimal.Decimal(str(mark_total)) - mark_penalty) # put the total mark and numeric grade objects in place am.mark = mark_total value = mark_total if isinstance(am, StudentActivityMark): grades = NumericGrade.objects.filter(activity=activity, member=member) if grades: numeric_grade = grades[0] numeric_grade.flag = "GRAD" else: numeric_grade = NumericGrade(activity=activity, member=member, flag="GRAD") numeric_grade.value = value am.numeric_grade = numeric_grade numeric_grades.append(numeric_grade) else: group_members = GroupMember.objects.filter(group=group, activity=activity, confirmed=True) for g_member in group_members: try: ngrade = NumericGrade.objects.get(activity=activity, member=g_member.student) except NumericGrade.DoesNotExist: ngrade = NumericGrade(activity=activity, member=g_member.student) ngrade.value = value ngrade.flag = 'GRAD' numeric_grades.append(ngrade) return (activity_marks, activity_component_marks, numeric_grades)
def activity_marks_from_JSON(activity, userid, data): """ Build ActivityMark and ActivityComponentMark objects from imported JSON data. Return three lists: all ActivityMarks and all ActivityComponentMark and all NumericGrades *all not yet saved*. """ if not isinstance(data, dict): raise ValidationError(u'Outer JSON data structure must be an object.') if 'marks' not in data: raise ValidationError(u'Outer JSON data object must contain key "marks".') if not isinstance(data['marks'], list): raise ValidationError(u'Value for "marks" must be a list.') # All the ActivityMark and ActivityComponentMark objects get built here: # we basically have to do this work to validate anyway. components = ActivityComponent.objects.filter(numeric_activity=activity, deleted=False) components = dict((ac.slug, ac) for ac in components) activity_marks = [] activity_component_marks = [] numeric_grades = [] found = set() combine = False # are we combining these marks with existing (as opposed to overwriting)? if 'combine' in data and bool(data['combine']): combine = True for markdata in data['marks']: if not isinstance(markdata, dict): raise ValidationError(u'Elements of array must be JSON objects.') # build the ActivityMark object and populate as much as possible for now. if activity.group and 'group' in markdata: # GroupActivityMark try: group = Group.objects.get(slug=markdata['group'], courseoffering=activity.offering) except Group.DoesNotExist: raise ValidationError(u'Group with id "%s" not found.' % (markdata['group'])) am = GroupActivityMark(activity=activity, numeric_activity=activity, group=group, created_by=userid) recordid = markdata['group'] elif 'userid' in markdata: # StudentActivityMark try: member = Member.objects.get(person__userid=markdata['userid'], offering=activity.offering, role="STUD") except Member.DoesNotExist: raise ValidationError(u'Userid %s not in course.' % (markdata['userid'])) am = StudentActivityMark(activity=activity, created_by=userid) recordid = markdata['userid'] else: raise ValidationError(u'Must specify "userid" or "group" for mark.') # check for duplicates in import if recordid in found: raise ValidationError(u'Duplicate marks for "%s".' % (recordid)) found.add(recordid) if combine: # if we're being asked to combine with old marks, get the old one (if exists) try: if activity.group: old_am = get_group_mark(activity, group) else: old_am = get_activity_mark_for_student(activity, member) except NumericGrade.DoesNotExist: old_am = None activity_marks.append(am) # build ActivityComponentMarks found_comp_slugs = set() mark_total = 0 late_percent = decimal.Decimal(0) mark_penalty = decimal.Decimal(0) mark_penalty_reason = "" overall_comment = "" file_filename = None file_data = None file_mediatype = None if combine and old_am: late_percent = old_am.late_penalty mark_penalty = old_am.mark_adjustment mark_penalty_reason = old_am.mark_adjustment_reason overall_comment = old_am.overall_comment for slug in markdata: # handle special-case slugs (that don't represent MarkComponents) if slug in ['userid', 'group']: continue elif slug=="late_percent": try: late_percent = decimal.Decimal(str(markdata[slug])) except decimal.InvalidOperation: raise ValidationError(u'Value for "late_percent" must be numeric in record for "%s".' % (recordid)) continue elif slug=="mark_penalty": try: mark_penalty = decimal.Decimal(str(markdata[slug])) except decimal.InvalidOperation: raise ValidationError(u'Value for "mark_penalty" must be numeric in record for "%s".' % (recordid)) continue elif slug=="mark_penalty_reason": mark_penalty_reason = unicode(markdata[slug]) continue elif slug=="overall_comment": overall_comment = unicode(markdata[slug]) continue elif slug=="attach_type": file_mediatype = str(markdata[slug]) continue elif slug=="attach_filename": file_filename = unicode(markdata[slug]) continue elif slug=="attach_data": try: file_data = base64.b64decode(markdata[slug]) except TypeError: raise ValidationError('Invalid base64 file data for "%s"' % (recordid)) continue # handle MarkComponents if slug in components and slug not in found_comp_slugs: comp = components[slug] found_comp_slugs.add(slug) elif slug in components: # shouldn't happend because JSON lib forces unique keys, but let's be extra safe... raise ValidationError(u'Multiple values given for "%s" in record for "%s".' % (slug, recordid)) else: raise ValidationError(u'Mark component "%s" not found in record for "%s".' % (slug, recordid)) cm = ActivityComponentMark(activity_mark=am, activity_component=comp) activity_component_marks.append(cm) componentdata = markdata[slug] if not isinstance(componentdata, dict): raise ValidationError(u'Mark component data must be JSON object (in "%s" for "%s").' % (slug, recordid)) if 'mark' not in componentdata: raise ValidationError(u'Must give "mark" for "%s" in record for "%s".' % (comp.title, recordid)) try: value = decimal.Decimal(str(componentdata['mark'])) except decimal.InvalidOperation: raise ValidationError(u'Value for "mark" must be numeric for "%s" in record for "%s".' % (comp.title, recordid)) cm.value = value mark_total += float(componentdata['mark']) if 'comment' in componentdata: cm.comment = unicode(componentdata['comment']) for slug in set(components.keys()) - found_comp_slugs: # handle missing components cm = ActivityComponentMark(activity_mark=am, activity_component=components[slug]) activity_component_marks.append(cm) if combine and old_am: old_cm = ActivityComponentMark.objects.get(activity_mark=old_am, activity_component=components[slug]) cm.value = old_cm.value cm.comment = old_cm.comment mark_total += float(cm.value) else: cm.value = decimal.Decimal(0) cm.comment = '' # handle file attachment if file_filename or file_data or file_mediatype: # new attachment if not (file_filename and file_data and file_mediatype): raise ValidationError(u'Must specify all or none of "attach_type", "attach_filename", "attach_data" in record for "%s"' % (recordid)) am.file_attachment.save(name=file_filename, content=ContentFile(file_data), save=False) am.file_mediatype = file_mediatype elif combine and old_am: # recycle old am.file_attachment = old_am.file_attachment am.file_mediatype = old_am.file_mediatype else: # none am.file_attachment = None am.file_mediatype = None am.late_penalty = late_percent am.mark_adjustment = mark_penalty am.mark_adjustment_reason = mark_penalty_reason am.overall_comment = overall_comment mark_total = (1-late_percent/decimal.Decimal(100)) * \ (decimal.Decimal(str(mark_total)) - mark_penalty) # put the total mark and numeric grade objects in place am.mark = mark_total value = mark_total if isinstance(am, StudentActivityMark): grades = NumericGrade.objects.filter(activity=activity, member=member) if grades: numeric_grade = grades[0] numeric_grade.flag = "GRAD" else: numeric_grade = NumericGrade(activity=activity, member=member, flag="GRAD") numeric_grade.value = value am.numeric_grade = numeric_grade numeric_grades.append(numeric_grade) else: group_members = GroupMember.objects.filter(group=group, activity=activity, confirmed=True) for g_member in group_members: try: ngrade = NumericGrade.objects.get(activity=activity, member=g_member.student) except NumericGrade.DoesNotExist: ngrade = NumericGrade(activity=activity, member=g_member.student) ngrade.value = value ngrade.flag = 'GRAD' numeric_grades.append(ngrade) return (activity_marks, activity_component_marks, numeric_grades)