Skip to content

Commit

Permalink
use days as rate instead of hours
Browse files Browse the repository at this point in the history
  • Loading branch information
jbau committed Mar 29, 2013
1 parent 35a8007 commit e5a39f8
Show file tree
Hide file tree
Showing 8 changed files with 48 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@
class Migration(SchemaMigration):

def forwards(self, orm):
# Adding field 'Exam.hourly_late_penalty'
db.add_column('c2g_exam', 'hourly_late_penalty',
# Adding field 'Exam.daily_late_penalty'
db.add_column('c2g_exam', 'daily_late_penalty',
self.gf('django.db.models.fields.FloatField')(default=0.0, null=True, blank=True),
keep_default=False)


def backwards(self, orm):
# Deleting field 'Exam.hourly_late_penalty'
db.delete_column('c2g_exam', 'hourly_late_penalty')
# Deleting field 'Exam.daily_late_penalty'
db.delete_column('c2g_exam', 'daily_late_penalty')


models = {
Expand Down Expand Up @@ -217,14 +217,14 @@ def backwards(self, orm):
'assessment_type': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}),
'autograde': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'course': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['c2g.Course']"}),
'daily_late_penalty': ('django.db.models.fields.FloatField', [], {'default': '0.0', 'null': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'display_single': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'due_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'exam_type': ('django.db.models.fields.CharField', [], {'default': "'exam'", 'max_length': '32'}),
'grace_period': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'grade_single': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'hide_grades': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'hourly_late_penalty': ('django.db.models.fields.FloatField', [], {'default': '0.0', 'null': 'True', 'blank': 'True'}),
'html_content': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'image': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['c2g.Exam']"}),
Expand Down
14 changes: 7 additions & 7 deletions main/c2g/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1922,7 +1922,7 @@ class Exam(TimestampMixin, Deletable, Stageable, Sortable, models.Model):
grace_period = models.DateTimeField(null=True, blank=True)
partial_credit_deadline = models.DateTimeField(null=True, blank=True)
late_penalty = models.IntegerField(default=0, null=True, blank=True)
hourly_late_penalty = models.FloatField(default=0.0, null=True, blank=True)
daily_late_penalty = models.FloatField(default=0.0, null=True, blank=True)
submissions_permitted = models.IntegerField(default=999, null=True, blank=True)
resubmission_penalty = models.IntegerField(default=0, null=True, blank=True)
autograde = models.BooleanField(default=False)
Expand Down Expand Up @@ -2103,7 +2103,7 @@ def create_ready_instance(self):
xml_imported = self.xml_imported,
partial_credit_deadline = self.partial_credit_deadline,
late_penalty = self.late_penalty,
hourly_late_penalty = self.hourly_late_penalty,
daily_late_penalty = self.daily_late_penalty,
submissions_permitted = self.submissions_permitted,
resubmission_penalty = self.resubmission_penalty,
autograde = self.autograde,
Expand Down Expand Up @@ -2157,8 +2157,8 @@ def commit(self, clone_fields = None):
ready_instance.partial_credit_deadline = self.partial_credit_deadline
if not clone_fields or 'late_penalty' in clone_fields:
ready_instance.late_penalty = self.late_penalty
if not clone_fields or 'hourly_late_penalty' in clone_fields:
ready_instance.hourly_late_penalty = self.hourly_late_penalty
if not clone_fields or 'daily_late_penalty' in clone_fields:
ready_instance.daily_late_penalty = self.daily_late_penalty
if not clone_fields or 'submissions_permitted' in clone_fields:
ready_instance.submissions_permitted = self.submissions_permitted
if not clone_fields or 'resubmission_penalty' in clone_fields:
Expand Down Expand Up @@ -2220,8 +2220,8 @@ def revert(self, clone_fields = None):
self.partial_credit_deadline = ready_instance.partial_credit_deadline
if not clone_fields or 'late_penalty' in clone_fields:
self.late_penalty = ready_instance.late_penalty
if not clone_fields or 'hourly_late_penalty' in clone_fields:
self.hourly_late_penalty = ready_instance.hourly_late_penalty
if not clone_fields or 'daily_late_penalty' in clone_fields:
self.daily_late_penalty = ready_instance.daily_late_penalty
if not clone_fields or 'submissions_permitted' in clone_fields:
self.submissions_permitted = ready_instance.submissions_permitted
if not clone_fields or 'resubmission_penalty' in clone_fields:
Expand Down Expand Up @@ -2283,7 +2283,7 @@ def is_synced(self):
return False
if self.late_penalty != self.image.late_penalty:
return False
if self.hourly_late_penalty != self.image.hourly_late_penalty:
if self.daily_late_penalty != self.image.daily_late_penalty:
return False
if self.submissions_permitted != self.image.submissions_permitted:
return False
Expand Down
16 changes: 8 additions & 8 deletions main/courses/exams/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -429,17 +429,17 @@ def test_resubmission_and_late_penalty(self):
self.assertTrue(self.float_compare(compute_penalties(100.0, 3, 15, True, 150), 0))
self.assertTrue(self.float_compare(compute_penalties(100.0, 3, 150, True, 50), 0))

def test_hourly_penalty(self):
def test_daily_penalty(self):
"""Unit test for the discount function """
#Only hourly penalty and late penalty
self.assertTrue(self.float_compare(compute_penalties(100, 1, 0, False, 0, late_hours=0, hourly_late_penalty=0), 100))
self.assertTrue(self.float_compare(compute_penalties(100.0, 1, 0, True, 0, late_hours=3, hourly_late_penalty=10), 100*.9*.9*.9))
self.assertTrue(self.float_compare(compute_penalties(100.0, 1, 0, True, 50, late_hours=3, hourly_late_penalty=10), 100*.5*.9*.9*.9))
self.assertTrue(self.float_compare(compute_penalties(100.0, 1, 0, False, 50, late_hours=3, hourly_late_penalty=10), 100))
self.assertTrue(self.float_compare(compute_penalties(100.0, 1, 0, True, 0, late_hours=3, hourly_late_penalty=110), 0))
#Only daily penalty and late penalty
self.assertTrue(self.float_compare(compute_penalties(100, 1, 0, False, 0, late_days=0, daily_late_penalty=0), 100))
self.assertTrue(self.float_compare(compute_penalties(100.0, 1, 0, True, 0, late_days=3, daily_late_penalty=10), 100*.9*.9*.9))
self.assertTrue(self.float_compare(compute_penalties(100.0, 1, 0, True, 50, late_days=3, daily_late_penalty=10), 100*.5*.9*.9*.9))
self.assertTrue(self.float_compare(compute_penalties(100.0, 1, 0, False, 50, late_days=3, daily_late_penalty=10), 100))
self.assertTrue(self.float_compare(compute_penalties(100.0, 1, 0, True, 0, late_days=3, daily_late_penalty=110), 0))

#all penalties
self.assertTrue(self.float_compare(compute_penalties(100.0, 3, 15, True, 50, late_hours=3, hourly_late_penalty=10), 100*.85*.85*.5*.9*.9*.9))
self.assertTrue(self.float_compare(compute_penalties(100.0, 3, 15, True, 50, late_days=3, daily_late_penalty=10), 100*.85*.85*.5*.9*.9*.9))

def test_regex_metadata_errors(self):
"""
Expand Down
28 changes: 14 additions & 14 deletions main/courses/exams/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -615,8 +615,8 @@ def collect_data(request, course_prefix, course_suffix, exam_slug):

#apply penalties
late_timedelta = max(datetime.timedelta(0), record.time_created - exam.grace_period)
hours_late = math.ceil(late_timedelta.total_seconds() / 3600)
record.score = compute_penalties(total_score, attempt_number, exam.resubmission_penalty, record.late, exam.late_penalty, late_hours=hours_late, hourly_late_penalty = exam.hourly_late_penalty)
days_late = math.ceil(late_timedelta.total_seconds() / (3600.0*24.0))
record.score = compute_penalties(total_score, attempt_number, exam.resubmission_penalty, record.late, exam.late_penalty, late_days=days_late, daily_late_penalty = exam.daily_late_penalty)
record.save()

#Set ExamScore.score to max of ExamRecord.score for that student, exam.
Expand All @@ -629,7 +629,7 @@ def collect_data(request, course_prefix, course_suffix, exam_slug):
return HttpResponse("Submission has been saved.")


def compute_penalties(raw_score, attempt_number, resubmission_penalty, is_late, late_penalty, late_hours=0, hourly_late_penalty=0):
def compute_penalties(raw_score, attempt_number, resubmission_penalty, is_late, late_penalty, late_days=0, daily_late_penalty=0):
"""Helper function to factor out resubmission and late penalty calculations,
so I can write a few unit tests for it
"""
Expand All @@ -638,9 +638,9 @@ def compute_penalties(raw_score, attempt_number, resubmission_penalty, is_late,
late_discount = max(0.0, 100.0 - late_penalty)/100.0
if is_late:
score *= late_discount
if late_hours:
hourly_discount = pow(max(0.0, (100.0 - hourly_late_penalty)/100.0), late_hours)
score *= hourly_discount
if late_days:
daily_discount = pow(max(0.0, (100.0 - daily_late_penalty)/100.0), late_days)
score *= daily_discount
return max(score, 0.0)

@require_POST
Expand Down Expand Up @@ -670,7 +670,7 @@ def save_exam_ajax(request, course_prefix, course_suffix, create_or_edit="create
grace_period = request.POST.get('grace_period', '')
partial_credit_deadline = request.POST.get('partial_credit_deadline', '')
late_penalty = request.POST.get('late_penalty', '')
hourly_late_penalty = request.POST.get('hourly_late_penalty', '')
daily_late_penalty = request.POST.get('daily_late_penalty', '')
num_subs_permitted = request.POST.get('num_subs_permitted','')
resubmission_penalty = request.POST.get('resubmission_penalty','')
assessment_type = request.POST.get('assessment_type','')
Expand Down Expand Up @@ -779,13 +779,13 @@ def save_exam_ajax(request, course_prefix, course_suffix, create_or_edit="create
except ValueError:
return HttpResponseBadRequest("A non-numeric late penalty (" + late_penalty + ") was provided")

if not hourly_late_penalty:
hlp = 0
if not daily_late_penalty:
dlp = 0
else:
try:
hlp = int(hourly_late_penalty)
dlp = int(daily_late_penalty)
except ValueError:
return HttpResponseBadRequest("A non-numeric hourly late penalty (" + hourly_late_penalty + ") was provided")
return HttpResponseBadRequest("A non-numeric daily late penalty (" + daily_late_penalty + ") was provided")


if not num_subs_permitted:
Expand All @@ -808,7 +808,7 @@ def save_exam_ajax(request, course_prefix, course_suffix, create_or_edit="create
if create_or_edit == "create":
exam_obj = Exam(course=course, slug=slug, title=title, description=description, html_content=htmlContent, xml_metadata=metaXMLContent,
due_date=dd, assessment_type=assessment_type, mode="draft", total_score=total_score, grade_single=grade_single,
grace_period=gp, partial_credit_deadline=pcd, late_penalty=lp, hourly_late_penalty=hlp, submissions_permitted=sp,
grace_period=gp, partial_credit_deadline=pcd, late_penalty=lp, daily_late_penalty=dlp, submissions_permitted=sp,
resubmission_penalty=rp,
exam_type=exam_type, autograde=autograde, display_single=display_single, invideo=invideo, section=contentsection,
xml_imported=xmlImported, quizdown=quizdown, hide_grades=hide_grades
Expand Down Expand Up @@ -854,7 +854,7 @@ def save_exam_ajax(request, course_prefix, course_suffix, create_or_edit="create
exam_obj.grace_period=gp
exam_obj.partial_credit_deadline=pcd
exam_obj.late_penalty=lp
exam_obj.hourly_late_penalty=hlp
exam_obj.daily_late_penalty=dlp
exam_obj.submissions_permitted=sp
exam_obj.resubmission_penalty=rp
exam_obj.exam_type=exam_type
Expand Down Expand Up @@ -942,7 +942,7 @@ def edit_exam(request, course_prefix, course_suffix, exam_slug):
'assessment_type':exam.assessment_type, 'late_penalty':exam.late_penalty, 'num_subs_permitted':exam.submissions_permitted,
'resubmission_penalty':exam.resubmission_penalty, 'description':exam.description, 'section':exam.section.id,'invideo':exam.invideo,
'metadata':exam.xml_metadata, 'htmlContent':exam.html_content, 'xmlImported':exam.xml_imported, 'quizdown':exam.quizdown,
'hide_grades':exam.hide_grades, 'hourly_late_penalty':exam.hourly_late_penalty}
'hide_grades':exam.hide_grades, 'daily_late_penalty':exam.daily_late_penalty}

groupable_exam = exam
if exam.mode != 'ready':
Expand Down
11 changes: 6 additions & 5 deletions main/templates/exams/create_exam.html
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,8 @@ <h2>
<div class="atomic-unit well" id="penalties" >
<label class="control-label" for="late_penalty">{% trans '% Late Penalty (constant)' %}:*</label>
<input id="late_penalty" name="late_penalty" type="text" value="0"/>
<label class="control-label" for="hourly_late_penalty">{% trans '% Late Penalty (per hour)' %}:*</label>
<input id="hourly_late_penalty" name="hourly_late_penalty" type="text" value="0"/>
<label class="control-label" for="daily_late_penalty">{% trans '% Late Penalty (per 24 hrs)' %}:*</label>
<input id="daily_late_penalty" name="daily_late_penalty" type="text" value="0"/>
<label class="control-label" for="num_subs_permitted">{% trans 'Number of Submissions Allowed' %}:*</label>
<input id="num_subs_permitted" name="num_subs_permitted" type="text" value="999"/>
<label class="control-label" for="resubmission_penalty">{% trans '% Penalty Per Submission After First' %}:*</label>
Expand Down Expand Up @@ -238,6 +238,7 @@ <h4>{% trans 'Create Exam' %}</h4>
grace-period: 12/29/2013 00:00
hard-deadline: 12/29/2013 00:00
late-penalty: 10
daily-late-penalty: 0
num-subs-permitted: 9999
resubmission-penalty: 10
section: Test Videos
Expand Down Expand Up @@ -531,7 +532,7 @@ <h3 class="questionNumber"></h3>
<!-- possible types are: "formative" / "summative" / "interactive" / "exam-autograde" / "exam-csv" / "survey" -->
<description>This is the problem set description.</description>
<dates due-date="12/29/2012 00:00" grace-period="12/29/2012 00:00" hard-deadline="12/29/2012 00:00" />
<grading late-penalty="10" num-submissions="9999" resubmission-penalty="10" />
<grading late-penalty="10" daily-late-penalty="1" num-submissions="9999" resubmission-penalty="10" />
<section section="Problem set templates" parent="Example formative multiple choice (checkbox)" />
<problem data-report="Apple Competitor Question">
<p>
Expand Down Expand Up @@ -995,7 +996,7 @@ <h3 class="questionNumber"></h3>
partial_credit_deadline:$('input#hard_deadline').val(),
assessment_type:$('select#assessment_type').val(),
late_penalty:$('input#late_penalty').val(),
hourly_late_penalty:$('input#hourly_late_penalty').val(),
daily_late_penalty:$('input#daily_late_penalty').val(),
num_subs_permitted:$('input#num_subs_permitted').val(),
resubmission_penalty:$('input#resubmission_penalty').val(),
description:$('textarea#description').val(),
Expand Down Expand Up @@ -1073,7 +1074,7 @@ <h3 class="questionNumber"></h3>
$('input#hard_deadline').val(prepop.partial_credit_deadline);
$('select#assessment_type').val(prepop.assessment_type);
$('input#late_penalty').val(prepop.late_penalty);
$('input#hourly_late_penalty').val(prepop.hourly_late_penalty);
$('input#daily_late_penalty').val(prepop.daily_late_penalty);
$('input#num_subs_permitted').val(prepop.num_subs_permitted);
$('input#resubmission_penalty').val(prepop.resubmission_penalty);
$('textarea#description').val(prepop.description);
Expand Down
2 changes: 1 addition & 1 deletion main/templates/exams/quizdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ c2gXMLParse.importMeta = function (meta) {
k == "late_penalty" || kslug == "late_penalty" ||
k == "num_subs_permitted" || kslug == "num_subs_permitted" ||
k == "resubmission_penalty" || kslug == "resubmission_penalty" ||
k == "description") {
k == "description" || kslug == "daily_late_penalty") {
$('#'+kslug).val(value);
}
else if (k == "invideo" && value.toLowerCase() != "false") {
Expand Down
7 changes: 6 additions & 1 deletion main/templates/exams/ready/exam_list_item.html
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,12 @@ <h4 class="pull-left rightmargin" >
{% if record.late or record.attempt_number > 1 and exam.resubmission_penalty > 0 %}
( {% trans 'Raw score' %}: {{ record.examrecordscore.raw_score }} with
{% if record.late %}
{{exam.late_penalty}}% {% trans 'late penalty' %}
{% if exam.late_penalty %}
{{exam.late_penalty}}% {% trans 'late penalty' %}
{% endif %}
{% if exam.daily_late_penalty %}
{{exam.daily_late_penalty}}% {% trans 'late penalty (per day)' %}
{% endif %}
{% if record.attempt_number > 1 and exam.resubmission_penalty > 0 %}
{% trans 'and' %}
{% endif %}
Expand Down
1 change: 1 addition & 0 deletions main/templates/exams/renderPreview.js
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,7 @@ var c2gXMLParse = (function() {

if (gradingDOM.length) {
setValIfDef($('input#late_penalty'), $(gradingDOM).attr('late-penalty'));
setValIfDef($('input#daily_late_penalty'), $(gradingDOM).attr('daily-late-penalty'));
setValIfDef($('input#num_subs_permitted'), $(gradingDOM).attr('num-submissions'));
setValIfDef($('input#resubmission_penalty'), $(gradingDOM).attr('resubmission-penalty'));

Expand Down

0 comments on commit e5a39f8

Please sign in to comment.