Newer
Older
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
@classmethod
def deadline_for_course(cls, course_key):
"""
Retrieve the verification deadline for a particular course.
Arguments:
course_key (CourseKey): The identifier for the course.
Returns:
datetime or None
"""
try:
deadline = cls.objects.get(course_key=course_key)
return deadline.deadline
except cls.DoesNotExist:
return None
@receiver(models.signals.post_save, sender=VerificationDeadline)
@receiver(models.signals.post_delete, sender=VerificationDeadline)
def invalidate_deadline_caches(sender, **kwargs): # pylint: disable=unused-argument
"""Invalidate the cached verification deadline information. """
cache.delete(VerificationDeadline.ALL_DEADLINES_CACHE_KEY)
zubair-arbi
committed
"""Represents a point at which a user is asked to re-verify his/her
identity.
zubair-arbi
committed
Each checkpoint is uniquely identified by a
(course_id, checkpoint_location) tuple.
"""
course_id = CourseKeyField(max_length=255, db_index=True)
zubair-arbi
committed
checkpoint_location = models.CharField(max_length=255)
photo_verification = models.ManyToManyField(SoftwareSecurePhotoVerification)
class Meta(object): # pylint: disable=missing-docstring
zubair-arbi
committed
unique_together = ('course_id', 'checkpoint_location')
zubair-arbi
committed
"""
Unicode representation of the checkpoint.
"""
return u"{checkpoint} in {course}".format(
checkpoint=self.checkpoint_name,
course=self.course_id
)
zubair-arbi
committed
@lazy
def checkpoint_name(self):
"""Lazy method for getting checkpoint name of reverification block.
Return location of the checkpoint if no related assessment found in
database.
"""
checkpoint_key = UsageKey.from_string(self.checkpoint_location)
try:
checkpoint_name = modulestore().get_item(checkpoint_key).related_assessment
except ItemNotFoundError:
log.warning(
u"Verification checkpoint block with location '%s' and course id '%s' "
u"not found in database.", self.checkpoint_location, unicode(self.course_id)
)
checkpoint_name = self.checkpoint_location
return checkpoint_name
def add_verification_attempt(self, verification_attempt):
zubair-arbi
committed
"""Add the verification attempt in M2M relation of photo_verification.
zubair-arbi
committed
verification_attempt(object): SoftwareSecurePhotoVerification object
zubair-arbi
committed
self.photo_verification.add(verification_attempt) # pylint: disable=no-member
aamir-khan
committed
def get_user_latest_status(self, user_id):
zubair-arbi
committed
"""Get the status of the latest checkpoint attempt of the given user.
aamir-khan
committed
Args:
user_id(str): Id of user
Returns:
VerificationStatus object if found any else None
"""
try:
zubair-arbi
committed
return self.checkpoint_status.filter(user_id=user_id).latest() # pylint: disable=no-member
aamir-khan
committed
except ObjectDoesNotExist:
return None
def get_or_create_verification_checkpoint(cls, course_id, checkpoint_location):
"""
Get or create the verification checkpoint for given 'course_id' and
zubair-arbi
committed
checkpoint name.
course_id (CourseKey): CourseKey
checkpoint_location (str): Verification checkpoint location
Returns:
VerificationCheckpoint object if exists otherwise None
"""
try:
checkpoint, __ = cls.objects.get_or_create(course_id=course_id, checkpoint_location=checkpoint_location)
return checkpoint
except IntegrityError:
log.info(
u"An integrity error occurred while getting-or-creating the verification checkpoint "
"for course %s at location %s. This can occur if two processes try to get-or-create "
"the checkpoint at the same time and the database is set to REPEATABLE READ. "
"We will try committing the transaction and retrying."
)
transaction.commit()
zubair-arbi
committed
return cls.objects.get(course_id=course_id, checkpoint_location=checkpoint_location)
class VerificationStatus(models.Model):
zubair-arbi
committed
"""This model is an append-only table that represents user status changes
during the verification process.
zubair-arbi
committed
A verification status represents a user’s progress through the verification
process for a particular checkpoint.
"""
VERIFICATION_STATUS_CHOICES = (
("submitted", "submitted"),
("approved", "approved"),
("denied", "denied"),
("error", "error")
)
aamir-khan
committed
checkpoint = models.ForeignKey(VerificationCheckpoint, related_name="checkpoint_status")
user = models.ForeignKey(User)
status = models.CharField(choices=VERIFICATION_STATUS_CHOICES, db_index=True, max_length=32)
timestamp = models.DateTimeField(auto_now_add=True)
response = models.TextField(null=True, blank=True)
error = models.TextField(null=True, blank=True)
aamir-khan
committed
class Meta(object): # pylint: disable=missing-docstring
get_latest_by = "timestamp"
verbose_name = "Verification Status"
verbose_name_plural = "Verification Statuses"
aamir-khan
committed
zubair-arbi
committed
def add_verification_status(cls, checkpoint, user, status):
"""Create new verification status object.
Arguments:
checkpoint(VerificationCheckpoint): VerificationCheckpoint object
user(User): user object
zubair-arbi
committed
status(str): Status from VERIFICATION_STATUS_CHOICES
zubair-arbi
committed
cls.objects.create(checkpoint=checkpoint, user=user, status=status)
@classmethod
def add_status_from_checkpoints(cls, checkpoints, user, status):
zubair-arbi
committed
"""Create new verification status objects for a user against the given
checkpoints.
Arguments:
checkpoints(list): list of VerificationCheckpoint objects
user(User): user object
zubair-arbi
committed
status(str): Status from VERIFICATION_STATUS_CHOICES
Returns:
None
"""
for checkpoint in checkpoints:
zubair-arbi
committed
cls.objects.create(checkpoint=checkpoint, user=user, status=status)
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
@classmethod
def get_user_status_at_checkpoint(cls, user, course_key, location):
"""
Get the user's latest status at the checkpoint.
Arguments:
user (User): The user whose status we are retrieving.
course_key (CourseKey): The identifier for the course.
location (UsageKey): The location of the checkpoint in the course.
Returns:
unicode or None
"""
try:
return cls.objects.filter(
user=user,
checkpoint__course_id=course_key,
checkpoint__checkpoint_location=unicode(location),
).latest().status
except cls.DoesNotExist:
return None
zubair-arbi
committed
def get_user_attempts(cls, user_id, course_key, related_assessment_location):
"""
Get re-verification attempts against a user for a given 'checkpoint'
and 'course_id'.
Arguments:
user_id(str): User Id string
course_key(str): A CourseKey of a course
zubair-arbi
committed
related_assessment_location(str): Verification checkpoint location
zubair-arbi
committed
Count of re-verification attempts
"""
return cls.objects.filter(
user_id=user_id,
checkpoint__course_id=course_key,
zubair-arbi
committed
checkpoint__checkpoint_location=related_assessment_location,
status="submitted"
).count()
@classmethod
def get_location_id(cls, photo_verification):
zubair-arbi
committed
"""Get the location ID of reverification XBlock.
Args:
zubair-arbi
committed
photo_verification(object): SoftwareSecurePhotoVerification object
Return:
zubair-arbi
committed
Location Id of XBlock if any else empty string
"""
try:
zubair-arbi
committed
verification_status = cls.objects.filter(checkpoint__photo_verification=photo_verification).latest()
return verification_status.checkpoint.checkpoint_location
except cls.DoesNotExist:
return ""
# DEPRECATED: this feature has been permanently enabled.
# Once the application code has been updated in production,
# this table can be safely deleted.
class InCourseReverificationConfiguration(ConfigurationModel):
"""Configure in-course re-verification.
Enable or disable in-course re-verification feature.
When this flag is disabled, the "in-course re-verification" feature
will be disabled.
When the flag is enabled, the "in-course re-verification" feature
will be enabled.
"""
pass
class SkippedReverification(models.Model):
zubair-arbi
committed
"""Model for tracking skipped Reverification of a user against a specific
zubair-arbi
committed
If a user skipped a Reverification checkpoint for a specific course then in
future that user cannot see the reverification link.
"""
user = models.ForeignKey(User)
course_id = CourseKeyField(max_length=255, db_index=True)
checkpoint = models.ForeignKey(VerificationCheckpoint, related_name="skipped_checkpoint")
created_at = models.DateTimeField(auto_now_add=True)
class Meta(object): # pylint: disable=missing-docstring
unique_together = (('user', 'course_id'),)
@classmethod
def add_skipped_reverification_attempt(cls, checkpoint, user_id, course_id):
zubair-arbi
committed
"""Create skipped reverification object.
Arguments:
checkpoint(VerificationCheckpoint): VerificationCheckpoint object
user_id(str): User Id of currently logged in user
course_id(CourseKey): CourseKey
zubair-arbi
committed
Returns:
None
"""
cls.objects.create(checkpoint=checkpoint, user_id=user_id, course_id=course_id)
@classmethod
def check_user_skipped_reverification_exists(cls, user, course_id):
zubair-arbi
committed
"""Check existence of a user's skipped re-verification attempt for a
specific course.
Arguments:
user(User): user object
course_id(CourseKey): CourseKey
zubair-arbi
committed