Skip to content

Commit

Permalink
Merge pull request #785 from openedx/mkeating/ENT-8248
Browse files Browse the repository at this point in the history
Take Exec Ed course data from course runs
  • Loading branch information
marlonkeating authored Aug 12, 2024
2 parents f44c9bd + 65f4dc3 commit 140de60
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 59 deletions.
11 changes: 0 additions & 11 deletions enterprise_catalog/apps/api/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,17 +288,6 @@ def _update_full_content_metadata_course(content_keys, dry_run=False):
# Merge the full metadata from discovery's /api/v1/courses into the local metadata object.
metadata_record.json_metadata.update(course_metadata_dict)

# Exec ed provides the start/end dates in additional_metadata, so we should copy those over to the keys that
# we use (inside the advertised course run).
if metadata_record.is_exec_ed_2u_course:
json_meta = metadata_record.json_metadata
start_date = json_meta.get('additional_metadata', {}).get('start_date')
end_date = json_meta.get('additional_metadata', {}).get('end_date')
course_run_uuid = json_meta.get('advertised_course_run_uuid')
for run in json_meta.get('course_runs', []):
if run.get('uuid') == course_run_uuid:
run.update({'start': start_date, 'end': end_date})

# Perform more steps to normalize and move keys around
# for more consistency across content types.
metadata_record.json_metadata['normalized_metadata'] =\
Expand Down
23 changes: 21 additions & 2 deletions enterprise_catalog/apps/api/tests/test_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,23 @@ def test_update_full_metadata(self, mock_oauth_client, mock_partition_course_key
]
}],
'advertised_course_run_uuid': course_run_3_uuid,
'advertised_course_run': {
'key': f'course-v1:{course_key_3}+1',
'uuid': course_run_3_uuid,
'start': '2023-03-01T00:00:00Z',
'end': '2023-03-01T00:00:00Z',
'first_enrollable_paid_seat_price': 90,
'seats': [
{
'type': CourseMode.VERIFIED,
'upgrade_deadline': '2023-02-01T00:00:00Z',
},
{
"type": str(CourseMode.PROFESSIONAL),
"upgrade_deadline": '2022-02-01T00:00:00Z',
},
]
},
}
course_key_4 = 'edX+superDuperFakeX'
course_data_4 = {'key': course_key_4, 'full_course_only_field': 'test_4', 'programs': []}
Expand Down Expand Up @@ -640,8 +657,10 @@ def test_update_full_metadata_exec_ed(self, mock_oauth_client, mock_partition_co
'key': course_run_key,
'uuid': course_run_uuid,
# Use dummy 2022 dates that we will assert are overwritten.
'start': '2022-03-01T00:00:00Z',
'end': '2022-03-01T00:00:00Z',
'start': '2023-03-01T00:00:00Z',
'end': '2023-04-09T23:59:59Z',
'enrollment_end': '2023-02-01T00:00:00Z',
"first_enrollable_paid_seat_price": 2900,
}],
'programs': [],
'additional_metadata': {
Expand Down
25 changes: 12 additions & 13 deletions enterprise_catalog/apps/api/v1/export_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,13 @@ def course_hit_to_row(hit):


def fetch_and_format_registration_date(obj):
enroll_by_date = obj.get('registration_deadline')
"""
Args:
obj: CourseRun object
Returns:
string: date to register by
"""
enroll_by_date = obj.get('end')
stripped_enroll_by = enroll_by_date.split("T")[0]
formatted_enroll_by = None
try:
Expand All @@ -264,26 +270,19 @@ def exec_ed_course_to_row(hit):
csv_row.append(hit['partners'][0]['name'])
else:
csv_row.append(None)
if hit.get('additional_metadata'):
start_date = None
additional_md = hit['additional_metadata']
if additional_md.get('start_date'):
start_date = parser.parse(additional_md['start_date']).strftime(DATE_FORMAT)
csv_row.append(start_date)

end_date = None
if additional_md.get('end_date'):
end_date = parser.parse(additional_md['end_date']).strftime(DATE_FORMAT)
csv_row.append(end_date)
formatted_enroll_by = fetch_and_format_registration_date(additional_md)
adv_course_run = hit.get('advertised_course_run', {})
if (start_date := adv_course_run.get('start'), end_date := adv_course_run.get('end')) and start_date and end_date:
csv_row.append(start_date and parser.parse(start_date).strftime(DATE_FORMAT))
csv_row.append(end_date and parser.parse(end_date).strftime(DATE_FORMAT))
formatted_enroll_by = fetch_and_format_registration_date(adv_course_run)
else:
csv_row.append(None) # no start date
csv_row.append(None) # no end date
formatted_enroll_by = None

csv_row.append(formatted_enroll_by)

adv_course_run = hit.get('advertised_course_run', {})
key = adv_course_run.get('key')

price = float(hit['entitlements'][0]['price'])
Expand Down
4 changes: 2 additions & 2 deletions enterprise_catalog/apps/api/v1/tests/test_export_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ def test_fetch_and_format_registration_date(self):
"""
# expected hit format from algolia, porperly reformatted for csv download
assert export_utils.fetch_and_format_registration_date(
{'registration_deadline': '2002-02-15T12:12:200'}
{'end': '2002-02-15T12:12:200'}
) == '02-15-2002'
# some other format from algolia, should return None
assert export_utils.fetch_and_format_registration_date(
{'registration_deadline': '02-15-2015T12:12:200'}
{'end': '02-15-2015T12:12:200'}
) is None
11 changes: 5 additions & 6 deletions enterprise_catalog/apps/catalog/algolia_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,20 +236,18 @@ def _has_enroll_by_deadline_passed(course_json_metadata, advertised_course_run):
Helper to determine if the enrollment deadline has passed for the given course
and advertised course run. For course metadata records with a `course_type` of "course" (e.g. OCM courses),
this is based on the verified upgrade deadline.
For 2u exec ed courses, this is based on the registration deadline.
For 2u exec ed courses, this is based on the enrollment_end deadline.
"""
enroll_by_deadline_timestamp = 0
if course_json_metadata.get('course_type') == EXEC_ED_2U_COURSE_TYPE:
additional_metadata = course_json_metadata.get('additional_metadata') or {}
registration_deadline = additional_metadata.get('registration_deadline')
if registration_deadline:
enrollment_end = advertised_course_run.get('end') or {}
if enrollment_end:
enroll_by_deadline_timestamp = datetime.datetime.strptime(
registration_deadline,
enrollment_end,
'%Y-%m-%dT%H:%M:%S%z',
).timestamp()
else:
enroll_by_deadline_timestamp = _get_verified_upgrade_deadline(advertised_course_run)

return enroll_by_deadline_timestamp < localized_utcnow().timestamp()


Expand Down Expand Up @@ -1096,6 +1094,7 @@ def _get_course_run(full_course_run):
'availability': full_course_run.get('availability'),
'start': full_course_run.get('start'),
'end': full_course_run.get('end'),
'enrollment_end': full_course_run.get('enrollment_end'),
'min_effort': full_course_run.get('min_effort'),
'max_effort': full_course_run.get('max_effort'),
'weeks_to_complete': full_course_run.get('weeks_to_complete'),
Expand Down
28 changes: 6 additions & 22 deletions enterprise_catalog/apps/catalog/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,15 +95,8 @@ def advertised_course_run(self):
advertised_course_run_uuid = self.instance.json_metadata.get('advertised_course_run_uuid')
return _get_course_run_by_uuid(self.instance.json_metadata, advertised_course_run_uuid)

@cached_property
def additional_metadata(self):
return self.instance.json_metadata.get('additional_metadata', {})

@extend_schema_field(serializers.DateTimeField)
def get_start_date(self, obj) -> str:
if obj.is_exec_ed_2u_course:
return self.additional_metadata.get('start_date')

def get_start_date(self, obj) -> str: # pylint: disable=unused-argument
if not self.advertised_course_run:
return None

Expand All @@ -113,10 +106,7 @@ def get_start_date(self, obj) -> str:
return None

@extend_schema_field(serializers.DateTimeField)
def get_end_date(self, obj) -> str:
if obj.is_exec_ed_2u_course:
return self.additional_metadata.get('end_date')

def get_end_date(self, obj) -> str: # pylint: disable=unused-argument
if not self.advertised_course_run:
return None

Expand All @@ -126,17 +116,16 @@ def get_end_date(self, obj) -> str:
return None

@extend_schema_field(serializers.DateTimeField)
def get_enroll_by_date(self, obj) -> str:
if obj.is_exec_ed_2u_course:
return self.additional_metadata.get('registration_deadline')

def get_enroll_by_date(self, obj) -> str: # pylint: disable=unused-argument
if not self.advertised_course_run:
return None

all_seats = self.advertised_course_run.get('seats', [])
seat = _find_best_mode_seat(all_seats)
if seat:
return seat.get('upgrade_deadline')
if enrollment_end := self.advertised_course_run.get('enrollment_end'):
return enrollment_end
else:
logger.info(
f"No Seat Found for course run '{self.advertised_course_run.get('key')}'. "
Expand All @@ -145,12 +134,7 @@ def get_enroll_by_date(self, obj) -> str:
return None

@extend_schema_field(serializers.FloatField)
def get_content_price(self, obj) -> float:
if obj.is_exec_ed_2u_course:
for entitlement in obj.json_metadata.get('entitlements', []):
if entitlement.get('mode') == CourseMode.PAID_EXECUTIVE_EDUCATION:
return entitlement.get('price') or DEFAULT_NORMALIZED_PRICE

def get_content_price(self, obj) -> float: # pylint: disable=unused-argument
if not self.advertised_course_run:
return None

Expand Down
29 changes: 26 additions & 3 deletions enterprise_catalog/apps/catalog/tests/test_algolia_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,16 @@ class AlgoliaUtilsTests(TestCase):
{
'course_type': EXEC_ED_2U_COURSE_TYPE,
'expected_result': True,
'additional_metadata': {'registration_deadline': '2073-03-21T23:59:59Z'},
'start': _fake_upgrade_deadline(1),
'end': _fake_upgrade_deadline(30),
'enrollment_end': _fake_upgrade_deadline(1)
},
{
'course_type': EXEC_ED_2U_COURSE_TYPE,
'expected_result': False,
'additional_metadata': {'registration_deadline': '2021-03-21T23:59:59Z'},
'start': _fake_upgrade_deadline(-30),
'end': _fake_upgrade_deadline(-1),
'enrollment_end': _fake_upgrade_deadline(-30)
},
{
'course_type': EXEC_ED_2U_COURSE_TYPE,
Expand All @@ -92,7 +96,10 @@ def test_should_index_course(
course_run_availability='current',
seats=None,
course_type=COURSE,
additional_metadata=None
additional_metadata=None,
start='2023-01-29T23:59:59Z',
end='2023-02-28T23:59:59Z',
enrollment_end='2023-01-29T23:59:59Z'
):
"""
Verify that only a course that has a non-hidden advertised course run, at least one owner, and a marketing slug
Expand All @@ -113,6 +120,9 @@ def test_should_index_course(
'is_marketable': is_marketable,
'availability': course_run_availability,
'seats': seats or [],
'start': start,
'end': end,
'enrollment_end': enrollment_end
},
],
'owners': owners,
Expand Down Expand Up @@ -304,6 +314,7 @@ def test_get_course_subjects(self, course_metadata, expected_subjects):
'pacing_type': 'instructor_paced',
'start': '2013-10-16T14:00:00Z',
'end': '2014-10-16T14:00:00Z',
'enrollment_end': '2013-10-17T14:00:00Z',
'availability': 'Current',
'min_effort': 10,
'max_effort': 14,
Expand All @@ -316,6 +327,7 @@ def test_get_course_subjects(self, course_metadata, expected_subjects):
'pacing_type': 'instructor_paced',
'start': '2013-10-16T14:00:00Z',
'end': '2014-10-16T14:00:00Z',
'enrollment_end': '2013-10-17T14:00:00Z',
'availability': 'Current',
'min_effort': 10,
'max_effort': 14,
Expand All @@ -340,6 +352,7 @@ def test_get_course_subjects(self, course_metadata, expected_subjects):
'pacing_type': 'instructor_paced',
'start': '2013-10-16T14:00:00Z',
'end': '2014-10-16T14:00:00Z',
'enrollment_end': '2013-10-17T14:00:00Z',
'availability': 'Current',
'min_effort': 10,
'max_effort': 14,
Expand All @@ -359,6 +372,7 @@ def test_get_course_subjects(self, course_metadata, expected_subjects):
'pacing_type': 'instructor_paced',
'start': '2013-10-16T14:00:00Z',
'end': '2014-10-16T14:00:00Z',
'enrollment_end': '2013-10-17T14:00:00Z',
'availability': 'Current',
'min_effort': 10,
'max_effort': 14,
Expand All @@ -374,6 +388,7 @@ def test_get_course_subjects(self, course_metadata, expected_subjects):
'pacing_type': 'instructor_paced',
'start': '2013-10-16T14:00:00Z',
'end': '2014-10-16T14:00:00Z',
'enrollment_end': '2013-10-17T14:00:00Z',
'availability': 'Current',
'min_effort': 10,
'max_effort': 14,
Expand All @@ -390,6 +405,7 @@ def test_get_course_subjects(self, course_metadata, expected_subjects):
'pacing_type': 'instructor_paced',
'start': '2013-10-16T14:00:00Z',
'end': '2014-10-16T14:00:00Z',
'enrollment_end': '2013-10-17T14:00:00Z',
'availability': 'Current',
'min_effort': 10,
'max_effort': 14,
Expand Down Expand Up @@ -465,6 +481,7 @@ def test_get_upcoming_course_runs(self, searchable_course, expected_course_runs)
'availability': 'Archived',
'start': "2000-01-04T00:00:00Z",
'end': "2001-12-31T23:59:00Z",
'enrollment_end': '2000-01-04T00:00:00Z',
'min_effort': 2,
'max_effort': 6,
'weeks_to_complete': 6,
Expand All @@ -479,6 +496,7 @@ def test_get_upcoming_course_runs(self, searchable_course, expected_course_runs)
'availability': 'Current',
'start': "2018-01-04T00:00:00Z",
'end': "3022-12-31T23:59:00Z",
'enrollment_end': '2018-01-04T00:00:00Z',
'min_effort': 2,
'max_effort': 6,
'weeks_to_complete': 6,
Expand All @@ -493,6 +511,7 @@ def test_get_upcoming_course_runs(self, searchable_course, expected_course_runs)
'availability': 'Upcoming',
'start': "3000-01-04T00:00:00Z",
'end': "3022-12-31T23:59:00Z",
'enrollment_end': '3000-01-04T00:00:00Z',
'min_effort': 2,
'max_effort': 6,
'weeks_to_complete': 6,
Expand All @@ -507,6 +526,7 @@ def test_get_upcoming_course_runs(self, searchable_course, expected_course_runs)
'availability': 'Starting Soon',
'start': "3000-01-04T00:00:00Z",
'end': "3022-12-31T23:59:00Z",
'enrollment_end': '3000-01-04T00:00:00Z',
'min_effort': 2,
'max_effort': 6,
'weeks_to_complete': 6,
Expand All @@ -521,6 +541,7 @@ def test_get_upcoming_course_runs(self, searchable_course, expected_course_runs)
'availability': 'Current',
'start': "2018-01-04T00:00:00Z",
'end': "3022-12-31T23:59:00Z",
'enrollment_end': '2018-01-04T00:00:00Z',
'min_effort': 2,
'max_effort': 6,
'weeks_to_complete': 6,
Expand All @@ -532,6 +553,7 @@ def test_get_upcoming_course_runs(self, searchable_course, expected_course_runs)
'availability': 'Upcoming',
'start': "3000-01-04T00:00:00Z",
'end': "3022-12-31T23:59:00Z",
'enrollment_end': '3000-01-04T00:00:00Z',
'min_effort': 2,
'max_effort': 6,
'weeks_to_complete': 6,
Expand All @@ -543,6 +565,7 @@ def test_get_upcoming_course_runs(self, searchable_course, expected_course_runs)
'availability': 'Starting Soon',
'start': "3000-01-04T00:00:00Z",
'end': "3022-12-31T23:59:00Z",
'enrollment_end': '3000-01-04T00:00:00Z',
'min_effort': 2,
'max_effort': 6,
'weeks_to_complete': 6,
Expand Down

0 comments on commit 140de60

Please sign in to comment.