diff --git a/lms/templates/dashboard/_dashboard_credit_information.html b/lms/templates/dashboard/_dashboard_credit_information.html
new file mode 100644
index 0000000000..efdb39337b
--- /dev/null
+++ b/lms/templates/dashboard/_dashboard_credit_information.html
@@ -0,0 +1,70 @@
+<%page args="credit_message" />
+
+<%!
+from django.utils.translation import ugettext as _
+from course_modes.models import CourseMode
+from util.date_utils import get_default_time_display
+%>
+<%namespace name='static' file='../static_content.html'/>
+
+<%block name="js_extra" args="credit_message">
+ <%static:js group='credit_wv'/>
+
+%block>
+
+
+
+ % if credit_message["status"] == "requirements_meet":
+
+ % if credit_message["urgent"]:
+ ${_("{username}, your eligibility for credit expires on {expiry}. Don't miss out!").format(
+ username=credit_message["user_full_name"],
+ expiry=get_default_time_display(credit_message["expiry"])
+ )
+ }
+ % else:
+ ${_("{congrats} {username}, You have meet requirements for credit.").format(
+ congrats="Congratulations",
+ username=credit_message["user_full_name"]
+ )
+ }
+ % endif
+
+ ${_("Purchase Credit")}
+
+ % elif credit_message["status"] == "pending":
+ ${_("Thank you, your payment is complete, your credit is processing. Please see {provider_link} for more information.").format(
+ provider_link='{}'.format(credit_message["provider"]["display_name"])
+ )
+ }
+ % elif credit_message["status"] == "approved":
+ ${_("Thank you, your credit is approved. Please see {provider_link} for more information.").format(
+ provider_link='{}'.format(credit_message["provider"]["display_name"])
+ )
+ }
+ % elif credit_message["status"] == "rejected":
+ ${_("Your credit has been denied. Please contact {provider_link} for more information.").format(
+ provider_link='{}'.format(credit_message["provider"]["display_name"])
+ )
+ }
+ % endif
+
+
+
+
diff --git a/openedx/core/djangoapps/credit/api.py b/openedx/core/djangoapps/credit/api.py
index d678c1d91a..bb567d7ea1 100644
--- a/openedx/core/djangoapps/credit/api.py
+++ b/openedx/core/djangoapps/credit/api.py
@@ -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 {}
diff --git a/openedx/core/djangoapps/credit/migrations/0009_auto__del_field_crediteligibility_provider.py b/openedx/core/djangoapps/credit/migrations/0009_auto__del_field_crediteligibility_provider.py
new file mode 100644
index 0000000000..73cd9be397
--- /dev/null
+++ b/openedx/core/djangoapps/credit/migrations/0009_auto__del_field_crediteligibility_provider.py
@@ -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']
\ No newline at end of file
diff --git a/openedx/core/djangoapps/credit/models.py b/openedx/core/djangoapps/credit/models.py
index 9187be4b01..d107ceee77 100644
--- a/openedx/core/djangoapps/credit/models.py
+++ b/openedx/core/djangoapps/credit/models.py
@@ -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
diff --git a/openedx/core/djangoapps/credit/tests/test_api.py b/openedx/core/djangoapps/credit/tests/test_api.py
index 91a902a3f2..dd2f3cbd83 100644
--- a/openedx/core/djangoapps/credit/tests/test_api.py
+++ b/openedx/core/djangoapps/credit/tests/test_api.py
@@ -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,
+ "
Congratulations {}, 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='
{provider_name}'.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
+ )
diff --git a/openedx/core/djangoapps/credit/tests/test_views.py b/openedx/core/djangoapps/credit/tests/test_views.py
index ec7a8ba67c..d454009ace 100644
--- a/openedx/core/djangoapps/credit/tests/test_views.py
+++ b/openedx/core/djangoapps/credit/tests/test_views.py
@@ -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):