diff --git a/docs/lms-openapi.yaml b/docs/lms-openapi.yaml index 8011f84b4573..4d318f8a2b62 100644 --- a/docs/lms-openapi.yaml +++ b/docs/lms-openapi.yaml @@ -9291,21 +9291,6 @@ paths: in: path required: true type: string - /user/v1/skill_level/{job_id}/: - get: - operationId: user_v1_skill_level_read - description: GET /api/user/v1/skill_level/{job_id}/ - parameters: [] - responses: - '200': - description: '' - tags: - - user - parameters: - - name: job_id - in: path - required: true - type: string /user/v1/user_prefs/: get: operationId: user_v1_user_prefs_list diff --git a/openedx/core/djangoapps/user_api/learner_skill_levels/__init__.py b/openedx/core/djangoapps/user_api/learner_skill_levels/__init__.py deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/openedx/core/djangoapps/user_api/learner_skill_levels/api.py b/openedx/core/djangoapps/user_api/learner_skill_levels/api.py deleted file mode 100644 index ea1714cb6a36..000000000000 --- a/openedx/core/djangoapps/user_api/learner_skill_levels/api.py +++ /dev/null @@ -1,31 +0,0 @@ -""" -APIs for learner skill levels. -""" -from .utils import get_skills_score, calculate_user_skill_score, generate_skill_score_mapping - - -def get_learner_skill_levels(user, top_categories): - """ - Evaluates learner's skill levels in the given job category. Only considers skills for the categories - and not their sub-categories. - - Params: - user: user for each score is being calculated. - top_categories (List, string): A list of fields (as strings) of job categories and their skills. - Returns: - top_categories: Categories with scores appended to skills. - """ - - # get a skill to score mapping for every course user has passed - skill_score_mapping = generate_skill_score_mapping(user) - for skill_category in top_categories: - category_skills = skill_category['skills'] - get_skills_score(category_skills, skill_score_mapping) - skill_category['user_score'] = calculate_user_skill_score(category_skills) - skill_category['edx_average_score'] = None - sub_categories = skill_category['skills_subcategories'] - for sub_category in sub_categories: - subcategory_skills = sub_category['skills'] - get_skills_score(subcategory_skills, skill_score_mapping) - - return top_categories diff --git a/openedx/core/djangoapps/user_api/learner_skill_levels/constants.py b/openedx/core/djangoapps/user_api/learner_skill_levels/constants.py deleted file mode 100644 index 225dc1faba14..000000000000 --- a/openedx/core/djangoapps/user_api/learner_skill_levels/constants.py +++ /dev/null @@ -1,8 +0,0 @@ -""" -Constants for learner skill levels app. -""" -LEVEL_TYPE_SCORE_MAPPING = { - 'Introductory': 1, - 'Intermediate': 2, - 'Advanced': 3 -} diff --git a/openedx/core/djangoapps/user_api/learner_skill_levels/tests/__init__.py b/openedx/core/djangoapps/user_api/learner_skill_levels/tests/__init__.py deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/openedx/core/djangoapps/user_api/learner_skill_levels/tests/test_utils.py b/openedx/core/djangoapps/user_api/learner_skill_levels/tests/test_utils.py deleted file mode 100644 index fa90feb3c309..000000000000 --- a/openedx/core/djangoapps/user_api/learner_skill_levels/tests/test_utils.py +++ /dev/null @@ -1,219 +0,0 @@ -""" Unit tests for Learner Skill Levels utilities. """ - -import ddt -from collections import defaultdict -from unittest import mock - -from rest_framework.test import APIClient - -from common.djangoapps.student.tests.factories import UserFactory - -from openedx.core.djangoapps.catalog.tests.mixins import CatalogIntegrationMixin -from openedx.core.djangoapps.user_api.learner_skill_levels.utils import ( - calculate_user_skill_score, - generate_skill_score_mapping, - get_base_url, - get_job_holder_usernames, - get_skills_score, - get_top_skill_categories_for_job, - update_category_user_scores_map, - update_edx_average_score, -) -from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase - -from .testutils import ( - DUMMY_CATEGORIES_RESPONSE, - DUMMY_CATEGORIES_WITH_SCORES, - DUMMY_USERNAMES_RESPONSE, - DUMMY_COURSE_DATA_RESPONSE, - DUMMY_USER_SCORES_MAP, -) - - -@ddt.ddt -class LearnerSkillLevelsUtilsTests(SharedModuleStoreTestCase, CatalogIntegrationMixin): - """ - Test LearnerSkillLevel utilities. - """ - SERVICE_USERNAME = 'catalog_service_username' - - def setUp(self): - """ - Unit tests setup. - """ - super().setUp() - - self.client = APIClient() - self.service_user = UserFactory(username=self.SERVICE_USERNAME) - self.catalog_integration = self.create_catalog_integration() - - @mock.patch('openedx.core.djangoapps.user_api.learner_skill_levels.utils.get_course_run_ids') - @mock.patch('openedx.core.djangoapps.user_api.learner_skill_levels.utils.get_course_run_data') - @mock.patch('openedx.core.djangoapps.user_api.learner_skill_levels.utils.get_course_data') - def test_generate_skill_score_mapping( - self, - mock_get_course_data, - mock_get_course_run_data, - mock_get_course_run_ids, - ): - """ - Test that skill-score mapping is returned in correct format. - """ - user = UserFactory(username='edX') - mock_get_course_run_ids.return_value = ['course-v1:AWS+OTP-AWSD12'] - mock_get_course_run_data.return_value = {'course': 'AWS+OTP'} - mock_get_course_data.return_value = DUMMY_COURSE_DATA_RESPONSE - result = generate_skill_score_mapping(user) - expected_response = {"python": 3, "MongoDB": 3, "Data Science": 3} - assert result == expected_response - - @ddt.data( - ([], 0.0), - ( - [ - {"id": 1, "name": "Financial Management", "score": None}, - {"id": 2, "name": "Fintech", "score": None}, - ], 0.0 - ), - ( - [ - {"id": 1, "name": "Financial Management", "score": None}, - {"id": 2, "name": "Fintech", "score": None}, - ], 0.0 - ), - ( - [ - {"id": 1, "name": "Financial Management", "score": 3}, - {"id": 2, "name": "Fintech", "score": 2}, - ], 0.8 - ), - ) - @ddt.unpack - def test_calculate_user_skill_score(self, skills_with_score, expected): - """ - Test that skill-score mapping is returned in correct format. - """ - - result = calculate_user_skill_score(skills_with_score) - assert result == expected - - @ddt.data( - ([], {"Financial Management": 1, "Fintech": 3}, []), - ( - [ - {"id": 1, "name": "Financial Management"}, - {"id": 2, "name": "Fintech"}, - ], - { - "Financial Management": 1, - "Fintech": 3 - }, - [ - {"id": 1, "name": "Financial Management", "score": 1}, - {"id": 2, "name": "Fintech", "score": 3}, - ], - ), - ( - [ - {"id": 1, "name": "Financial Management"}, - {"id": 2, "name": "Fintech"}, - ], - {}, - [ - {"id": 1, "name": "Financial Management", "score": None}, - {"id": 2, "name": "Fintech", "score": None}, - ], - ), - ( - [ - {"id": 1, "name": "Financial Management"}, - {"id": 2, "name": "Fintech"}, - ], - { - "Python": 1, - "AI": 3 - }, - [ - {"id": 1, "name": "Financial Management", "score": None}, - {"id": 2, "name": "Fintech", "score": None}, - ], - ), - ) - @ddt.unpack - def test_get_skills_score(self, skills, learner_skill_score, expected): - """ - Test that skill-score mapping is returned in correct format. - """ - get_skills_score(skills, learner_skill_score) - assert skills == expected - - def test_update_category_user_scores_map(self): - """ - Test that skill-score mapping is returned in correct format. - """ - category_user_scores_map = defaultdict(list) - update_category_user_scores_map(DUMMY_CATEGORIES_WITH_SCORES["skill_categories"], category_user_scores_map) - expected = {"Information Technology": [0.8], "Finance": [0.3]} - assert category_user_scores_map == expected - - def test_update_edx_average_score(self): - """ - Test that skill-score mapping is returned in correct format. - """ - update_edx_average_score(DUMMY_CATEGORIES_WITH_SCORES["skill_categories"], DUMMY_USER_SCORES_MAP) - assert DUMMY_CATEGORIES_WITH_SCORES["skill_categories"][0]["edx_average_score"] == 0.4 - assert DUMMY_CATEGORIES_WITH_SCORES["skill_categories"][1]["edx_average_score"] == 0.5 - - @ddt.data( - ("http://localhost:18000/api/", "http://localhost:18000"), - ("http://localhost:18000/", "http://localhost:18000"), - ) - @ddt.unpack - def test_get_base_url(self, source_url, expected): - """ - Test that base url is returned correctly. - """ - actual = get_base_url(source_url) - assert actual == expected - - @mock.patch('openedx.core.djangoapps.user_api.learner_skill_levels.utils.get_catalog_api_client') - @mock.patch('openedx.core.djangoapps.user_api.learner_skill_levels.utils.get_catalog_api_base_url') - @mock.patch('openedx.core.djangoapps.user_api.learner_skill_levels.utils.get_api_data') - @mock.patch('openedx.core.djangoapps.user_api.learner_skill_levels.utils.check_catalog_integration_and_get_user') - def test_get_top_skill_categories_for_job( - self, - mock_check_catalog_integration_and_get_user, - mock_get_api_data, - mock_get_catalog_api_base_url, - mock_get_catalog_api_client - ): - """ - Test that get_top_skill_categories_for_job returns jobs categories. - """ - mock_check_catalog_integration_and_get_user.return_value = self.service_user, self.catalog_integration - mock_get_api_data.return_value = DUMMY_CATEGORIES_RESPONSE - mock_get_catalog_api_base_url.return_value = 'localhost:18381/api' - mock_get_catalog_api_client.return_value = self.client - result = get_top_skill_categories_for_job(1) - assert result == DUMMY_CATEGORIES_RESPONSE - - @mock.patch('openedx.core.djangoapps.user_api.learner_skill_levels.utils.get_catalog_api_client') - @mock.patch('openedx.core.djangoapps.user_api.learner_skill_levels.utils.get_catalog_api_base_url') - @mock.patch('openedx.core.djangoapps.user_api.learner_skill_levels.utils.get_api_data') - @mock.patch('openedx.core.djangoapps.user_api.learner_skill_levels.utils.check_catalog_integration_and_get_user') - def test_get_job_holder_usernames( - self, - mock_check_catalog_integration_and_get_user, - mock_get_api_data, - mock_get_catalog_api_base_url, - mock_get_catalog_api_client - ): - """ - Test that test_get_job_holder_usernames returns usernames. - """ - mock_check_catalog_integration_and_get_user.return_value = self.service_user, self.catalog_integration - mock_get_api_data.return_value = DUMMY_USERNAMES_RESPONSE - mock_get_catalog_api_base_url.return_value = 'localhost:18381/api' - mock_get_catalog_api_client.return_value = self.client - result = get_job_holder_usernames(1) - assert result == DUMMY_USERNAMES_RESPONSE diff --git a/openedx/core/djangoapps/user_api/learner_skill_levels/tests/test_views.py b/openedx/core/djangoapps/user_api/learner_skill_levels/tests/test_views.py deleted file mode 100644 index 369f32787f90..000000000000 --- a/openedx/core/djangoapps/user_api/learner_skill_levels/tests/test_views.py +++ /dev/null @@ -1,119 +0,0 @@ -""" -Test cases for LearnerSkillLevelsView. -""" - -from unittest import mock - -from django.urls import reverse -from rest_framework.test import APIClient, APITestCase - -from common.djangoapps.student.tests.factories import TEST_PASSWORD, UserFactory - -from .testutils import DUMMY_CATEGORIES_RESPONSE, DUMMY_USERNAMES_RESPONSE - - -class LearnerSkillLevelsViewTests(APITestCase): - """ - The tests for LearnerSkillLevelsView. - """ - - def setUp(self): - super().setUp() - - self.client = APIClient() - self.user = UserFactory.create(password=TEST_PASSWORD) - self.url = reverse('learner_skill_level', kwargs={'job_id': '1'}) - - for username in DUMMY_USERNAMES_RESPONSE['usernames']: - UserFactory(username=username) - - def test_unauthorized_get_endpoint(self): - """ - Test that endpoint is only accessible to authorized user. - """ - response = self.client.get(self.url) - assert response.status_code == 401 - - @mock.patch('openedx.core.djangoapps.user_api.learner_skill_levels.views.get_top_skill_categories_for_job') - @mock.patch('openedx.core.djangoapps.user_api.learner_skill_levels.views.get_job_holder_usernames') - @mock.patch('openedx.core.djangoapps.user_api.learner_skill_levels.api.generate_skill_score_mapping') - def test_get_endpoint( - self, - mock_generate_skill_score_mapping, - mock_get_job_holder_usernames, - mock_get_top_skill_categories_for_job - ): - """ - Test that response if returned with correct scores appended. - """ - mock_get_top_skill_categories_for_job.return_value = DUMMY_CATEGORIES_RESPONSE - mock_get_job_holder_usernames.return_value = DUMMY_USERNAMES_RESPONSE - mock_generate_skill_score_mapping.return_value = {'Technology Roadmap': 2, 'Python': 3} - - self.client.login(username=self.user.username, password=TEST_PASSWORD) - response = self.client.get(self.url) - assert response.status_code == 200 - # check if the response is mutated and scores are appended for skills - # for when some skills are learned by user in a category, check if user_score and avg score is appended - assert response.data['skill_categories'][0]['user_score'] == 0.8 - assert response.data['skill_categories'][0]['edx_average_score'] == 0.8 - - # for when no skill is learned by user in a category, check if user_score and avg score is appended - assert response.data['skill_categories'][1]['user_score'] == 0.0 - assert response.data['skill_categories'][1]['edx_average_score'] == 0.0 - - @mock.patch('openedx.core.djangoapps.user_api.learner_skill_levels.views.get_top_skill_categories_for_job') - @mock.patch('openedx.core.djangoapps.user_api.learner_skill_levels.views.get_job_holder_usernames') - @mock.patch('openedx.core.djangoapps.user_api.learner_skill_levels.api.generate_skill_score_mapping') - def test_get_with_less_than_5_users( - self, - mock_generate_skill_score_mapping, - mock_get_job_holder_usernames, - mock_get_top_skill_categories_for_job - ): - """ - Test that average value is None when users are less than 5. - """ - mock_get_top_skill_categories_for_job.return_value = DUMMY_CATEGORIES_RESPONSE - mock_get_job_holder_usernames.return_value = {"usernames": ['user1', 'user2']} - mock_generate_skill_score_mapping.return_value = {'Technology Roadmap': 2, 'Python': 3} - - self.client.login(username=self.user.username, password=TEST_PASSWORD) - response = self.client.get(self.url) - assert response.status_code == 200 - # check if the response is mutated and scores are appended for skills - # for when some skills are learned by user in a category, check if user_score and avg score is appended - assert response.data['skill_categories'][0]['user_score'] == 0.8 - assert response.data['skill_categories'][0]['edx_average_score'] is None - - # for when no skill is learned by user in a category, check if user_score and avg score is appended - assert response.data['skill_categories'][1]['user_score'] == 0.0 - assert response.data['skill_categories'][1]['edx_average_score'] is None - - @mock.patch('openedx.core.djangoapps.user_api.learner_skill_levels.views.get_top_skill_categories_for_job') - @mock.patch('openedx.core.djangoapps.user_api.learner_skill_levels.views.get_job_holder_usernames') - @mock.patch('openedx.core.djangoapps.user_api.learner_skill_levels.api.generate_skill_score_mapping') - def test_get_no_skills_learned( - self, - mock_generate_skill_score_mapping, - mock_get_job_holder_usernames, - mock_get_top_skill_categories_for_job - ): - """ - Test that score is 0.0 when no skills are learned by a user. - """ - mock_get_top_skill_categories_for_job.return_value = DUMMY_CATEGORIES_RESPONSE - mock_get_job_holder_usernames.return_value = DUMMY_USERNAMES_RESPONSE - mock_generate_skill_score_mapping.return_value = {} - - self.client.login(username=self.user.username, password=TEST_PASSWORD) - response = self.client.get(self.url) - assert response.status_code == 200 - # check if the response is mutated and scores are appended for skills - # for when some skills are learned by user in a category, check if user_score and avg score is appended - assert response.data['skill_categories'][0]['user_score'] == 0.0 - assert response.data['skill_categories'][0]['edx_average_score'] == 0.0 - - # for when no skill is learned by user in a category, check if user_score and avg score is appended - assert response.data['skill_categories'][1]['user_score'] == 0.0 - assert response.data['skill_categories'][1]['edx_average_score'] == 0.0 diff --git a/openedx/core/djangoapps/user_api/learner_skill_levels/tests/testutils.py b/openedx/core/djangoapps/user_api/learner_skill_levels/tests/testutils.py deleted file mode 100644 index 9be19ed15a20..000000000000 --- a/openedx/core/djangoapps/user_api/learner_skill_levels/tests/testutils.py +++ /dev/null @@ -1,110 +0,0 @@ -""" -Utilities for unit tests of learner skill levels. -""" - -DUMMY_CATEGORIES_RESPONSE = { - "job": "Digital Product Manager", - "skill_categories": [ - { - "name": "Information Technology", - "id": 1, - "skills": [ - {"id": 3, "name": "Technology Roadmap"}, - {"id": 12, "name": "Python"}, - {"id": 2, "name": "MongoDB"} - ], - "skills_subcategories": [ - { - "id": 1, - "name": "Databases", - "skills": [ - {"id": 1, "name": "Query Languages"}, - {"id": 2, "name": "MongoDB"}, - ] - }, - { - "id": 2, - "name": "IT Management", - "skills": [ - {"id": 3, "name": "Technology Roadmap"}, - ] - }, - ] - }, - { - "name": "Finance", - "id": 2, - "skills": [ - {"id": 4, "name": "Accounting"}, - {"id": 5, "name": "TQM"}, - ], - "skills_subcategories": [ - { - "id": 3, - "name": "Auditing", - "skills": [ - {"id": 4, "name": "Accounting"}, - {"id": 5, "name": "TQM"}, - ] - }, - { - "id": 4, - "name": "Management", - "skills": [ - {"id": 6, "name": "Financial Management"}, - ] - }, - ] - }, - ] -} - -DUMMY_CATEGORIES_WITH_SCORES = { - "job": "Digital Product Manager", - "skill_categories": [ - { - "name": "Information Technology", - "id": 1, - "skills": [ - {"id": 3, "name": "Technology Roadmap", "score": 1}, - {"id": 12, "name": "Python", "score": 2}, - {"id": 2, "name": "MongoDB", "score": 3} - ], - "user_score": 0.8, - }, - { - "name": "Finance", - "id": 2, - "skills": [ - {"id": 1, "name": "Query Languages", "score": 1}, - {"id": 4, "name": "System Design", "score": 2}, - ], - "user_score": 0.3, - }, - ] -} - -DUMMY_USER_SCORES_MAP = { - "Information Technology": [0.1, 0.3, 0.5, 0.7], - "Finance": [0.2, 0.4, 0.6, 0.8] - -} - -DUMMY_USERNAMES_RESPONSE = { - "usernames": [ - 'test_user_1', - 'test_user_2', - 'test_user_3', - 'test_user_4', - 'test_user_5', - 'test_user_6', - ] -} - -DUMMY_COURSE_DATA_RESPONSE = { - "key": "AWS+OTP", - "uuid": "fe1a9ad4-a452-45cd-80e5-9babd3d43f96", - "title": "Demonstration Course", - "level_type": 'Advanced', - "skill_names": ["python", "MongoDB", "Data Science"] -} diff --git a/openedx/core/djangoapps/user_api/learner_skill_levels/utils.py b/openedx/core/djangoapps/user_api/learner_skill_levels/utils.py deleted file mode 100644 index 22f8745e0828..000000000000 --- a/openedx/core/djangoapps/user_api/learner_skill_levels/utils.py +++ /dev/null @@ -1,182 +0,0 @@ -""" -Utilities for learner_skill_levels. -""" -from logging import getLogger -from urllib.parse import urlparse - -from lms.djangoapps.grades.models import PersistentCourseGrade # lint-amnesty, pylint: disable=unused-import -from openedx.core.djangoapps.catalog.utils import ( - get_catalog_api_client, - check_catalog_integration_and_get_user, - get_catalog_api_base_url, - -) -from openedx.core.djangoapps.catalog.utils import get_course_data, get_course_run_data -from openedx.core.lib.edx_api_utils import get_api_data - -from .constants import LEVEL_TYPE_SCORE_MAPPING - -LOGGER = getLogger(__name__) # pylint: disable=invalid-name - - -def get_course_run_ids(user): - """ - Returns all the course run ids of the courses that user has passed from PersistentCourseGrade model. - """ - return list( - PersistentCourseGrade.objects.filter( - user_id=user.id, - passed_timestamp__isnull=False - ).values_list('course_id', flat=True) - ) - - -def generate_skill_score_mapping(user): - """ - Generates a skill to score mapping for all the skills user has learner so far in passed courses. - """ - # get course_run_ids of all courses the user has passed - course_run_ids = get_course_run_ids(user) - - skill_score_mapping = {} - for course_run_id in course_run_ids: - # fetch course details from course run id to get course key - course_run_data = get_course_run_data(course_run_id, ['course']) - if course_run_data: - # fetch course details to get level type and skills - course_data = get_course_data(course_run_data['course'], ['skill_names', 'level_type']) - skill_names = course_data['skill_names'] - level_type = course_data['level_type'] - - # if a level_type is None for a course, we should skip that course. - if level_type: - score = LEVEL_TYPE_SCORE_MAPPING[level_type.capitalize()] - for skill in skill_names: - if skill in skill_score_mapping: - # assign scores b/w 1-3 based on level type - # assign the larger score if skill is repeated in 2 courses - skill_score_mapping[skill] = max(score, skill_score_mapping[skill]) - else: - skill_score_mapping.update({skill: score}) - LOGGER.info( - "Could not find course_key for course run id [%s].", course_run_id - ) - return skill_score_mapping - - -def calculate_user_skill_score(skills_with_score): - """ - Calculates user skill score to see where the user falls in a certain job category. - """ - # generate a dict with skill name as key and score as value - # take only those skills that user has learned. - - if not skills_with_score: - return 0.0 - - skills_score_dict = { - item['name']: item['score'] - for item in skills_with_score - if item['score'] is not None - } - sum_of_skills = sum(skills_score_dict.values()) - skills_count = len(skills_score_dict) - if not skills_count: - return 0.0 - # sum of skills score in the category/ 3*no. of skills in category - return round(sum_of_skills / (3 * skills_count), 1) - - -def get_skills_score(skills, learner_skill_score): - """ - Takes each skill item in list and appends its score to it. - For a skill that doesn't exist in learner's skills set, appends None as score. - """ - for skill in skills: - skill['score'] = learner_skill_score.get(skill['name']) - - -def update_category_user_scores_map(categories, category_user_scores_map): - """ - Appends user's scores for each category in the dict. - """ - for category in categories: - category_user_scores_map[category['name']].append(category['user_score']) - - -def update_edx_average_score(categories, user_score_mapping): - """ - Calculates average score for each category and appends it. - """ - for category in categories: - category_scores = user_score_mapping[category['name']] - sum_score = sum(category_scores, 0.0) - average_score = round(sum_score / len(category_scores), 1) - category['edx_average_score'] = average_score - - -def get_base_url(url): - """ - Returns the base url for any given url. - """ - if url: - parsed = urlparse(url) - return f'{parsed.scheme}://{parsed.netloc}' - - -def get_top_skill_categories_for_job(job_id): - """ - Retrieve top categories for the job with the given job_id. - - Arguments: - job_id (int): id of the job about which we are retrieving information. - - Returns: - dict with top 5 categories of specified job. - """ - user, catalog_integration = check_catalog_integration_and_get_user(error_message_field='Skill Categories') - if user: - api_client = get_catalog_api_client(user) - root_url = get_catalog_api_base_url() - base_api_url = get_base_url(root_url) - resource = '/taxonomy/api/v1/job-top-subcategories' - cache_key = f'{catalog_integration.CACHE_KEY}.job-categories.{job_id}' - data = get_api_data( - catalog_integration, - resource=resource, - resource_id=job_id, - api_client=api_client, - base_api_url=base_api_url, - cache_key=cache_key if catalog_integration.is_cache_enabled else None, - ) - if data: - return data - - -def get_job_holder_usernames(job_id): - """ - Retrieve usernames of users who have the same job as given job_id. - - Arguments: - job_id (int): id of the job for which we are retrieving usernames. - - Returns: - list with oldest 100 users' usernames that exist in our system. - """ - user, catalog_integration = check_catalog_integration_and_get_user(error_message_field='Job Holder Usernames') - if user: - api_client = get_catalog_api_client(user) - root_url = get_catalog_api_base_url() - base_api_url = get_base_url(root_url) - resource = '/taxonomy/api/v1/job-holder-usernames' - cache_key = f'{catalog_integration.CACHE_KEY}.job-holder-usernames.{job_id}' - data = get_api_data( - catalog_integration, - resource=resource, - resource_id=job_id, - api_client=api_client, - base_api_url=base_api_url, - cache_key=cache_key if catalog_integration.is_cache_enabled else None, - ) - if data: - return data diff --git a/openedx/core/djangoapps/user_api/learner_skill_levels/views.py b/openedx/core/djangoapps/user_api/learner_skill_levels/views.py deleted file mode 100644 index 3fee78588bd4..000000000000 --- a/openedx/core/djangoapps/user_api/learner_skill_levels/views.py +++ /dev/null @@ -1,129 +0,0 @@ -""" -Views for learner_skill_levels. -""" -from collections import defaultdict -from copy import deepcopy - -from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user -from rest_framework import permissions, status -from rest_framework.response import Response -from rest_framework.views import APIView - -from edx_rest_framework_extensions.auth.jwt.authentication import JwtAuthentication -from edx_rest_framework_extensions.auth.session.authentication import SessionAuthenticationAllowInactiveUser -from openedx.core.lib.api.authentication import BearerAuthenticationAllowInactiveUser - -from .api import get_learner_skill_levels -from .utils import get_top_skill_categories_for_job, get_job_holder_usernames, update_category_user_scores_map, \ - update_edx_average_score - - -class LearnerSkillLevelsView(APIView): - """ - **Use Cases** - - Returns top 5 job categories for the given job. Checks which skill the user has learned via courses - and assign scores to each skill in category. Also takes first 100 users in our system to calculate - average score for each category. - - **Request format** - - GET /api/user/v1/skill_level/{job_id}/ - - **Response Values for GET** - - If the specified job_id doesn't exist, an HTTP - 404 "Not Found" response is returned. - - If a logged in user makes a request with an existing job, an HTTP 200 - "OK" response is returned that contains a JSON string. - - **Example Request** - - GET /api/user/v1/skill_level/1/ - - **Example Response** - - { - "job": "Digital Product Manager", - "skill_categories": [ - { - "name": "Information Technology", - "id": 1, - "skills": [ - {"id": 2, "name": "Query Languages", "score": 1}, - {"id": 3, "name": "MongoDB", "score": 3}, - ] - "user_score": 0.4, // request user's score - "edx_average_score": 0.7, - "skills_subcategories": [ - { - "id": 1, - "name": "Databases", - "skills": [ - {"id": 2, "name": "Query Languages", "score": 1}, - {"id": 3, "name": "MongoDB", "score": None}, - ] - }, - { - "id": 2, - "name": "IT Management", - "skills": [ - {"id": 1, "name": "Technology Roadmap", "score": 2}, - ] - }, - // here remaining job related skills subcategories - ] - }, - - // Here more 4 skill categories - ] - } - """ - authentication_classes = ( - JwtAuthentication, - BearerAuthenticationAllowInactiveUser, - SessionAuthenticationAllowInactiveUser - ) - permission_classes = (permissions.IsAuthenticated, ) - - def get(self, request, job_id): - """ - GET /api/user/v1/skill_level/{job_id}/ - """ - # get top categories for the given job - job_skill_categories = get_top_skill_categories_for_job(job_id) - if not job_skill_categories: - return Response( - status=status.HTTP_404_NOT_FOUND, - data={'message': "The job id doesn't exist, enter a valid job id."} - ) - - # assign scores for every skill request user has learned - top_categories = deepcopy(job_skill_categories['skill_categories']) - user_category_scores = get_learner_skill_levels( - user=request.user, - top_categories=top_categories, - ) - - # repeat the same logic for 100 job holder users in our system - job_holder_usernames = get_job_holder_usernames(job_id) - users = User.objects.filter(username__in=job_holder_usernames['usernames']) - - # edx_avg_score should only be calculated if users count is greater than 5, else skip it. - if len(users) > 5: - # To save all the users' scores against every category to calculate average score - category_user_scores_map = defaultdict(list) - - for user in users: - categories = deepcopy(job_skill_categories['skill_categories']) - categories_with_scores = get_learner_skill_levels( - user=user, - top_categories=categories, - ) - update_category_user_scores_map(categories_with_scores, category_user_scores_map) - - update_edx_average_score(user_category_scores, category_user_scores_map) - - job_skill_categories['skill_categories'] = user_category_scores - return Response(job_skill_categories) diff --git a/openedx/core/djangoapps/user_api/urls.py b/openedx/core/djangoapps/user_api/urls.py index 6bd34fccc6e5..b4bf1b3b2bbe 100644 --- a/openedx/core/djangoapps/user_api/urls.py +++ b/openedx/core/djangoapps/user_api/urls.py @@ -19,7 +19,6 @@ NameChangeView, UsernameReplacementView, CancelAccountRetirementStatusView ) -from .learner_skill_levels.views import LearnerSkillLevelsView from . import views as user_api_views from .models import UserPreference from .preferences.views import PreferencesDetailView, PreferencesView @@ -191,11 +190,6 @@ PreferencesDetailView.as_view(), name='preferences_detail_api' ), - re_path( - r'^v1/skill_level/(?P[0-9]+)/$', - LearnerSkillLevelsView.as_view(), - name="learner_skill_level" - ), # Moved from user_api/legacy_urls.py path('v1/', include(USER_API_ROUTER.urls)),