Commit a24cf993 by Chris Dodge

Switch to use User object rather than integer. Also add new field for allowed_time_limit_mins

parent ac0115f7
......@@ -167,10 +167,16 @@ def create_exam_attempt(exam_id, user_id, external_id):
raise StudentExamAttemptAlreadyExistsException(err_msg)
# for now the student is allowed the exam default
exam = get_exam_by_id(exam_id)
allowed_time_limit_mins = exam['time_limit_mins']
attempt = ProctoredExamStudentAttempt.create_exam_attempt(
exam_id,
user_id,
'', # student name is TBD
allowed_time_limit_mins,
external_id
)
return attempt.id
......@@ -312,7 +318,7 @@ def get_student_view(user_id, course_id, content_id, context):
has_started_exam = attempt and attempt.get('started_at')
if has_started_exam:
now_utc = datetime.now(pytz.UTC)
expires_at = attempt['started_at'] + timedelta(minutes=context['default_time_limit_mins'])
expires_at = attempt['started_at'] + timedelta(minutes=attempt['allowed_time_limit_mins'])
has_time_expired = now_utc > expires_at
if not has_started_exam:
......
# -*- coding: utf-8 -*-
from south.utils import datetime_utils as datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding field 'ProctoredExamStudentAttempt.taking_as_proctored'
db.add_column('proctoring_proctoredexamstudentattempt', 'taking_as_proctored',
self.gf('django.db.models.fields.BooleanField')(default=False),
keep_default=False)
def backwards(self, orm):
# Deleting field 'ProctoredExamStudentAttempt.taking_as_proctored'
db.delete_column('proctoring_proctoredexamstudentattempt', 'taking_as_proctored')
models = {
'edx_proctoring.proctoredexam': {
'Meta': {'unique_together': "(('course_id', 'content_id'),)", 'object_name': 'ProctoredExam', 'db_table': "'proctoring_proctoredexam'"},
'content_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'course_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}),
'exam_name': ('django.db.models.fields.TextField', [], {}),
'external_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_index': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_proctored': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}),
'time_limit_mins': ('django.db.models.fields.IntegerField', [], {})
},
'edx_proctoring.proctoredexamstudentallowance': {
'Meta': {'unique_together': "(('user_id', 'proctored_exam', 'key'),)", 'object_name': 'ProctoredExamStudentAllowance', 'db_table': "'proctoring_proctoredexamstudentallowance'"},
'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}),
'proctored_exam': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['edx_proctoring.ProctoredExam']"}),
'user_id': ('django.db.models.fields.IntegerField', [], {}),
'value': ('django.db.models.fields.CharField', [], {'max_length': '255'})
},
'edx_proctoring.proctoredexamstudentallowancehistory': {
'Meta': {'object_name': 'ProctoredExamStudentAllowanceHistory', 'db_table': "'proctoring_proctoredexamstudentallowancehistory'"},
'allowance_id': ('django.db.models.fields.IntegerField', [], {}),
'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}),
'proctored_exam': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['edx_proctoring.ProctoredExam']"}),
'user_id': ('django.db.models.fields.IntegerField', [], {}),
'value': ('django.db.models.fields.CharField', [], {'max_length': '255'})
},
'edx_proctoring.proctoredexamstudentattempt': {
'Meta': {'object_name': 'ProctoredExamStudentAttempt', 'db_table': "'proctoring_proctoredexamstudentattempt'"},
'completed_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}),
'external_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_index': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}),
'proctored_exam': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['edx_proctoring.ProctoredExam']"}),
'started_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
'status': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
'taking_as_proctored': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'user_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'})
}
}
complete_apps = ['edx_proctoring']
\ No newline at end of file
# -*- coding: utf-8 -*-
from south.utils import datetime_utils as datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding field 'ProctoredExamStudentAttempt.student_name'
db.add_column('proctoring_proctoredexamstudentattempt', 'student_name',
self.gf('django.db.models.fields.CharField')(max_length=255, null=True),
keep_default=False)
def backwards(self, orm):
# Deleting field 'ProctoredExamStudentAttempt.student_name'
db.delete_column('proctoring_proctoredexamstudentattempt', 'student_name')
models = {
'edx_proctoring.proctoredexam': {
'Meta': {'unique_together': "(('course_id', 'content_id'),)", 'object_name': 'ProctoredExam', 'db_table': "'proctoring_proctoredexam'"},
'content_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'course_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}),
'exam_name': ('django.db.models.fields.TextField', [], {}),
'external_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_index': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_proctored': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}),
'time_limit_mins': ('django.db.models.fields.IntegerField', [], {})
},
'edx_proctoring.proctoredexamstudentallowance': {
'Meta': {'unique_together': "(('user_id', 'proctored_exam', 'key'),)", 'object_name': 'ProctoredExamStudentAllowance', 'db_table': "'proctoring_proctoredexamstudentallowance'"},
'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}),
'proctored_exam': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['edx_proctoring.ProctoredExam']"}),
'user_id': ('django.db.models.fields.IntegerField', [], {}),
'value': ('django.db.models.fields.CharField', [], {'max_length': '255'})
},
'edx_proctoring.proctoredexamstudentallowancehistory': {
'Meta': {'object_name': 'ProctoredExamStudentAllowanceHistory', 'db_table': "'proctoring_proctoredexamstudentallowancehistory'"},
'allowance_id': ('django.db.models.fields.IntegerField', [], {}),
'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}),
'proctored_exam': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['edx_proctoring.ProctoredExam']"}),
'user_id': ('django.db.models.fields.IntegerField', [], {}),
'value': ('django.db.models.fields.CharField', [], {'max_length': '255'})
},
'edx_proctoring.proctoredexamstudentattempt': {
'Meta': {'object_name': 'ProctoredExamStudentAttempt', 'db_table': "'proctoring_proctoredexamstudentattempt'"},
'completed_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}),
'external_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_index': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}),
'proctored_exam': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['edx_proctoring.ProctoredExam']"}),
'started_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
'status': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
'student_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
'taking_as_proctored': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'user_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'})
}
}
complete_apps = ['edx_proctoring']
\ No newline at end of file
......@@ -9,6 +9,8 @@ from django.db.models.signals import post_save, pre_delete
from django.dispatch import receiver
from model_utils.models import TimeStampedModel
from django.contrib.auth.models import User
class ProctoredExam(TimeStampedModel):
"""
......@@ -78,7 +80,7 @@ class ProctoredExamStudentAttempt(TimeStampedModel):
Information about the Student Attempt on a
Proctored Exam.
"""
user_id = models.IntegerField(db_index=True)
user = models.ForeignKey(User, db_index=True)
proctored_exam = models.ForeignKey(ProctoredExam, db_index=True)
......@@ -89,6 +91,9 @@ class ProctoredExamStudentAttempt(TimeStampedModel):
# This will be a integration specific ID - say to SoftwareSecure.
external_id = models.CharField(max_length=255, null=True, db_index=True)
# this is the time limit allowed to the student
allowed_time_limit_mins = models.IntegerField()
# what is the status of this attempt
status = models.CharField(max_length=64)
......@@ -109,7 +114,7 @@ class ProctoredExamStudentAttempt(TimeStampedModel):
return self.started_at and not self.completed_at
@classmethod
def create_exam_attempt(cls, exam_id, user_id, student_name, external_id):
def create_exam_attempt(cls, exam_id, user_id, student_name, allowed_time_limit_mins, external_id):
"""
Create a new exam attempt entry for a given exam_id and
user_id.
......@@ -119,6 +124,7 @@ class ProctoredExamStudentAttempt(TimeStampedModel):
proctored_exam_id=exam_id,
user_id=user_id,
student_name=student_name,
allowed_time_limit_mins=allowed_time_limit_mins,
external_id=external_id
)
......@@ -180,7 +186,7 @@ class ProctoredExamStudentAllowance(TimeStampedModel):
objects = ProctoredExamStudentAllowanceManager()
user_id = models.IntegerField()
user = models.ForeignKey(User)
proctored_exam = models.ForeignKey(ProctoredExam)
......@@ -190,7 +196,7 @@ class ProctoredExamStudentAllowance(TimeStampedModel):
class Meta:
""" Meta class for this Django model """
unique_together = (('user_id', 'proctored_exam', 'key'),)
unique_together = (('user', 'proctored_exam', 'key'),)
db_table = 'proctoring_proctoredexamstudentallowance'
verbose_name = 'proctored allowance'
......@@ -234,7 +240,7 @@ class ProctoredExamStudentAllowanceHistory(TimeStampedModel):
# what was the original id of the allowance
allowance_id = models.IntegerField()
user_id = models.IntegerField()
user = models.ForeignKey(User)
proctored_exam = models.ForeignKey(ProctoredExam)
......@@ -276,7 +282,7 @@ def _make_archive_copy(item):
archive_object = ProctoredExamStudentAllowanceHistory(
allowance_id=item.id,
user_id=item.user_id,
user=item.user,
proctored_exam=item.proctored_exam,
key=item.key,
value=item.value
......
......@@ -47,6 +47,7 @@ class ProctoredExamStudentAttemptSerializer(serializers.ModelSerializer):
Serializer for the ProctoredExamStudentAttempt Model.
"""
proctored_exam_id = serializers.IntegerField(source="proctored_exam_id")
user_id = serializers.IntegerField(source='user_id')
class Meta:
"""
......@@ -56,7 +57,7 @@ class ProctoredExamStudentAttemptSerializer(serializers.ModelSerializer):
fields = (
"id", "created", "modified", "user_id", "started_at", "completed_at",
"external_id", "status", "proctored_exam_id"
"external_id", "status", "proctored_exam_id", "allowed_time_limit_mins"
)
......@@ -70,5 +71,5 @@ class ProctoredExamStudentAllowanceSerializer(serializers.ModelSerializer):
"""
model = ProctoredExamStudentAllowance
fields = (
"id", "created", "modified", "user_id", "key", "value"
"id", "created", "modified", "user", "key", "value"
)
......@@ -94,7 +94,8 @@ class ProctoredExamApiTests(LoggedInTestCase):
return ProctoredExamStudentAttempt.objects.create(
proctored_exam_id=self.proctored_exam_id,
user_id=self.user_id,
external_id=self.external_id
external_id=self.external_id,
allowed_time_limit_mins=10
)
def _create_started_exam_attempt(self, started_at=None):
......@@ -105,7 +106,8 @@ class ProctoredExamApiTests(LoggedInTestCase):
proctored_exam_id=self.proctored_exam_id,
user_id=self.user_id,
external_id=self.external_id,
started_at=started_at if started_at else datetime.now(pytz.UTC)
started_at=started_at if started_at else datetime.now(pytz.UTC),
allowed_time_limit_mins=10
)
def _add_allowance_for_user(self):
......
......@@ -47,7 +47,7 @@ class ProctoredExamModelTests(LoggedInTestCase):
user_id=1,
proctored_exam=proctored_exam,
).update(
user_id=1,
user=1,
proctored_exam=proctored_exam,
key='allowance_key update 1',
value='10 minutes'
......@@ -57,7 +57,7 @@ class ProctoredExamModelTests(LoggedInTestCase):
user_id=1,
proctored_exam=proctored_exam,
).update(
user_id=1,
user=1,
proctored_exam=proctored_exam,
key='allowance_key update 2',
value='5 minutes'
......
......@@ -639,7 +639,8 @@ class TestActiveExamsForUserView(LoggedInTestCase):
proctored_exam_id=proctored_exam.id,
user_id=self.student_taking_exam.id,
external_id='123aXqe3',
started_at=datetime.now(pytz.UTC)
started_at=datetime.now(pytz.UTC),
allowed_time_limit_mins=90
)
ProctoredExamStudentAllowance.objects.create(
......
......@@ -248,7 +248,7 @@ class StudentProctoredExamAttempt(AuthenticatedAPIView):
exam = exams[0]
# need to adjust for allowances
expires_at = exam['attempt']['started_at'] + timedelta(minutes=exam['exam']['time_limit_mins'])
expires_at = exam['attempt']['started_at'] + timedelta(minutes=exam['attempt']['allowed_time_limit_mins'])
now_utc = datetime.now(pytz.UTC)
if expires_at > now_utc:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment