diff --git a/course_discovery/apps/course_metadata/data_loaders/csv_loader.py b/course_discovery/apps/course_metadata/data_loaders/csv_loader.py index 4a39f0a8bb..bc01a133ad 100644 --- a/course_discovery/apps/course_metadata/data_loaders/csv_loader.py +++ b/course_discovery/apps/course_metadata/data_loaders/csv_loader.py @@ -863,6 +863,18 @@ def process_meta_information(self, meta_title, meta_description, meta_keywords): 'keywords': [keyword.strip() for keyword in meta_keywords.split(',')] if meta_keywords else [] } + def process_taxi_form_information(self, form_id, post_submit_url): + """ + Return a dict containing processed product taxi form information. + """ + if not all([form_id, post_submit_url]): + return {} + + return { + 'form_id': form_id, + 'post_submit_url': post_submit_url, + } + def get_additional_metadata_dict(self, data, type_slug): """ Return the appropriate additional metadata dict representation, skipping the keys that are not @@ -871,6 +883,9 @@ def get_additional_metadata_dict(self, data, type_slug): if type_slug not in [CourseType.EXECUTIVE_EDUCATION_2U, CourseType.BOOTCAMP_2U]: return {} + taxi_form = self.process_taxi_form_information( + data.get('taxi_form_id', ''), data.get('post_submit_url', '') + ) additional_metadata = { 'external_url': data['redirect_url'], 'external_identifier': data['external_identifier'], @@ -915,4 +930,6 @@ def get_additional_metadata_dict(self, data, type_slug): )}) if external_course_marketing_type in dict(ExternalCourseMarketingType.choices): additional_metadata.update({'external_course_marketing_type': external_course_marketing_type}) + if taxi_form: + additional_metadata.update({'taxi_form': taxi_form}) return additional_metadata diff --git a/course_discovery/apps/course_metadata/data_loaders/tests/mixins.py b/course_discovery/apps/course_metadata/data_loaders/tests/mixins.py index 2c2eba3f31..c3cabd0e7c 100644 --- a/course_discovery/apps/course_metadata/data_loaders/tests/mixins.py +++ b/course_discovery/apps/course_metadata/data_loaders/tests/mixins.py @@ -330,7 +330,8 @@ class CSVLoaderMixin: 'upgrade_deadline_override_date', 'upgrade_deadline_override_time', 'redirect_url', 'external_identifier', 'lead_capture_form_url', 'organic_url', 'certificate_header', 'certificate_text', 'stat1', 'stat1_text', 'stat2', 'stat2_text', 'organization_logo_override', 'organization_short_code_override', 'variant_id', - 'meta_title', 'meta_description', 'meta_keywords', 'slug', 'external_course_marketing_type', 'fixed_price_usd' + 'meta_title', 'meta_description', 'meta_keywords', 'slug', 'external_course_marketing_type', 'fixed_price_usd', + 'post_submit_url', 'taxi_form_id' ] # The list of minimal data headers MINIMAL_CSV_DATA_KEYS_ORDER = [ @@ -374,6 +375,8 @@ class CSVLoaderMixin: "meta_title": "SEO Title", "meta_description": "SEO Description", "meta_keywords": ["Keyword 1", "Keyword 2"], + "taxi_form_id": "test-form-id", + "post_submit_url": "https://www.getsmarter.com/blog/career-advice" } BASE_EXPECTED_COURSE_RUN_DATA = { @@ -523,6 +526,9 @@ def _assert_course_data(self, course, expected_data): ) == set(expected_data['meta_keywords']) assert course.additional_metadata.registration_deadline.isoformat() == expected_data['registration_deadline'] assert course.additional_metadata.certificate_info.heading == expected_data['certificate_info']['heading'] + if not expected_data.get('taxi_form_is_none', ''): + assert course.additional_metadata.taxi_form.form_id == expected_data['taxi_form_id'] + assert course.additional_metadata.taxi_form.post_submit_url == expected_data['post_submit_url'] assert expected_data['certificate_info']['blurb'] in course.additional_metadata.certificate_info.blurb assert sorted([subject.slug for subject in course.subjects.all()]) == sorted(expected_data['subjects']) assert sorted( diff --git a/course_discovery/apps/course_metadata/data_loaders/tests/mock_data.py b/course_discovery/apps/course_metadata/data_loaders/tests/mock_data.py index a84d870cbb..2afdb9a707 100644 --- a/course_discovery/apps/course_metadata/data_loaders/tests/mock_data.py +++ b/course_discovery/apps/course_metadata/data_loaders/tests/mock_data.py @@ -3234,7 +3234,9 @@ "meta_keywords": "Keyword 1, Keyword 2", "slug": "", 'external_course_marketing_type': "short_course", - "fixed_price_usd": "123.4" + "fixed_price_usd": "123.4", + "post_submit_url": "https://www.getsmarter.com/blog/career-advice", + "taxi_form_id": "test-form-id" } VALID_MINIMAL_COURSE_AND_COURSE_RUN_CSV_DICT = { diff --git a/course_discovery/apps/course_metadata/data_loaders/tests/test_csv_loader.py b/course_discovery/apps/course_metadata/data_loaders/tests/test_csv_loader.py index 4e1dedf1bc..19b267d305 100644 --- a/course_discovery/apps/course_metadata/data_loaders/tests/test_csv_loader.py +++ b/course_discovery/apps/course_metadata/data_loaders/tests/test_csv_loader.py @@ -19,7 +19,7 @@ from course_discovery.apps.course_metadata.data_loaders.tests import mock_data from course_discovery.apps.course_metadata.data_loaders.tests.mixins import CSVLoaderMixin from course_discovery.apps.course_metadata.models import ( - AdditionalMetadata, Course, CourseEntitlement, CourseRun, CourseType, Seat, Source + AdditionalMetadata, Course, CourseEntitlement, CourseRun, CourseType, Seat, Source, TaxiForm ) from course_discovery.apps.course_metadata.tests.factories import ( AdditionalMetadataFactory, CourseFactory, CourseRunFactory, CourseTypeFactory, OrganizationFactory, SourceFactory @@ -279,6 +279,8 @@ def test_single_valid_row(self, csv_slug, expected_slug, jwt_decode_patch): # p assert course.active_url_slug == expected_slug assert course.official_version.active_url_slug == expected_slug + assert TaxiForm.objects.count() == 1 + assert loader.get_ingestion_stats() == { 'total_products_count': 1, 'success_count': 1, @@ -659,7 +661,10 @@ def test_ingest_flow_for_preexisting_unpublished_course(self, jwt_decode_patch): self.mock_ecommerce_publication(self.partner) self.mock_image_response() - course = CourseFactory(key=self.COURSE_KEY, partner=self.partner, type=self.course_type, draft=True) + course = CourseFactory( + key=self.COURSE_KEY, partner=self.partner, type=self.course_type, draft=True, + additional_metadata=AdditionalMetadataFactory(taxi_form=None) + ) CourseRunFactory( course=course, key=self.COURSE_RUN_KEY, @@ -670,7 +675,11 @@ def test_ingest_flow_for_preexisting_unpublished_course(self, jwt_decode_patch): ) with NamedTemporaryFile() as csv: - csv = self._write_csv(csv, [{**mock_data.VALID_COURSE_AND_COURSE_RUN_CSV_DICT, "fixed_price_usd": ""}]) + csv = self._write_csv(csv, [{ + **mock_data.VALID_COURSE_AND_COURSE_RUN_CSV_DICT, + "fixed_price_usd": "", + "taxi_form_id": "" + }]) with LogCapture(LOGGER_PATH) as log_capture: with mock.patch.object( CSVDataLoader, @@ -697,11 +706,12 @@ def test_ingest_flow_for_preexisting_unpublished_course(self, jwt_decode_patch): # Verify the existence of draft and non-draft assert Course.everything.count() == 2 assert CourseRun.everything.count() == 2 + assert TaxiForm.objects.count() == 0 course = Course.everything.get(key=self.COURSE_KEY, partner=self.partner, draft=True) course_run = CourseRun.everything.get(course=course, draft=True) - self._assert_course_data(course, self.BASE_EXPECTED_COURSE_DATA) + self._assert_course_data(course, {**self.BASE_EXPECTED_COURSE_DATA, 'taxi_form_is_none': True}) self._assert_course_run_data( course_run, {**self.BASE_EXPECTED_COURSE_RUN_DATA, "fixed_price_usd": Decimal('111.11')} diff --git a/course_discovery/apps/course_metadata/management/commands/populate_executive_education_data_csv.py b/course_discovery/apps/course_metadata/management/commands/populate_executive_education_data_csv.py index b1b7d7783a..0b95bc9bf9 100644 --- a/course_discovery/apps/course_metadata/management/commands/populate_executive_education_data_csv.py +++ b/course_discovery/apps/course_metadata/management/commands/populate_executive_education_data_csv.py @@ -42,7 +42,7 @@ class Command(BaseCommand): 'lead_capture_form_url', 'certificate_header', 'certificate_text', 'stat1', 'stat1_text', 'stat2', 'stat2_text', 'organic_url', 'organization_short_code_override', 'organization_logo_override', 'variant_id', 'meta_title', 'meta_description', 'meta_keywords', 'slug', 'external_course_marketing_type', 'restriction_type', - 'fixed_price_usd', + 'fixed_price_usd', 'taxi_form_id', 'post_submit_url' ] # Mapping English and Spanish languages to IETF equivalent variants @@ -381,4 +381,6 @@ def get_transformed_data(self, partially_filled_csv_dict, product_dict): product_dict['logoUrl'] ), 'external_course_marketing_type': product_dict['productType'], + 'taxi_form_id': product_dict['edxTaxiFormId'], + 'post_submit_url': utils.format_base64_strings(product_dict['prospectusUrl']), } diff --git a/course_discovery/apps/course_metadata/management/commands/tests/test_populate_executive_education_data_csv.py b/course_discovery/apps/course_metadata/management/commands/tests/test_populate_executive_education_data_csv.py index 0686c43458..f6727fb5d7 100644 --- a/course_discovery/apps/course_metadata/management/commands/tests/test_populate_executive_education_data_csv.py +++ b/course_discovery/apps/course_metadata/management/commands/tests/test_populate_executive_education_data_csv.py @@ -56,6 +56,8 @@ class TestPopulateExecutiveEducationDataCsv(CSVLoaderMixin, TestCase): "metaKeywords": "Keyword 1, Keyword 2", "slug": "csv-course-slug", "productType": "short_course", + "prospectusUrl": "aHR0cHM6Ly93d3cuZ2V0c21hcnRlci5jb20vYmxvZy9jYXJlZXItYWR2aWNl", + "edxTaxiFormId": "test-form-id", "variant": { "id": "00000000-0000-0000-0000-000000000000", "endDate": "2022-05-06", @@ -140,6 +142,7 @@ class TestPopulateExecutiveEducationDataCsv(CSVLoaderMixin, TestCase): SUCCESS_API_RESPONSE_V2 = copy.deepcopy(SUCCESS_API_RESPONSE) SUCCESS_API_RESPONSE_V2['products'][0].pop('variant') SUCCESS_API_RESPONSE_V2["products"][0].update({"variants": [variant_1, variant_2,]}) + SUCCESS_API_RESPONSE_V2["products"][0].update({"edxTaxiFormId": None}) def mock_product_api_call(self, override_product_api_response=None): """ @@ -219,6 +222,8 @@ def test_successful_file_data_population_with_getsmarter_flag_with_multiple_vari assert data_row['Verified Price'] == str(self.variant_1['finalPrice']) assert data_row['Restriction Type'] == 'custom-b2b-enterprise' assert data_row['Fixed Price Usd'] == '3510.0' + assert data_row['Taxi Form Id'] == '' + assert data_row['Post Submit Url'] == 'https://www.getsmarter.com/blog/career-advice' data_row = next(reader) assert data_row['Variant Id'] == self.variant_2['id'] @@ -231,6 +236,8 @@ def test_successful_file_data_population_with_getsmarter_flag_with_multiple_vari assert data_row['Verified Price'] == str(self.variant_2['finalPrice']) assert data_row['Restriction Type'] == 'None' assert data_row['Fixed Price Usd'] == '' + assert data_row['Taxi Form Id'] == '' + assert data_row['Post Submit Url'] == 'https://www.getsmarter.com/blog/career-advice' log_capture.check_present( ( @@ -426,6 +433,7 @@ def _assert_api_response(self, data_row): """ Assert the default API response in output CSV dict. """ + # pylint: disable=too-many-statements assert data_row['Organization Short Code Override'] == 'altEdx' assert data_row['2U Organization Code'] == 'edX' assert data_row['Number'] == 'TC' @@ -478,3 +486,5 @@ def _assert_api_response(self, data_row): assert data_row['Slug'] == 'csv-course-slug' assert data_row['External Course Marketing Type'] == "short_course" assert data_row['Fixed Price Usd'] == "333.3" + assert data_row['Taxi Form Id'] == 'test-form-id' + assert data_row['Post Submit Url'] == 'https://www.getsmarter.com/blog/career-advice'