Merge pull request #32365 from openedx/btahir/ENT-7192

feat: management command to fetch enterprise learner current job in profile
This commit is contained in:
Muhammad Bilal Tahir
2023-06-07 15:45:19 +05:00
committed by GitHub

View File

@@ -0,0 +1,143 @@
""" Management command for populaing current job of learners in learner profile. """
import logging
from edx_rest_api_client.client import OAuthAPIClient
from urllib.parse import urljoin
from django.core.exceptions import ObjectDoesNotExist
from django.conf import settings
from django.core.management.base import BaseCommand
from common.djangoapps.student.models import User
from openedx.core.djangoapps.user_api import errors
LOGGER = logging.getLogger(__name__)
class Command(BaseCommand):
"""
Command to populate current job of learners in extended learner profile.
This command will fetch the current job of learners from course-discovery service
and populate it in the extended learner profile in edx-platform. This command is
supposed to be run only once in the system.
Example usage:
$ # Update the current job of learners.
$ ./manage.py lms populate_enterprise_learner_current_job
"""
help = 'Populates current job of learners in extended learner profile.'
TOTAL_USERS_COUNT = 0
TOTAL_USERS_UPDATED = 0
def _get_edx_api_client(self):
return OAuthAPIClient(
base_url=settings.ENTERPRISE_BACKEND_SERVICE_EDX_OAUTH2_PROVIDER_URL,
client_id=settings.ENTERPRISE_BACKEND_SERVICE_EDX_OAUTH2_KEY,
client_secret=settings.ENTERPRISE_BACKEND_SERVICE_EDX_OAUTH2_SECRET,
)
def _fetch_learner_job_data(self, url=None):
"""
Get the username and current job of learners from discovery service.
Returns:
list: List of dictionaries containing username and current job of learners.
"""
client = self._get_edx_api_client()
if not url:
course_discovery_url = settings.COURSE_CATALOG_URL_ROOT
learner_jobs_endpoint = '/taxonomy/api/v1/learners-current-job/?page_size=1000'
url = urljoin(course_discovery_url, learner_jobs_endpoint)
response = client.get(url)
response.raise_for_status()
response = response.json()
self.TOTAL_USERS_COUNT = response['count']
return response
def _get_user_profile(self, username):
"""
Helper method to return the user profile object based on username.
"""
try:
user = User.objects.filter(username=username).select_related('profile').first()
except ObjectDoesNotExist:
raise errors.UserNotFound() # lint-amnesty, pylint: disable=raise-missing-from
return user.profile
def _update_current_job_of_learner(self, username, current_job):
"""
Update the current job of learner in their extended profile.
Args:
user_profile (UserProfile): Extended profile of the learner.
current_job (number): Current job of the learner.
"""
try:
user_profile = self._get_user_profile(username)
meta = user_profile.get_meta()
meta['enterprise_learner_current_job'] = current_job
user_profile.set_meta(meta)
user_profile.save()
self.TOTAL_USERS_UPDATED += 1
except Exception: # lint-amnesty, pylint: disable=broad-except
LOGGER.exception('Could not update profile of user %s as %s.', username, current_job)
def _populate_learner_current_job(self, response):
"""
Populate the current job of learners in extended learner profile. An example of
response is as follows:
[
{
"username": "learner1",
"current_job": 1
},
{
"username": "learner2",
"current_job": 2
}
]
Args:
response (list): List of dictionaries containing username and current job of learners.
Returns:
None
"""
for learner_data in response:
self._update_current_job_of_learner(
username=learner_data['username'],
current_job=learner_data['current_job'],
)
def _populate_learner_profiles(self):
"""
Populate the current job of learners in extended learner profile.
"""
response = self._fetch_learner_job_data()
self._populate_learner_current_job(response['results'])
while response['next']:
response = self._fetch_learner_job_data(response['next'])
self._populate_learner_current_job(response['results'])
def handle(self, *args, **options):
"""
Handle the command.
Args:
*args: Variable length argument list.
**options: Arbitrary keyword arguments.
"""
try:
LOGGER.info('Populating current job of learners in their extended profiles.')
self._populate_learner_profiles()
LOGGER.info('Successfully populated current job of %s learner(s) from %s total learners.',
self.TOTAL_USERS_UPDATED, self.TOTAL_USERS_COUNT)
except Exception as err: # lint-amnesty, pylint: disable=broad-except
LOGGER.exception('Could not populate current job of learners. %s', err)