Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-proctoring
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
OpenEdx
edx-proctoring
Commits
b3d9949c
Commit
b3d9949c
authored
Jun 24, 2015
by
Chris Dodge
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
update models
parent
92d47c45
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
183 additions
and
7 deletions
+183
-7
edx_proctoring/api.py
+88
-0
edx_proctoring/migrations/0001_initial.py
+28
-2
edx_proctoring/models.py
+36
-4
edx_proctoring/tests/test_models.py
+30
-0
pylintrc
+1
-1
No files found.
edx_proctoring/api.py
View file @
b3d9949c
# pylint: disable=unused-argument
# remove pylint rule after we implement each method
"""
"""
In-Proc API (aka Library) for the edx_proctoring subsystem. This is not to be confused with a HTTP REST
In-Proc API (aka Library) for the edx_proctoring subsystem. This is not to be confused with a HTTP REST
API which is in the views.py file, per edX coding standards
API which is in the views.py file, per edX coding standards
"""
"""
def
create_exam
(
course_id
,
content_id
,
exam_name
,
time_limit_mins
,
is_proctored
=
True
,
external_id
=
None
,
is_active
=
True
):
"""
Creates a new ProctoredExam entity, if the course_id/content_id pair do not already exist.
If that pair already exists, then raise exception.
Returns: id (PK)
"""
def
update_exam
(
exam_id
,
exam_name
=
None
,
time_limit_mins
=
None
,
is_proctored
=
None
,
external_id
=
None
,
is_active
=
None
):
"""
Given a Django ORM id, update the existing record, otherwise raise exception if not found.
If an argument is not passed in, then do not change it's current value.
Returns: id
"""
def
get_exam_by_id
(
exam_id
):
"""
Looks up exam by the Primary Key. Raises exception if not found.
Returns dictionary version of the Django ORM object
"""
def
get_exam_by_content_id
(
course_id
,
content_id
):
"""
Looks up exam by the course_id/content_id pair. Raises exception if not found.
Returns dictionary version of the Django ORM object
"""
def
add_allowance_for_user
(
exam_id
,
user_id
,
key
,
value
):
"""
Adds (or updates) an allowance for a user within a given exam
"""
def
remove_allowance_for_user
(
exam_id
,
user_id
,
key
):
"""
Deletes an allowance for a user within a given exam.
"""
def
start_exam_attempt
(
exam_id
,
user_id
,
external_id
):
"""
Signals the beginning of an exam attempt for a given
exam_id. If one already exists, then an exception should be thrown.
Returns: exam_attempt_id (PK)
"""
def
stop_exam_attempt
(
exam_id
,
user
):
"""
Marks the exam attempt as completed (sets the completed_at field and updates the record)
"""
def
get_active_exams_for_user
(
user_id
,
course_id
=
None
):
"""
This method will return a list of active exams for the user,
i.e. started_at != None and completed_at == None. Theoretically there
could be more than one, but in practice it will be one active exam.
If course_id is set, then attempts only for an exam in that course_id
should be returned.
The return set should be a list of dictionary objects which are nested
[{
'exam': <exam fields as dict>,
'attempt': <student attempt fields as dict>,
'allowances': <student allowances as dict of key/value pairs
}, {}, ...]
"""
edx_proctoring/migrations/0001_initial.py
View file @
b3d9949c
...
@@ -11,23 +11,32 @@ class Migration(SchemaMigration):
...
@@ -11,23 +11,32 @@ class Migration(SchemaMigration):
# Adding model 'ProctoredExam'
# Adding model 'ProctoredExam'
db
.
create_table
(
'edx_proctoring_proctoredexam'
,
(
db
.
create_table
(
'edx_proctoring_proctoredexam'
,
(
(
'id'
,
self
.
gf
(
'django.db.models.fields.AutoField'
)(
primary_key
=
True
)),
(
'id'
,
self
.
gf
(
'django.db.models.fields.AutoField'
)(
primary_key
=
True
)),
(
'created'
,
self
.
gf
(
'model_utils.fields.AutoCreatedField'
)(
default
=
datetime
.
datetime
.
now
)),
(
'modified'
,
self
.
gf
(
'model_utils.fields.AutoLastModifiedField'
)(
default
=
datetime
.
datetime
.
now
)),
(
'course_id'
,
self
.
gf
(
'django.db.models.fields.CharField'
)(
max_length
=
255
,
db_index
=
True
)),
(
'course_id'
,
self
.
gf
(
'django.db.models.fields.CharField'
)(
max_length
=
255
,
db_index
=
True
)),
(
'content_id'
,
self
.
gf
(
'django.db.models.fields.CharField'
)(
max_length
=
255
,
db_index
=
True
)),
(
'content_id'
,
self
.
gf
(
'django.db.models.fields.CharField'
)(
max_length
=
255
,
db_index
=
True
)),
(
'external_id'
,
self
.
gf
(
'django.db.models.fields.TextField'
)(
null
=
True
,
db_index
=
True
)),
(
'external_id'
,
self
.
gf
(
'django.db.models.fields.TextField'
)(
null
=
True
,
db_index
=
True
)),
(
'exam_name'
,
self
.
gf
(
'django.db.models.fields.TextField'
)()),
(
'time_limit_mins'
,
self
.
gf
(
'django.db.models.fields.IntegerField'
)()),
(
'time_limit_mins'
,
self
.
gf
(
'django.db.models.fields.IntegerField'
)()),
(
'is_proctored'
,
self
.
gf
(
'django.db.models.fields.BooleanField'
)(
default
=
False
)),
(
'is_proctored'
,
self
.
gf
(
'django.db.models.fields.BooleanField'
)(
default
=
False
)),
(
'is_active'
,
self
.
gf
(
'django.db.models.fields.BooleanField'
)(
default
=
False
)),
(
'is_active'
,
self
.
gf
(
'django.db.models.fields.BooleanField'
)(
default
=
False
)),
))
))
db
.
send_create_signal
(
'edx_proctoring'
,
[
'ProctoredExam'
])
db
.
send_create_signal
(
'edx_proctoring'
,
[
'ProctoredExam'
])
# Adding unique constraint on 'ProctoredExam', fields ['course_id', 'content_id']
db
.
create_unique
(
'edx_proctoring_proctoredexam'
,
[
'course_id'
,
'content_id'
])
# Adding model 'ProctoredExamStudentAttempt'
# Adding model 'ProctoredExamStudentAttempt'
db
.
create_table
(
'edx_proctoring_proctoredexamstudentattempt'
,
(
db
.
create_table
(
'edx_proctoring_proctoredexamstudentattempt'
,
(
(
'id'
,
self
.
gf
(
'django.db.models.fields.AutoField'
)(
primary_key
=
True
)),
(
'id'
,
self
.
gf
(
'django.db.models.fields.AutoField'
)(
primary_key
=
True
)),
(
'created'
,
self
.
gf
(
'model_utils.fields.AutoCreatedField'
)(
default
=
datetime
.
datetime
.
now
)),
(
'modified'
,
self
.
gf
(
'model_utils.fields.AutoLastModifiedField'
)(
default
=
datetime
.
datetime
.
now
)),
(
'user_id'
,
self
.
gf
(
'django.db.models.fields.IntegerField'
)(
db_index
=
True
)),
(
'user_id'
,
self
.
gf
(
'django.db.models.fields.IntegerField'
)(
db_index
=
True
)),
(
'proctored_exam'
,
self
.
gf
(
'django.db.models.fields.related.ForeignKey'
)(
to
=
orm
[
'edx_proctoring.ProctoredExam'
])),
(
'proctored_exam'
,
self
.
gf
(
'django.db.models.fields.related.ForeignKey'
)(
to
=
orm
[
'edx_proctoring.ProctoredExam'
])),
(
'started_at'
,
self
.
gf
(
'django.db.models.fields.DateTimeField'
)(
null
=
True
)),
(
'started_at'
,
self
.
gf
(
'django.db.models.fields.DateTimeField'
)(
null
=
True
)),
(
'completed_at'
,
self
.
gf
(
'django.db.models.fields.DateTimeField'
)(
null
=
True
)),
(
'completed_at'
,
self
.
gf
(
'django.db.models.fields.DateTimeField'
)(
null
=
True
)),
(
'external_id'
,
self
.
gf
(
'django.db.models.fields.TextField'
)(
null
=
True
,
db_index
=
True
)),
(
'external_id'
,
self
.
gf
(
'django.db.models.fields.TextField'
)(
null
=
True
,
db_index
=
True
)),
(
'status'
,
self
.
gf
(
'django.db.models.fields.CharField'
)(
max_length
=
64
)),
))
))
db
.
send_create_signal
(
'edx_proctoring'
,
[
'ProctoredExamStudentAttempt'
])
db
.
send_create_signal
(
'edx_proctoring'
,
[
'ProctoredExamStudentAttempt'
])
...
@@ -43,11 +52,15 @@ class Migration(SchemaMigration):
...
@@ -43,11 +52,15 @@ class Migration(SchemaMigration):
))
))
db
.
send_create_signal
(
'edx_proctoring'
,
[
'ProctoredExamStudentAllowance'
])
db
.
send_create_signal
(
'edx_proctoring'
,
[
'ProctoredExamStudentAllowance'
])
# Adding unique constraint on 'ProctoredExamStudentAllowance', fields ['user_id', 'proctored_exam', 'key']
db
.
create_unique
(
'edx_proctoring_proctoredexamstudentallowance'
,
[
'user_id'
,
'proctored_exam_id'
,
'key'
])
# Adding model 'ProctoredExamStudentAllowanceHistory'
# Adding model 'ProctoredExamStudentAllowanceHistory'
db
.
create_table
(
'edx_proctoring_proctoredexamstudentallowancehistory'
,
(
db
.
create_table
(
'edx_proctoring_proctoredexamstudentallowancehistory'
,
(
(
'id'
,
self
.
gf
(
'django.db.models.fields.AutoField'
)(
primary_key
=
True
)),
(
'id'
,
self
.
gf
(
'django.db.models.fields.AutoField'
)(
primary_key
=
True
)),
(
'created'
,
self
.
gf
(
'model_utils.fields.AutoCreatedField'
)(
default
=
datetime
.
datetime
.
now
)),
(
'created'
,
self
.
gf
(
'model_utils.fields.AutoCreatedField'
)(
default
=
datetime
.
datetime
.
now
)),
(
'modified'
,
self
.
gf
(
'model_utils.fields.AutoLastModifiedField'
)(
default
=
datetime
.
datetime
.
now
)),
(
'modified'
,
self
.
gf
(
'model_utils.fields.AutoLastModifiedField'
)(
default
=
datetime
.
datetime
.
now
)),
(
'allowance_id'
,
self
.
gf
(
'django.db.models.fields.IntegerField'
)()),
(
'user_id'
,
self
.
gf
(
'django.db.models.fields.IntegerField'
)()),
(
'user_id'
,
self
.
gf
(
'django.db.models.fields.IntegerField'
)()),
(
'proctored_exam'
,
self
.
gf
(
'django.db.models.fields.related.ForeignKey'
)(
to
=
orm
[
'edx_proctoring.ProctoredExam'
])),
(
'proctored_exam'
,
self
.
gf
(
'django.db.models.fields.related.ForeignKey'
)(
to
=
orm
[
'edx_proctoring.ProctoredExam'
])),
(
'key'
,
self
.
gf
(
'django.db.models.fields.CharField'
)(
max_length
=
255
)),
(
'key'
,
self
.
gf
(
'django.db.models.fields.CharField'
)(
max_length
=
255
)),
...
@@ -57,6 +70,12 @@ class Migration(SchemaMigration):
...
@@ -57,6 +70,12 @@ class Migration(SchemaMigration):
def
backwards
(
self
,
orm
):
def
backwards
(
self
,
orm
):
# Removing unique constraint on 'ProctoredExamStudentAllowance', fields ['user_id', 'proctored_exam', 'key']
db
.
delete_unique
(
'edx_proctoring_proctoredexamstudentallowance'
,
[
'user_id'
,
'proctored_exam_id'
,
'key'
])
# Removing unique constraint on 'ProctoredExam', fields ['course_id', 'content_id']
db
.
delete_unique
(
'edx_proctoring_proctoredexam'
,
[
'course_id'
,
'content_id'
])
# Deleting model 'ProctoredExam'
# Deleting model 'ProctoredExam'
db
.
delete_table
(
'edx_proctoring_proctoredexam'
)
db
.
delete_table
(
'edx_proctoring_proctoredexam'
)
...
@@ -72,17 +91,20 @@ class Migration(SchemaMigration):
...
@@ -72,17 +91,20 @@ class Migration(SchemaMigration):
models
=
{
models
=
{
'edx_proctoring.proctoredexam'
:
{
'edx_proctoring.proctoredexam'
:
{
'Meta'
:
{
'object_name'
:
'ProctoredExam'
},
'Meta'
:
{
'
unique_together'
:
"(('course_id', 'content_id'),)"
,
'
object_name'
:
'ProctoredExam'
},
'content_id'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
,
'db_index'
:
'True'
}),
'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'
}),
'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.TextField'
,
[],
{
'null'
:
'True'
,
'db_index'
:
'True'
}),
'external_id'
:
(
'django.db.models.fields.TextField'
,
[],
{
'null'
:
'True'
,
'db_index'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'is_active'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'False'
}),
'is_active'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'False'
}),
'is_proctored'
:
(
'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'
,
[],
{})
'time_limit_mins'
:
(
'django.db.models.fields.IntegerField'
,
[],
{})
},
},
'edx_proctoring.proctoredexamstudentallowance'
:
{
'edx_proctoring.proctoredexamstudentallowance'
:
{
'Meta'
:
{
'object_name'
:
'ProctoredExamStudentAllowance'
},
'Meta'
:
{
'
unique_together'
:
"(('user_id', 'proctored_exam', 'key'),)"
,
'
object_name'
:
'ProctoredExamStudentAllowance'
},
'created'
:
(
'model_utils.fields.AutoCreatedField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'created'
:
(
'model_utils.fields.AutoCreatedField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'key'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
}),
'key'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
}),
...
@@ -93,6 +115,7 @@ class Migration(SchemaMigration):
...
@@ -93,6 +115,7 @@ class Migration(SchemaMigration):
},
},
'edx_proctoring.proctoredexamstudentallowancehistory'
:
{
'edx_proctoring.proctoredexamstudentallowancehistory'
:
{
'Meta'
:
{
'object_name'
:
'ProctoredExamStudentAllowanceHistory'
},
'Meta'
:
{
'object_name'
:
'ProctoredExamStudentAllowanceHistory'
},
'allowance_id'
:
(
'django.db.models.fields.IntegerField'
,
[],
{}),
'created'
:
(
'model_utils.fields.AutoCreatedField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'created'
:
(
'model_utils.fields.AutoCreatedField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'key'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
}),
'key'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
}),
...
@@ -104,10 +127,13 @@ class Migration(SchemaMigration):
...
@@ -104,10 +127,13 @@ class Migration(SchemaMigration):
'edx_proctoring.proctoredexamstudentattempt'
:
{
'edx_proctoring.proctoredexamstudentattempt'
:
{
'Meta'
:
{
'object_name'
:
'ProctoredExamStudentAttempt'
},
'Meta'
:
{
'object_name'
:
'ProctoredExamStudentAttempt'
},
'completed_at'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'null'
:
'True'
}),
'completed_at'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'null'
:
'True'
}),
'created'
:
(
'model_utils.fields.AutoCreatedField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'external_id'
:
(
'django.db.models.fields.TextField'
,
[],
{
'null'
:
'True'
,
'db_index'
:
'True'
}),
'external_id'
:
(
'django.db.models.fields.TextField'
,
[],
{
'null'
:
'True'
,
'db_index'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'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']"
}),
'proctored_exam'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['edx_proctoring.ProctoredExam']"
}),
'started_at'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'null'
:
'True'
}),
'started_at'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'null'
:
'True'
}),
'status'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'64'
}),
'user_id'
:
(
'django.db.models.fields.IntegerField'
,
[],
{
'db_index'
:
'True'
})
'user_id'
:
(
'django.db.models.fields.IntegerField'
,
[],
{
'db_index'
:
'True'
})
}
}
}
}
...
...
edx_proctoring/models.py
View file @
b3d9949c
...
@@ -2,12 +2,12 @@
...
@@ -2,12 +2,12 @@
Data models for the proctoring subsystem
Data models for the proctoring subsystem
"""
"""
from
django.db
import
models
from
django.db
import
models
from
django.db.models.signals
import
post_save
from
django.db.models.signals
import
post_save
,
pre_delete
from
django.dispatch
import
receiver
from
django.dispatch
import
receiver
from
model_utils.models
import
TimeStampedModel
from
model_utils.models
import
TimeStampedModel
class
ProctoredExam
(
models
.
Model
):
class
ProctoredExam
(
TimeStamped
Model
):
"""
"""
Information about the Proctored Exam.
Information about the Proctored Exam.
"""
"""
...
@@ -21,6 +21,9 @@ class ProctoredExam(models.Model):
...
@@ -21,6 +21,9 @@ class ProctoredExam(models.Model):
# This will be a integration specific ID - say to SoftwareSecure.
# This will be a integration specific ID - say to SoftwareSecure.
external_id
=
models
.
TextField
(
null
=
True
,
db_index
=
True
)
external_id
=
models
.
TextField
(
null
=
True
,
db_index
=
True
)
# This is the display name of the course
exam_name
=
models
.
TextField
()
# Time limit (in minutes) that a student can finish this exam
# Time limit (in minutes) that a student can finish this exam
time_limit_mins
=
models
.
IntegerField
()
time_limit_mins
=
models
.
IntegerField
()
...
@@ -30,8 +33,12 @@ class ProctoredExam(models.Model):
...
@@ -30,8 +33,12 @@ class ProctoredExam(models.Model):
# This will be a integration specific ID - say to SoftwareSecure.
# This will be a integration specific ID - say to SoftwareSecure.
is_active
=
models
.
BooleanField
()
is_active
=
models
.
BooleanField
()
class
Meta
:
""" Meta class for this Django model """
unique_together
=
((
'course_id'
,
'content_id'
),)
class
ProctoredExamStudentAttempt
(
models
.
Model
):
class
ProctoredExamStudentAttempt
(
TimeStamped
Model
):
"""
"""
Information about the Student Attempt on a
Information about the Student Attempt on a
Proctored Exam.
Proctored Exam.
...
@@ -47,6 +54,14 @@ class ProctoredExamStudentAttempt(models.Model):
...
@@ -47,6 +54,14 @@ class ProctoredExamStudentAttempt(models.Model):
# This will be a integration specific ID - say to SoftwareSecure.
# This will be a integration specific ID - say to SoftwareSecure.
external_id
=
models
.
TextField
(
null
=
True
,
db_index
=
True
)
external_id
=
models
.
TextField
(
null
=
True
,
db_index
=
True
)
# what is the status of this attempt
status
=
models
.
CharField
(
max_length
=
64
)
@property
def
is_active
(
self
):
""" returns boolean if this attempt is considered active """
return
self
.
started_at
and
not
self
.
completed_at
class
QuerySetWithUpdateOverride
(
models
.
query
.
QuerySet
):
class
QuerySetWithUpdateOverride
(
models
.
query
.
QuerySet
):
"""
"""
...
@@ -82,6 +97,10 @@ class ProctoredExamStudentAllowance(TimeStampedModel):
...
@@ -82,6 +97,10 @@ class ProctoredExamStudentAllowance(TimeStampedModel):
value
=
models
.
CharField
(
max_length
=
255
)
value
=
models
.
CharField
(
max_length
=
255
)
class
Meta
:
""" Meta class for this Django model """
unique_together
=
((
'user_id'
,
'proctored_exam'
,
'key'
),)
class
ProctoredExamStudentAllowanceHistory
(
TimeStampedModel
):
class
ProctoredExamStudentAllowanceHistory
(
TimeStampedModel
):
"""
"""
...
@@ -89,6 +108,9 @@ class ProctoredExamStudentAllowanceHistory(TimeStampedModel):
...
@@ -89,6 +108,9 @@ class ProctoredExamStudentAllowanceHistory(TimeStampedModel):
but will record (for audit history) all entries that have been updated.
but will record (for audit history) all entries that have been updated.
"""
"""
# what was the original id of the allowance
allowance_id
=
models
.
IntegerField
()
user_id
=
models
.
IntegerField
()
user_id
=
models
.
IntegerField
()
proctored_exam
=
models
.
ForeignKey
(
ProctoredExam
)
proctored_exam
=
models
.
ForeignKey
(
ProctoredExam
)
...
@@ -100,7 +122,7 @@ class ProctoredExamStudentAllowanceHistory(TimeStampedModel):
...
@@ -100,7 +122,7 @@ class ProctoredExamStudentAllowanceHistory(TimeStampedModel):
# Hook up the custom POST_UPDATE_SIGNAL signal to record updations in the ProctoredExamStudentAllowanceHistory table.
# Hook up the custom POST_UPDATE_SIGNAL signal to record updations in the ProctoredExamStudentAllowanceHistory table.
@receiver
(
post_save
,
sender
=
ProctoredExamStudentAllowance
)
@receiver
(
post_save
,
sender
=
ProctoredExamStudentAllowance
)
def
archive_allowance_updations
(
sender
,
instance
,
created
,
**
kwargs
):
# pylint: disable=unused-argument
def
on_allowance_saved
(
sender
,
instance
,
created
,
**
kwargs
):
# pylint: disable=unused-argument
"""
"""
Archiving all changes made to the Student Allowance.
Archiving all changes made to the Student Allowance.
Will only archive on update, and not on new entries created.
Will only archive on update, and not on new entries created.
...
@@ -110,12 +132,22 @@ def archive_allowance_updations(sender, instance, created, **kwargs): # pylint:
...
@@ -110,12 +132,22 @@ def archive_allowance_updations(sender, instance, created, **kwargs): # pylint:
_make_archive_copy
(
instance
)
_make_archive_copy
(
instance
)
@receiver
(
pre_delete
,
sender
=
ProctoredExamStudentAllowance
)
def
on_allowance_deleted
(
sender
,
instance
,
**
kwargs
):
# pylint: disable=unused-argument
"""
Archive the allowance when the item is about to be deleted
"""
_make_archive_copy
(
instance
)
def
_make_archive_copy
(
item
):
def
_make_archive_copy
(
item
):
"""
"""
Make a clone and populate in the History table
Make a clone and populate in the History table
"""
"""
archive_object
=
ProctoredExamStudentAllowanceHistory
(
archive_object
=
ProctoredExamStudentAllowanceHistory
(
allowance_id
=
item
.
id
,
user_id
=
item
.
user_id
,
user_id
=
item
.
user_id
,
proctored_exam
=
item
.
proctored_exam
,
proctored_exam
=
item
.
proctored_exam
,
key
=
item
.
key
,
key
=
item
.
key
,
...
...
edx_proctoring/tests/test_models.py
View file @
b3d9949c
...
@@ -28,6 +28,7 @@ class ProctoredExamModelTests(LoggedInTestCase):
...
@@ -28,6 +28,7 @@ class ProctoredExamModelTests(LoggedInTestCase):
proctored_exam
=
ProctoredExam
.
objects
.
create
(
proctored_exam
=
ProctoredExam
.
objects
.
create
(
course_id
=
'test_course'
,
course_id
=
'test_course'
,
content_id
=
'test_content'
,
content_id
=
'test_content'
,
exam_name
=
'Test Exam'
,
external_id
=
'123aXqe3'
,
external_id
=
'123aXqe3'
,
time_limit_mins
=
90
time_limit_mins
=
90
)
)
...
@@ -74,3 +75,32 @@ class ProctoredExamModelTests(LoggedInTestCase):
...
@@ -74,3 +75,32 @@ class ProctoredExamModelTests(LoggedInTestCase):
proctored_exam_student_history
=
ProctoredExamStudentAllowanceHistory
.
objects
.
filter
(
user_id
=
1
)
proctored_exam_student_history
=
ProctoredExamStudentAllowanceHistory
.
objects
.
filter
(
user_id
=
1
)
self
.
assertEqual
(
len
(
proctored_exam_student_history
),
3
)
self
.
assertEqual
(
len
(
proctored_exam_student_history
),
3
)
def
test_delete_proctored_exam_student_allowance_history
(
self
):
# pylint: disable=invalid-name
"""
Test to delete the proctored Exam Student Allowance object.
Upon first save, a new entry is _not_ created in the History table
However, a new entry in the History table is created every time the Student Allowance entry is updated.
"""
proctored_exam
=
ProctoredExam
.
objects
.
create
(
course_id
=
'test_course'
,
content_id
=
'test_content'
,
exam_name
=
'Test Exam'
,
external_id
=
'123aXqe3'
,
time_limit_mins
=
90
)
allowance
=
ProctoredExamStudentAllowance
.
objects
.
create
(
user_id
=
1
,
proctored_exam
=
proctored_exam
,
key
=
'allowance_key'
,
value
=
'20 minutes'
)
# No entry in the History table on creation of the Allowance entry.
proctored_exam_student_history
=
ProctoredExamStudentAllowanceHistory
.
objects
.
filter
(
user_id
=
1
)
self
.
assertEqual
(
len
(
proctored_exam_student_history
),
0
)
allowance
.
delete
()
proctored_exam_student_history
=
ProctoredExamStudentAllowanceHistory
.
objects
.
filter
(
user_id
=
1
)
self
.
assertEqual
(
len
(
proctored_exam_student_history
),
1
)
pylintrc
View file @
b3d9949c
...
@@ -39,7 +39,7 @@ load-plugins=
...
@@ -39,7 +39,7 @@ load-plugins=
# no Warning level messages displayed, use"--disable=all --enable=classes
# no Warning level messages displayed, use"--disable=all --enable=classes
# --disable=W"
# --disable=W"
# I0011 locally-disabled (module-level pylint overrides)
# I0011 locally-disabled (module-level pylint overrides)
disable=I0011,W0232,too-few-public-methods,abstract-class-little-used,abstract-class-not-used,too-many-public-methods,no-self-use,too-many-instance-attributes,duplicate-code,too-many-arguments,too-many-locals
disable=I0011,W0232,too-few-public-methods,abstract-class-little-used,abstract-class-not-used,too-many-public-methods,no-self-use,too-many-instance-attributes,duplicate-code,too-many-arguments,too-many-locals
,old-style-class
[REPORTS]
[REPORTS]
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment