ECOM-1524: Display credit availability on the dashboard
This commit is contained in:
@@ -26,6 +26,7 @@ from .exceptions import (
|
||||
)
|
||||
from .models import (
|
||||
CreditCourse,
|
||||
CreditProvider,
|
||||
CreditRequirement,
|
||||
CreditRequirementStatus,
|
||||
CreditRequest,
|
||||
@@ -33,6 +34,7 @@ from .models import (
|
||||
)
|
||||
from .signature import signature, get_shared_secret_key
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -211,14 +213,13 @@ def create_credit_request(course_key, provider_id, username):
|
||||
|
||||
"""
|
||||
try:
|
||||
user_eligibility = CreditEligibility.objects.select_related('course', 'provider').get(
|
||||
user_eligibility = CreditEligibility.objects.select_related('course').get(
|
||||
username=username,
|
||||
course__course_key=course_key,
|
||||
provider__provider_id=provider_id
|
||||
course__course_key=course_key
|
||||
)
|
||||
credit_course = user_eligibility.course
|
||||
credit_provider = user_eligibility.provider
|
||||
except CreditEligibility.DoesNotExist:
|
||||
credit_provider = credit_course.providers.get(provider_id=provider_id)
|
||||
except (CreditEligibility.DoesNotExist, CreditProvider.DoesNotExist):
|
||||
log.warning(u'User tried to initiate a request for credit, but the user is not eligible for credit')
|
||||
raise UserIsNotEligible
|
||||
|
||||
@@ -614,3 +615,132 @@ def is_credit_course(course_key):
|
||||
return False
|
||||
|
||||
return CreditCourse.is_credit_course(course_key=course_key)
|
||||
|
||||
|
||||
def get_credit_request_status(username, course_key):
|
||||
"""Get the credit request status.
|
||||
|
||||
This function returns the status of credit request of user for given course.
|
||||
It returns the latest request status for the any credit provider.
|
||||
The valid status are 'pending', 'approved' or 'rejected'.
|
||||
|
||||
Args:
|
||||
username(str): The username of user
|
||||
course_key(CourseKey): The course locator key
|
||||
|
||||
Returns:
|
||||
A dictionary of credit request user has made if any
|
||||
|
||||
"""
|
||||
credit_request = CreditRequest.get_user_request_status(username, course_key)
|
||||
if credit_request:
|
||||
credit_status = {
|
||||
"uuid": credit_request.uuid,
|
||||
"timestamp": credit_request.modified,
|
||||
"course_key": credit_request.course.course_key,
|
||||
"provider": {
|
||||
"id": credit_request.provider.provider_id,
|
||||
"display_name": credit_request.provider.display_name
|
||||
},
|
||||
"status": credit_request.status
|
||||
}
|
||||
else:
|
||||
credit_status = {}
|
||||
return credit_status
|
||||
|
||||
|
||||
def _get_duration_and_providers(credit_course):
|
||||
"""Returns the credit providers and eligibility durations.
|
||||
|
||||
The eligibility_duration is the max of the credit duration of
|
||||
all the credit providers of given course.
|
||||
|
||||
Args:
|
||||
credit_course(CreditCourse): The CreditCourse object
|
||||
|
||||
Returns:
|
||||
Tuple of eligibility_duration and credit providers of given course
|
||||
|
||||
"""
|
||||
providers = credit_course.providers.all()
|
||||
seconds_good_for_display = 0
|
||||
providers_list = []
|
||||
for provider in providers:
|
||||
providers_list.append(
|
||||
{
|
||||
"id": provider.provider_id,
|
||||
"display_name": provider.display_name,
|
||||
"eligibility_duration": provider.eligibility_duration,
|
||||
"provider_url": provider.provider_url
|
||||
}
|
||||
)
|
||||
eligibility_duration = int(provider.eligibility_duration) if provider.eligibility_duration else 0
|
||||
seconds_good_for_display = max(eligibility_duration, seconds_good_for_display)
|
||||
|
||||
return seconds_good_for_display, providers_list
|
||||
|
||||
|
||||
def get_credit_eligibility(username):
|
||||
"""
|
||||
Returns the all the eligibility the user has meet.
|
||||
|
||||
Args:
|
||||
username(str): The username of user
|
||||
|
||||
Example:
|
||||
>> get_credit_eligibility('Aamir'):
|
||||
{
|
||||
"edX/DemoX/Demo_Course": {
|
||||
"created_at": "2015-12-21",
|
||||
"providers": [
|
||||
"id": 12,
|
||||
"display_name": "Arizona State University",
|
||||
"eligibility_duration": 60,
|
||||
"provider_url": "http://arizona/provideere/link"
|
||||
],
|
||||
"seconds_good_for_display": 90
|
||||
}
|
||||
}
|
||||
|
||||
Returns:
|
||||
A dict of eligibilities
|
||||
"""
|
||||
eligibilities = CreditEligibility.get_user_eligibility(username)
|
||||
user_credit_requests = get_credit_requests_for_user(username)
|
||||
request_dict = {}
|
||||
# Change the list to dict for iteration
|
||||
for request in user_credit_requests:
|
||||
request_dict[unicode(request["course_key"])] = request
|
||||
user_eligibilities = {}
|
||||
for eligibility in eligibilities:
|
||||
course_key = eligibility.course.course_key
|
||||
duration, providers_list = _get_duration_and_providers(eligibility.course)
|
||||
user_eligibilities[unicode(course_key)] = {
|
||||
"created_at": eligibility.created,
|
||||
"seconds_good_for_display": duration,
|
||||
"providers": providers_list,
|
||||
}
|
||||
|
||||
# Default status is requirements_meet
|
||||
user_eligibilities[unicode(course_key)]["status"] = "requirements_meet"
|
||||
# If there is some request user has made for this eligibility then update the status
|
||||
if unicode(course_key) in request_dict:
|
||||
user_eligibilities[unicode(course_key)]["status"] = request_dict[unicode(course_key)]["status"]
|
||||
user_eligibilities[unicode(course_key)]["provider"] = request_dict[unicode(course_key)]["provider"]
|
||||
|
||||
return user_eligibilities
|
||||
|
||||
|
||||
def get_purchased_credit_courses(username): # pylint: disable=unused-argument
|
||||
"""
|
||||
Returns the purchased credit courses.
|
||||
|
||||
Args:
|
||||
username(str): Username of the student
|
||||
|
||||
Returns:
|
||||
A dict of courses user has purchased from the credit provider after completion
|
||||
|
||||
"""
|
||||
# TODO: How to track the purchased courses. It requires Will's work for credit provider integration
|
||||
return {}
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
# -*- 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):
|
||||
# Deleting field 'CreditEligibility.provider'
|
||||
db.delete_column('credit_crediteligibility', 'provider_id')
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
# Adding field 'CreditEligibility.provider'
|
||||
db.add_column('credit_crediteligibility', 'provider',
|
||||
self.gf('django.db.models.fields.related.ForeignKey')(default=1, related_name='eligibilities', to=orm['credit.CreditProvider']),
|
||||
keep_default=False)
|
||||
|
||||
|
||||
models = {
|
||||
'auth.group': {
|
||||
'Meta': {'object_name': 'Group'},
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
|
||||
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
|
||||
},
|
||||
'auth.permission': {
|
||||
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
|
||||
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
|
||||
},
|
||||
'auth.user': {
|
||||
'Meta': {'object_name': 'User'},
|
||||
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
|
||||
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
||||
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
|
||||
},
|
||||
'contenttypes.contenttype': {
|
||||
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
|
||||
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
|
||||
},
|
||||
'credit.creditcourse': {
|
||||
'Meta': {'object_name': 'CreditCourse'},
|
||||
'course_key': ('xmodule_django.models.CourseKeyField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
|
||||
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'providers': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['credit.CreditProvider']", 'symmetrical': 'False'})
|
||||
},
|
||||
'credit.crediteligibility': {
|
||||
'Meta': {'unique_together': "(('username', 'course'),)", 'object_name': 'CreditEligibility'},
|
||||
'course': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'eligibilities'", 'to': "orm['credit.CreditCourse']"}),
|
||||
'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}),
|
||||
'username': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
|
||||
},
|
||||
'credit.creditprovider': {
|
||||
'Meta': {'object_name': 'CreditProvider'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}),
|
||||
'display_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'eligibility_duration': ('django.db.models.fields.PositiveIntegerField', [], {'default': '31556970'}),
|
||||
'enable_integration': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}),
|
||||
'provider_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
|
||||
'provider_url': ('django.db.models.fields.URLField', [], {'default': "''", 'max_length': '200'})
|
||||
},
|
||||
'credit.creditrequest': {
|
||||
'Meta': {'unique_together': "(('username', 'course', 'provider'),)", 'object_name': 'CreditRequest'},
|
||||
'course': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'credit_requests'", 'to': "orm['credit.CreditCourse']"}),
|
||||
'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}),
|
||||
'parameters': ('jsonfield.fields.JSONField', [], {}),
|
||||
'provider': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'credit_requests'", 'to': "orm['credit.CreditProvider']"}),
|
||||
'status': ('django.db.models.fields.CharField', [], {'default': "'pending'", 'max_length': '255'}),
|
||||
'username': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
|
||||
'uuid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32', 'db_index': 'True'})
|
||||
},
|
||||
'credit.creditrequirement': {
|
||||
'Meta': {'unique_together': "(('namespace', 'name', 'course'),)", 'object_name': 'CreditRequirement'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'course': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'credit_requirements'", 'to': "orm['credit.CreditCourse']"}),
|
||||
'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}),
|
||||
'criteria': ('jsonfield.fields.JSONField', [], {}),
|
||||
'display_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'namespace': ('django.db.models.fields.CharField', [], {'max_length': '255'})
|
||||
},
|
||||
'credit.creditrequirementstatus': {
|
||||
'Meta': {'object_name': 'CreditRequirementStatus'},
|
||||
'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}),
|
||||
'reason': ('jsonfield.fields.JSONField', [], {'default': '{}'}),
|
||||
'requirement': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'statuses'", 'to': "orm['credit.CreditRequirement']"}),
|
||||
'status': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
|
||||
'username': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
|
||||
},
|
||||
'credit.historicalcreditrequest': {
|
||||
'Meta': {'ordering': "(u'-history_date', u'-history_id')", 'object_name': 'HistoricalCreditRequest'},
|
||||
'course': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'+'", 'null': 'True', 'on_delete': 'models.DO_NOTHING', 'to': "orm['credit.CreditCourse']"}),
|
||||
'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}),
|
||||
u'history_date': ('django.db.models.fields.DateTimeField', [], {}),
|
||||
u'history_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
u'history_type': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
|
||||
u'history_user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['auth.User']"}),
|
||||
'id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'blank': 'True'}),
|
||||
'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}),
|
||||
'parameters': ('jsonfield.fields.JSONField', [], {}),
|
||||
'provider': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'+'", 'null': 'True', 'on_delete': 'models.DO_NOTHING', 'to': "orm['credit.CreditProvider']"}),
|
||||
'status': ('django.db.models.fields.CharField', [], {'default': "'pending'", 'max_length': '255'}),
|
||||
'username': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
|
||||
'uuid': ('django.db.models.fields.CharField', [], {'max_length': '32', 'db_index': 'True'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['credit']
|
||||
@@ -307,11 +307,23 @@ class CreditEligibility(TimeStampedModel):
|
||||
"""
|
||||
username = models.CharField(max_length=255, db_index=True)
|
||||
course = models.ForeignKey(CreditCourse, related_name="eligibilities")
|
||||
provider = models.ForeignKey(CreditProvider, related_name="eligibilities")
|
||||
|
||||
class Meta(object): # pylint: disable=missing-docstring
|
||||
unique_together = ('username', 'course')
|
||||
|
||||
@classmethod
|
||||
def get_user_eligibility(cls, username):
|
||||
"""Returns the eligibilities of given user.
|
||||
|
||||
Args:
|
||||
username(str): Username of the user
|
||||
|
||||
Returns:
|
||||
CreditEligibility queryset for the user
|
||||
|
||||
"""
|
||||
return cls.objects.filter(username=username).select_related('course').prefetch_related('course__providers')
|
||||
|
||||
@classmethod
|
||||
def is_user_eligible_for_credit(cls, course_key, username):
|
||||
"""Check if the given user is eligible for the provided credit course
|
||||
@@ -361,6 +373,12 @@ class CreditRequest(TimeStampedModel):
|
||||
|
||||
history = HistoricalRecords()
|
||||
|
||||
class Meta(object): # pylint: disable=missing-docstring
|
||||
# Enforce the constraint that each user can have exactly one outstanding
|
||||
# request to a given provider. Multiple requests use the same UUID.
|
||||
unique_together = ('username', 'course', 'provider')
|
||||
get_latest_by = 'created'
|
||||
|
||||
@classmethod
|
||||
def credit_requests_for_user(cls, username):
|
||||
"""
|
||||
@@ -402,7 +420,21 @@ class CreditRequest(TimeStampedModel):
|
||||
for request in cls.objects.select_related('course', 'provider').filter(username=username)
|
||||
]
|
||||
|
||||
class Meta(object): # pylint: disable=missing-docstring
|
||||
# Enforce the constraint that each user can have exactly one outstanding
|
||||
# request to a given provider. Multiple requests use the same UUID.
|
||||
unique_together = ('username', 'course', 'provider')
|
||||
@classmethod
|
||||
def get_user_request_status(cls, username, course_key):
|
||||
"""Returns the latest credit request of user against the given course.
|
||||
|
||||
Args:
|
||||
username(str): The username of requesting user
|
||||
course_key(CourseKey): The course identifier
|
||||
|
||||
Returns:
|
||||
CreditRequest if any otherwise None
|
||||
|
||||
"""
|
||||
try:
|
||||
return cls.objects.filter(
|
||||
username=username, course__course_key=course_key
|
||||
).select_related('course', 'provider').latest()
|
||||
except cls.DoesNotExist:
|
||||
return None
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
"""
|
||||
Tests for the API functions in the credit app.
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import datetime
|
||||
import ddt
|
||||
import pytz
|
||||
from django.test import TestCase
|
||||
from django.test.utils import override_settings
|
||||
from django.db import connection, transaction
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.conf import settings
|
||||
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
|
||||
from student.tests.factories import UserFactory
|
||||
from util.date_utils import from_timestamp
|
||||
from openedx.core.djangoapps.credit import api
|
||||
from openedx.core.djangoapps.credit.exceptions import (
|
||||
@@ -34,13 +35,20 @@ from openedx.core.djangoapps.credit.api import (
|
||||
set_credit_requirement_status,
|
||||
get_credit_requirement
|
||||
)
|
||||
from student.models import CourseEnrollment
|
||||
from student.views import _create_credit_availability_message
|
||||
from student.tests.factories import UserFactory
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
|
||||
|
||||
TEST_CREDIT_PROVIDER_SECRET_KEY = "931433d583c84ca7ba41784bad3232e6"
|
||||
|
||||
|
||||
@override_settings(CREDIT_PROVIDER_SECRET_KEYS={
|
||||
"hogwarts": TEST_CREDIT_PROVIDER_SECRET_KEY
|
||||
"hogwarts": TEST_CREDIT_PROVIDER_SECRET_KEY,
|
||||
"ASU": TEST_CREDIT_PROVIDER_SECRET_KEY,
|
||||
"MIT": TEST_CREDIT_PROVIDER_SECRET_KEY
|
||||
})
|
||||
class CreditApiTestBase(TestCase):
|
||||
"""
|
||||
@@ -212,7 +220,7 @@ class CreditRequirementApiTests(CreditApiTestBase):
|
||||
def test_is_user_eligible_for_credit(self):
|
||||
credit_course = self.add_credit_course()
|
||||
CreditEligibility.objects.create(
|
||||
course=credit_course, username="staff", provider=CreditProvider.objects.get(provider_id=self.PROVIDER_ID)
|
||||
course=credit_course, username="staff"
|
||||
)
|
||||
is_eligible = api.is_user_eligible_for_credit('staff', credit_course.course_key)
|
||||
self.assertTrue(is_eligible)
|
||||
@@ -380,19 +388,26 @@ class CreditProviderIntegrationApiTests(CreditApiTestBase):
|
||||
# Initial status should be "pending"
|
||||
self._assert_credit_status("pending")
|
||||
|
||||
credit_request_status = api.get_credit_request_status(self.USER_INFO['username'], self.course_key)
|
||||
self.assertEqual(credit_request_status["status"], "pending")
|
||||
|
||||
# Update the status
|
||||
api.update_credit_request_status(request["parameters"]["request_uuid"], self.PROVIDER_ID, status)
|
||||
self._assert_credit_status(status)
|
||||
|
||||
credit_request_status = api.get_credit_request_status(self.USER_INFO['username'], self.course_key)
|
||||
self.assertEqual(credit_request_status["status"], status)
|
||||
|
||||
def test_query_counts(self):
|
||||
# Yes, this is a lot of queries, but this API call is also doing a lot of work :)
|
||||
# - 1 query: Check the user's eligibility and retrieve the credit course and provider.
|
||||
# - 1 query: Check the user's eligibility and retrieve the credit course
|
||||
# - 1 Get the provider of the credit course.
|
||||
# - 2 queries: Get-or-create the credit request.
|
||||
# - 1 query: Retrieve user account and profile information from the user API.
|
||||
# - 1 query: Look up the user's final grade from the credit requirements table.
|
||||
# - 2 queries: Update the request.
|
||||
# - 2 queries: Update the history table for the request.
|
||||
with self.assertNumQueries(9):
|
||||
with self.assertNumQueries(10):
|
||||
request = api.create_credit_request(self.course_key, self.PROVIDER_ID, self.USER_INFO['username'])
|
||||
|
||||
# - 3 queries: Retrieve and update the request
|
||||
@@ -522,12 +537,131 @@ class CreditProviderIntegrationApiTests(CreditApiTestBase):
|
||||
status.save()
|
||||
|
||||
CreditEligibility.objects.create(
|
||||
username=self.USER_INFO["username"],
|
||||
course=CreditCourse.objects.get(course_key=self.course_key),
|
||||
provider=CreditProvider.objects.get(provider_id=self.PROVIDER_ID)
|
||||
username=self.USER_INFO['username'],
|
||||
course=CreditCourse.objects.get(course_key=self.course_key)
|
||||
)
|
||||
|
||||
def _assert_credit_status(self, expected_status):
|
||||
"""Check the user's credit status. """
|
||||
statuses = api.get_credit_requests_for_user(self.USER_INFO["username"])
|
||||
self.assertEqual(statuses[0]["status"], expected_status)
|
||||
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
class CreditMessagesTests(ModuleStoreTestCase, CreditApiTestBase):
|
||||
"""
|
||||
Test dashboard messages of credit course.
|
||||
"""
|
||||
|
||||
FINAL_GRADE = 0.8
|
||||
|
||||
def setUp(self):
|
||||
super(CreditMessagesTests, self).setUp()
|
||||
self.student = UserFactory()
|
||||
self.student.set_password('test') # pylint: disable=no-member
|
||||
self.student.save() # pylint: disable=no-member
|
||||
|
||||
self.client.login(username=self.student.username, password='test')
|
||||
# New Course
|
||||
self.course = CourseFactory.create()
|
||||
self.enrollment = CourseEnrollment.enroll(self.student, self.course.id)
|
||||
|
||||
def _set_creditcourse(self):
|
||||
"""
|
||||
Mark the course to credit
|
||||
|
||||
"""
|
||||
# pylint: disable=attribute-defined-outside-init
|
||||
self.first_provider = CreditProvider.objects.create(
|
||||
provider_id="ASU",
|
||||
display_name="Arizona State University",
|
||||
provider_url="google.com",
|
||||
enable_integration=True
|
||||
) # pylint: disable=attribute-defined-outside-init
|
||||
self.second_provider = CreditProvider.objects.create(
|
||||
provider_id="MIT",
|
||||
display_name="Massachusetts Institute of Technology",
|
||||
provider_url="MIT.com",
|
||||
enable_integration=True
|
||||
) # pylint: disable=attribute-defined-outside-init
|
||||
|
||||
self.credit_course = CreditCourse.objects.create(course_key=self.course.id, enabled=True) # pylint: disable=attribute-defined-outside-init
|
||||
self.credit_course.providers.add(self.first_provider)
|
||||
self.credit_course.providers.add(self.second_provider)
|
||||
|
||||
def _set_user_eligible(self, credit_course, username):
|
||||
"""
|
||||
Mark the user eligible for credit for the given credit course.
|
||||
"""
|
||||
self.eligibility = CreditEligibility.objects.create(username=username, course=credit_course) # pylint: disable=attribute-defined-outside-init
|
||||
|
||||
def test_user_request_status(self):
|
||||
request_status = api.get_credit_request_status(self.student.username, self.course.id)
|
||||
self.assertEqual(len(request_status), 0)
|
||||
|
||||
def test_credit_messages(self):
|
||||
self._set_creditcourse()
|
||||
|
||||
requirement = CreditRequirement.objects.create(
|
||||
course=self.credit_course,
|
||||
namespace="grade",
|
||||
name="grade",
|
||||
active=True
|
||||
)
|
||||
status = CreditRequirementStatus.objects.create(
|
||||
username=self.student.username,
|
||||
requirement=requirement,
|
||||
)
|
||||
status.status = "satisfied"
|
||||
status.reason = {"final_grade": self.FINAL_GRADE}
|
||||
status.save()
|
||||
|
||||
self._set_user_eligible(self.credit_course, self.student.username)
|
||||
response = self.client.get(reverse("dashboard"))
|
||||
self.assertContains(
|
||||
response,
|
||||
"<b>Congratulations</b> {}, You have meet requirements for credit.".format(
|
||||
self.student.get_full_name() # pylint: disable=no-member
|
||||
)
|
||||
)
|
||||
|
||||
api.create_credit_request(self.course.id, self.first_provider.provider_id, self.student.username)
|
||||
|
||||
response = self.client.get(reverse("dashboard"))
|
||||
self.assertContains(
|
||||
response,
|
||||
'Thank you, your payment is complete, your credit is processing. '
|
||||
'Please see {provider_link} for more information.'.format(
|
||||
provider_link='<a href="#" target="_blank">{provider_name}</a>'.format(
|
||||
provider_name=self.first_provider.display_name
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
def test_query_counts(self):
|
||||
# This check the number of queries executed while rendering the
|
||||
# credit message to display on the dashboard.
|
||||
# - 1 query: Check the user's eligibility.
|
||||
# - 1 query: Get the user credit requests.
|
||||
|
||||
self._set_creditcourse()
|
||||
|
||||
requirement = CreditRequirement.objects.create(
|
||||
course=self.credit_course,
|
||||
namespace="grade",
|
||||
name="grade",
|
||||
active=True
|
||||
)
|
||||
status = CreditRequirementStatus.objects.create(
|
||||
username=self.student.username,
|
||||
requirement=requirement,
|
||||
)
|
||||
status.status = "satisfied"
|
||||
status.reason = {"final_grade": self.FINAL_GRADE}
|
||||
status.save()
|
||||
|
||||
with self.assertNumQueries(2):
|
||||
enrollment_dict = {unicode(self.course.id): self.course}
|
||||
_create_credit_availability_message(
|
||||
enrollment_dict, self.student
|
||||
)
|
||||
|
||||
@@ -99,7 +99,6 @@ class CreditProviderViewTests(UrlResetMixin, TestCase):
|
||||
CreditEligibility.objects.create(
|
||||
username=self.USERNAME,
|
||||
course=credit_course,
|
||||
provider=credit_provider,
|
||||
)
|
||||
|
||||
def test_credit_request_and_response(self):
|
||||
|
||||
Reference in New Issue
Block a user