feat: add course_status utility (#32545)

* feat: add course_status utility
This commit is contained in:
Mohammad Ahtasham ul Hassan
2023-07-07 12:38:05 +05:00
committed by GitHub
parent 74e3bb9fd4
commit ee871730c8
4 changed files with 109 additions and 4 deletions

View File

@@ -4477,6 +4477,7 @@ STUDENTMODULEHISTORYEXTENDED_OFFSET = 10000
CREDENTIALS_SERVICE_USERNAME = 'credentials_service_user'
CREDENTIALS_GENERATION_ROUTING_KEY = DEFAULT_PRIORITY_QUEUE
CREDENTIALS_COURSE_COMPLETION_STATE = 'awarded'
# Queue to use for award program certificates
PROGRAM_CERTIFICATES_ROUTING_KEY = 'edx.lms.core.default'

View File

@@ -25,3 +25,15 @@ class UserCredential(DictFactoryBase):
uuid = factory.Faker('uuid4')
certificate_url = factory.Faker('url')
credential = ProgramCredential()
class UserCredentialsCourseRunStatus(DictFactoryBase):
course_uuid = str(factory.Faker('uuid4'))
course_run = {
"uuid": str(factory.Faker('uuid4')),
"key": factory.LazyFunction(generate_course_run_key)
}
status = 'awarded'
type = 'verified'
certificate_available_date = factory.Faker('date')
grade = None

View File

@@ -2,13 +2,21 @@
import uuid
from unittest import mock
from django.conf import settings
from requests import Response
from requests.exceptions import HTTPError
from common.djangoapps.student.tests.factories import UserFactory
from openedx.core.djangoapps.credentials.models import CredentialsApiConfig
from openedx.core.djangoapps.credentials.tests import factories
from openedx.core.djangoapps.credentials.tests.mixins import CredentialsApiConfigMixin
from openedx.core.djangoapps.credentials.utils import get_credentials, get_credentials_records_url
from openedx.core.djangoapps.credentials.utils import (
get_courses_completion_status,
get_credentials,
get_credentials_records_url
)
from openedx.core.djangoapps.oauth_dispatch.tests.factories import ApplicationFactory
from openedx.core.djangolib.testing.utils import CacheIsolationTestCase, skip_unless_lms
from common.djangoapps.student.tests.factories import UserFactory
UTILS_MODULE = 'openedx.core.djangoapps.credentials.utils'
@@ -98,3 +106,31 @@ class TestGetCredentials(CredentialsApiConfigMixin, CacheIsolationTestCase):
result = get_credentials_records_url("abcdefgh-ijkl-mnop-qrst-uvwxyz123456")
assert result == "https://credentials.example.com/records/programs/abcdefghijklmnopqrstuvwxyz123456"
@mock.patch('requests.Response.raise_for_status')
@mock.patch('requests.Response.json')
@mock.patch(UTILS_MODULE + '.get_credentials_api_client')
def test_get_courses_completion_status(self, mock_get_api_client, mock_json, mock_raise):
"""
Test to verify the functionality of get_courses_completion_status
"""
UserFactory.create(username=settings.CREDENTIALS_SERVICE_USERNAME)
course_statuses = factories.UserCredentialsCourseRunStatus.create_batch(3)
response_data = [course_status['course_run']['key'] for course_status in course_statuses]
mock_raise.return_value = None
mock_json.return_value = {'lms_user_id': self.user.id,
'status': course_statuses,
'username': self.user.username}
mock_get_api_client.return_value.post.return_value = Response()
course_run_keys = [course_status['course_run']['key'] for course_status in course_statuses]
api_response, is_exception = get_courses_completion_status(self.user.id, course_run_keys)
assert api_response == response_data
assert is_exception is False
@mock.patch('requests.Response.raise_for_status')
def test_get_courses_completion_status_api_error(self, mock_raise):
mock_raise.return_value = HTTPError('An Error occured')
UserFactory.create(username=settings.CREDENTIALS_SERVICE_USERNAME)
api_response, is_exception = get_courses_completion_status(self.user.id, ['fake1', 'fake2', 'fake3'])
assert api_response == []
assert is_exception is True

View File

@@ -1,12 +1,19 @@
"""Helper functions for working with Credentials."""
import requests
from edx_rest_api_client.auth import SuppliedJwtAuth
import logging
from urllib.parse import urljoin
import requests
from django.conf import settings
from django.contrib.auth import get_user_model
from edx_rest_api_client.auth import SuppliedJwtAuth
from openedx.core.djangoapps.credentials.models import CredentialsApiConfig
from openedx.core.djangoapps.oauth_dispatch.jwt import create_jwt_for_user
from openedx.core.lib.edx_api_utils import get_api_data
log = logging.getLogger(__name__)
User = get_user_model()
def get_credentials_records_url(program_uuid=None):
"""
@@ -101,3 +108,52 @@ def get_credentials(user, program_uuid=None, credential_type=None):
querystring=querystring,
cache_key=cache_key
)
def get_courses_completion_status(lms_user_id, course_run_ids):
"""
Given the lms_user_id and course run ids, checks for course completion status
Arguments:
lms_user_id (User): The user to authenticate as when requesting credentials.
course_run_ids(List): list of course run ids for which we need to check the completion status
Returns:
list of course_run_ids for which user has completed the course
Boolean: True if an exception occurred while calling the api, False otherwise
"""
credential_configuration = CredentialsApiConfig.current()
if not credential_configuration.enabled:
log.warning('%s configuration is disabled.', credential_configuration.API_NAME)
return [], False
base_api_url = get_credentials_api_base_url()
completion_status_url = f'{base_api_url}/api/credentials/learner_cert_status'
try:
api_client = get_credentials_api_client(
User.objects.get(username=settings.CREDENTIALS_SERVICE_USERNAME)
)
api_response = api_client.post(
completion_status_url,
data={
'lms_user_id': lms_user_id,
'course_runs': course_run_ids,
}
)
api_response.raise_for_status()
course_completion_response = api_response.json()
except Exception as exc: # pylint: disable=broad-except
log.exception("An unexpected error occurred while reqeusting course completion statuses "
"for lms_user_id [%s] for course_run_ids [%s] with exc [%s]:",
lms_user_id,
course_run_ids,
exc
)
return [], True
# Yes, This is course_credentials_data. The key is named status but
# it contains all the courses data from credentials.
course_credentials_data = course_completion_response.get('status', [])
if course_credentials_data is not None:
filtered_records = [course_data['course_run']['key'] for course_data in course_credentials_data if
course_data['course_run']['key'] in course_run_ids and
course_data['status'] == settings.CREDENTIALS_COURSE_COMPLETION_STATE]
return filtered_records, False
return [], False