feat: add course_status utility (#32545)
* feat: add course_status utility
This commit is contained in:
committed by
GitHub
parent
74e3bb9fd4
commit
ee871730c8
@@ -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'
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user