From 6f90af2325ab8c5df9af491dab5e2848475ec937 Mon Sep 17 00:00:00 2001 From: Muhammad Ammar Date: Wed, 27 Mar 2024 16:46:43 +0500 Subject: [PATCH] feat: provider utils for skill validation exemption (#4300) * feat: provider utils for skill validation exemption * chore: upgrade python requirements to pull in taxonomy-connector 1.50.0 --- .../apps/taxonomy_support/providers.py | 38 +++++++++++++++- .../taxonomy_support/tests/test_providers.py | 43 ++++++++++++++++++- db_keyword_overrides.yml | 1 + requirements/local.txt | 16 +++---- requirements/production.txt | 14 +++--- 5 files changed, 94 insertions(+), 18 deletions(-) diff --git a/course_discovery/apps/taxonomy_support/providers.py b/course_discovery/apps/taxonomy_support/providers.py index 136996ef78..6899c0e7e8 100644 --- a/course_discovery/apps/taxonomy_support/providers.py +++ b/course_discovery/apps/taxonomy_support/providers.py @@ -27,7 +27,7 @@ from course_discovery.apps.course_metadata.contentful_utils import ( aggregate_contentful_data, fetch_and_transform_bootcamp_contentful_data, fetch_and_transform_degree_contentful_data ) -from course_discovery.apps.course_metadata.models import Course, CourseRun, Program +from course_discovery.apps.course_metadata.models import Course, CourseRun, Organization, Program class DiscoveryCourseMetadataProvider(CourseMetadataProvider): @@ -72,6 +72,42 @@ def get_all_courses(): # lint-amnesty, pylint: disable=arguments-differ ), } + def get_course_key(self, course_run_key): + """ + Get course key for the given `course_run_key`. + + Arguments: + course_run_key(str): course run key + + Returns: + str: course key if course run exists, None otherwise + """ + return CourseRun.objects.filter(key=course_run_key).values_list('course__key', flat=True).first() + + def is_valid_course(self, course_key): + """ + Validate that a course with given `course_key` exists. + + Arguments: + course_key(str): course key + + Returns: + bool: True if course is valid, False otherwise + """ + return Course.objects.filter(key=course_key).exists() + + def is_valid_organization(self, organization_key): + """ + Validate that an organization with given `organization_key` exists. + + Arguments: + organization_key(str): organization key + + Returns: + bool: True if organization is valid, False otherwise + """ + return Organization.objects.filter(key=organization_key).exists() + class DiscoveryCourseRunMetadataProvider(CourseRunMetadataProvider): """ diff --git a/course_discovery/apps/taxonomy_support/tests/test_providers.py b/course_discovery/apps/taxonomy_support/tests/test_providers.py index f08fd936da..a3bfa2def4 100644 --- a/course_discovery/apps/taxonomy_support/tests/test_providers.py +++ b/course_discovery/apps/taxonomy_support/tests/test_providers.py @@ -30,7 +30,9 @@ from django.conf import settings from django.test import TestCase from taxonomy.providers import CourseRunContent -from taxonomy.providers.utils import get_course_run_metadata_provider, get_xblock_metadata_provider +from taxonomy.providers.utils import ( + get_course_metadata_provider, get_course_run_metadata_provider, get_xblock_metadata_provider +) from taxonomy.validators import ( CourseMetadataProviderValidator, CourseRunMetadataProviderValidator, ProgramMetadataProviderValidator, XBlockMetadataProviderValidator @@ -39,7 +41,9 @@ from course_discovery.apps.core.tests.factories import PartnerFactory from course_discovery.apps.core.tests.mixins import LMSAPIClientMixin from course_discovery.apps.course_metadata.choices import CourseRunStatus -from course_discovery.apps.course_metadata.tests.factories import CourseFactory, CourseRunFactory, ProgramFactory +from course_discovery.apps.course_metadata.tests.factories import ( + CourseFactory, CourseRunFactory, OrganizationFactory, ProgramFactory +) class TaxonomyIntegrationTests(TestCase, LMSAPIClientMixin): @@ -138,3 +142,38 @@ def test_get_video_xblocks_content(self): provider = get_xblock_metadata_provider() xblocks = provider.get_xblocks(block_ids) assert 'Should not be included in tagging content.' not in xblocks[0].content + + +class DiscoveryCourseMetadataProviderTests(TestCase): + """ + Tests for `DiscoveryCourseMetadataProvider`. + """ + def setUp(self): + super().setUp() + + self.course = CourseFactory() + self.organization = OrganizationFactory(key='MAx') + self.courserun = CourseRunFactory(course=self.course) + + self.course_metadata_provider = get_course_metadata_provider() + + def test_get_course_key(self): + """ + Verify that `get_course_key` work as expected. + """ + assert self.course_metadata_provider.get_course_key(self.courserun.key) == self.course.key + assert not self.course_metadata_provider.get_course_key('blah blah') + + def test_is_valid_course(self): + """ + Verify that `is_valid_course` work as expected. + """ + assert self.course_metadata_provider.is_valid_course(self.course.key) is True + assert self.course_metadata_provider.is_valid_course('blah blah') is False + + def test_is_valid_organization(self): + """ + Verify that `is_valid_organization` work as expected. + """ + assert self.course_metadata_provider.is_valid_organization(self.organization.key) is True + assert self.course_metadata_provider.is_valid_organization('blah blah') is False diff --git a/db_keyword_overrides.yml b/db_keyword_overrides.yml index 795828fd7a..d573b44d40 100644 --- a/db_keyword_overrides.yml +++ b/db_keyword_overrides.yml @@ -17,4 +17,5 @@ SNOWFLAKE: - CourseRun.start - BackfillCourseRunSlugsConfig.all - HistoricalCourseRun.start + - SkillValidationConfiguration.organization STITCH: diff --git a/requirements/local.txt b/requirements/local.txt index 9f5558576b..44a8d6f4d7 100644 --- a/requirements/local.txt +++ b/requirements/local.txt @@ -77,9 +77,9 @@ boltons==21.0.0 # face # glom # semgrep -boto3==1.34.69 +boto3==1.34.71 # via django-ses -botocore==1.34.69 +botocore==1.34.71 # via # boto3 # s3transfer @@ -246,7 +246,7 @@ django-contrib-comments==2.2.0 # via -r requirements/base.in django-cors-headers==4.3.1 # via -r requirements/base.in -django-countries==7.5.1 +django-countries==7.6 # via -r requirements/base.in django-crum==0.7.9 # via @@ -440,11 +440,11 @@ face==22.0.0 # via glom factory-boy==3.3.0 # via -r requirements/test.in -faker==24.3.0 +faker==24.4.0 # via factory-boy fastavro==1.9.4 # via openedx-events -filelock==3.13.1 +filelock==3.13.3 # via # snowflake-connector-python # tox @@ -627,11 +627,11 @@ protobuf==4.25.3 # proto-plus psutil==5.9.8 # via edx-django-utils -pyasn1==0.5.1 +pyasn1==0.6.0 # via # pyasn1-modules # rsa -pyasn1-modules==0.3.0 +pyasn1-modules==0.4.0 # via google-auth pycodestyle==2.11.1 # via -r requirements/test.in @@ -892,7 +892,7 @@ stevedore==5.2.0 # edx-opaque-keys strenum==0.4.15 # via gspread -taxonomy-connector==1.46.2 +taxonomy-connector==1.50.0 # via -r requirements/base.in testfixtures==8.1.0 # via -r requirements/test.in diff --git a/requirements/production.txt b/requirements/production.txt index 3085fa9b3b..7b1952bc10 100644 --- a/requirements/production.txt +++ b/requirements/production.txt @@ -52,9 +52,9 @@ beautifulsoup4==4.12.3 # taxonomy-connector billiard==4.2.0 # via celery -boto3==1.34.69 +boto3==1.34.71 # via django-ses -botocore==1.34.69 +botocore==1.34.71 # via # boto3 # s3transfer @@ -186,7 +186,7 @@ django-contrib-comments==2.2.0 # via -r requirements/base.in django-cors-headers==4.3.1 # via -r requirements/base.in -django-countries==7.5.1 +django-countries==7.6 # via -r requirements/base.in django-crum==0.7.9 # via @@ -350,7 +350,7 @@ elasticsearch-dsl==7.4.1 # django-elasticsearch-dsl-drf fastavro==1.9.4 # via openedx-events -filelock==3.13.1 +filelock==3.13.3 # via snowflake-connector-python frozenlist==1.4.1 # via @@ -488,11 +488,11 @@ protobuf==4.25.3 # proto-plus psutil==5.9.8 # via edx-django-utils -pyasn1==0.5.1 +pyasn1==0.6.0 # via # pyasn1-modules # rsa -pyasn1-modules==0.3.0 +pyasn1-modules==0.4.0 # via google-auth pycountry==23.12.11 # via -r requirements/base.in @@ -634,7 +634,7 @@ stevedore==5.2.0 # edx-opaque-keys strenum==0.4.15 # via gspread -taxonomy-connector==1.46.2 +taxonomy-connector==1.50.0 # via -r requirements/base.in text-unidecode==1.3 # via python-slugify