Skip to content

Commit a2ddda6

Browse files
authored
Add LearningResource.delivery field (eventually to replace learning_format) (#1510)
1 parent 1c290a8 commit a2ddda6

File tree

24 files changed

+1433
-41
lines changed

24 files changed

+1433
-41
lines changed

frontends/api/src/generated/v1/api.ts

Lines changed: 503 additions & 5 deletions
Large diffs are not rendered by default.

learning_resources/constants.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,13 +270,25 @@ class LevelType(ExtendedEnum):
270270

271271

272272
class LearningResourceFormat(ExtendedEnum):
273-
"""Enum for resource formats"""
273+
"""Enum for resource learning_format"""
274274

275275
online = "Online"
276276
hybrid = "Hybrid"
277277
in_person = "In person"
278278

279279

280+
class LearningResourceDelivery(ExtendedEnum):
281+
"""
282+
Enum for resource delivery methods. This
283+
will eventually replace LearningResourceFormat
284+
"""
285+
286+
online = "Online"
287+
hybrid = "Hybrid"
288+
in_person = "In person"
289+
offline = "Offline"
290+
291+
280292
class CertificationType(ExtendedEnum):
281293
"""Enum for resource certification types"""
282294

learning_resources/etl/ocw.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
CONTENT_TYPE_VIDEO,
2121
VALID_TEXT_FILE_TYPES,
2222
Availability,
23+
LearningResourceDelivery,
2324
LearningResourceType,
2425
OfferedBy,
2526
PlatformType,
@@ -33,7 +34,11 @@
3334
transform_levels,
3435
transform_topics,
3536
)
36-
from learning_resources.models import ContentFile, LearningResource
37+
from learning_resources.models import (
38+
ContentFile,
39+
LearningResource,
40+
default_learning_format,
41+
)
3742
from learning_resources.utils import (
3843
get_s3_object_and_read,
3944
parse_instructors,
@@ -48,6 +53,22 @@
4853
UNIQUE_FIELD = "url"
4954

5055

56+
def parse_delivery(course_data: dict) -> list[str]:
57+
"""
58+
Parse delivery methods
59+
60+
Args:
61+
url (str): The course url
62+
63+
Returns:
64+
list[str]: The delivery method(s)
65+
"""
66+
delivery = default_learning_format()
67+
if not course_data.get("hide_download"):
68+
delivery.append(LearningResourceDelivery.offline.name)
69+
return delivery
70+
71+
5172
def transform_content_files(
5273
s3_resource: boto3.resource,
5374
course_prefix: str,
@@ -343,6 +364,7 @@ def transform_course(course_data: dict) -> dict:
343364
"resource_type": LearningResourceType.course.name,
344365
"unique_field": UNIQUE_FIELD,
345366
"availability": Availability.anytime.name,
367+
"delivery": parse_delivery(course_data),
346368
}
347369

348370

learning_resources/etl/ocw_test.py

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from moto import mock_s3
1010

1111
from learning_resources.conftest import OCW_TEST_PREFIX, setup_s3_ocw
12-
from learning_resources.constants import DEPARTMENTS
12+
from learning_resources.constants import DEPARTMENTS, LearningResourceDelivery
1313
from learning_resources.etl.constants import CourseNumberType, ETLSource
1414
from learning_resources.etl.ocw import (
1515
transform_content_files,
@@ -174,19 +174,37 @@ def test_transform_content_file_needs_text_update(
174174
"term",
175175
"year",
176176
"expected_id",
177+
"hide_download",
177178
),
178179
[
179-
("legacy-uid", None, "legacyuid", False, "Spring", "2005", "16.01+spring_2005"),
180-
(None, "site-uid", "siteuid", True, "", 2005, "16.01_2005"),
181-
(None, "site-uid", "siteuid", True, "Fall", 2005, "16.01+fall_2005"),
182-
(None, "site-uid", "siteuid", True, "Fall", None, "16.01+fall"),
183-
(None, "site-uid", "siteuid", True, "", "", "16.01"),
184-
(None, "site-uid", "siteuid", True, None, None, "16.01"),
185-
(None, None, None, True, "Spring", "2005", None),
180+
(
181+
"legacy-uid",
182+
None,
183+
"legacyuid",
184+
False,
185+
"Spring",
186+
"2005",
187+
"16.01+spring_2005",
188+
False,
189+
),
190+
(None, "site-uid", "siteuid", True, "", 2005, "16.01_2005", True),
191+
(None, "site-uid", "siteuid", True, "Fall", 2005, "16.01+fall_2005", None),
192+
(None, "site-uid", "siteuid", True, "Fall", None, "16.01+fall", False),
193+
(None, "site-uid", "siteuid", True, "", "", "16.01", True),
194+
(None, "site-uid", "siteuid", True, None, None, "16.01", False),
195+
(None, None, None, True, "Spring", "2005", None, None),
186196
],
187197
)
188198
def test_transform_course( # noqa: PLR0913
189-
settings, legacy_uid, site_uid, expected_uid, has_extra_num, term, year, expected_id
199+
settings,
200+
legacy_uid,
201+
site_uid,
202+
expected_uid,
203+
has_extra_num,
204+
term,
205+
year,
206+
expected_id,
207+
hide_download,
190208
):
191209
"""transform_course should return expected data"""
192210
settings.OCW_BASE_URL = "http://test.edu/"
@@ -201,17 +219,22 @@ def test_transform_course( # noqa: PLR0913
201219
course_json["year"] = year
202220
course_json["legacy_uid"] = legacy_uid
203221
course_json["site_uid"] = site_uid
222+
course_json["hide_download"] = hide_download
204223
course_json["extra_course_numbers"] = "1, 2" if has_extra_num else None
205224
extracted_json = {
206225
**course_json,
207226
"last_modified": now_in_utc(),
208227
"slug": "slug",
209228
"url": "http://test.edu/slug",
210229
}
230+
expected_delivery = [LearningResourceDelivery.online.name] + (
231+
[] if hide_download else [LearningResourceDelivery.offline.name]
232+
)
211233
transformed_json = transform_course(extracted_json)
212234
if expected_uid:
213235
assert transformed_json["readable_id"] == expected_id
214236
assert transformed_json["etl_source"] == ETLSource.ocw.name
237+
assert transformed_json["delivery"] == expected_delivery
215238
assert transformed_json["runs"][0]["run_id"] == expected_uid
216239
assert transformed_json["runs"][0]["level"] == ["undergraduate", "high_school"]
217240
assert transformed_json["runs"][0]["semester"] == (term if term else None)

learning_resources/etl/prolearn.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
from learning_resources.constants import Availability, CertificationType
1313
from learning_resources.etl.constants import ETLSource
14-
from learning_resources.etl.utils import transform_format, transform_topics
14+
from learning_resources.etl.utils import transform_delivery, transform_topics
1515
from learning_resources.models import LearningResourceOfferor, LearningResourcePlatform
1616
from main.utils import clean_data, now_in_utc
1717

@@ -189,6 +189,7 @@ def update_format(unique_resource: dict, resource_format: list[str]):
189189
unique_resource["learning_format"] = sorted(
190190
set(unique_resource["learning_format"] + resource_format)
191191
)
192+
unique_resource["delivery"] = unique_resource["learning_format"]
192193

193194

194195
def extract_data(course_or_program: str) -> list[dict]:
@@ -265,7 +266,7 @@ def transform_programs(programs: list[dict]) -> list[dict]:
265266
"professional": True,
266267
"certification": True,
267268
"certification_type": CertificationType.professional.name,
268-
"learning_format": transform_format(program["format_name"]),
269+
"learning_format": transform_delivery(program["format_name"]),
269270
"runs": runs,
270271
"topics": parse_topic(program, offered_by.code) if offered_by else None,
271272
"courses": [
@@ -355,7 +356,7 @@ def _transform_course(
355356
"course": {
356357
"course_numbers": [],
357358
},
358-
"learning_format": transform_format(course["format_name"]),
359+
"learning_format": transform_delivery(course["format_name"]),
359360
"published": True,
360361
"topics": parse_topic(course, offered_by.code) if offered_by else None,
361362
"runs": runs,

learning_resources/etl/prolearn_test.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
transform_programs,
3333
update_format,
3434
)
35-
from learning_resources.etl.utils import transform_format
35+
from learning_resources.etl.utils import transform_delivery
3636
from learning_resources.factories import (
3737
LearningResourceOfferorFactory,
3838
LearningResourcePlatformFactory,
@@ -157,7 +157,8 @@ def test_prolearn_transform_programs(mock_csail_programs_data):
157157
else None,
158158
"etl_source": ETLSource.prolearn.name,
159159
"professional": True,
160-
"learning_format": transform_format(program["format_name"]),
160+
"learning_format": transform_delivery(program["format_name"]),
161+
"delivery": transform_delivery(program["format_name"]),
161162
"certification": True,
162163
"certification_type": CertificationType.professional.name,
163164
"runs": [
@@ -226,7 +227,8 @@ def test_prolearn_transform_courses(mock_mitpe_courses_data):
226227
"professional": True,
227228
"certification": True,
228229
"certification_type": CertificationType.professional.name,
229-
"learning_format": transform_format(course["format_name"]),
230+
"learning_format": transform_delivery(course["format_name"]),
231+
"delivery": transform_delivery(course["format_name"]),
230232
"topics": parse_topic(course, "mitpe"),
231233
"url": course["course_link"]
232234
if urlparse(course["course_link"]).path
@@ -395,6 +397,7 @@ def test_update_format(
395397
first_course["learning_format"] = old_format
396398
update_format(first_course, new_format)
397399
assert first_course["learning_format"] == sorted(expected_format)
400+
assert first_course["delivery"] == sorted(expected_format)
398401

399402

400403
@pytest.mark.parametrize("sloan_api_enabled", [True, False])

learning_resources/etl/sloan.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
)
1919
from learning_resources.etl.constants import ETLSource
2020
from learning_resources.etl.utils import (
21-
transform_format,
21+
transform_delivery,
2222
transform_topics,
2323
)
2424

@@ -202,7 +202,9 @@ def transform_course(course_data: dict, runs_data: dict) -> dict:
202202
course_runs_data = [
203203
run for run in runs_data if run["Course_Id"] == course_data["Course_Id"]
204204
]
205-
205+
format_delivery = list(
206+
{transform_delivery(run["Delivery"])[0] for run in course_runs_data}
207+
)
206208
transformed_course = {
207209
"readable_id": course_data["Course_Id"],
208210
"title": course_data["Title"],
@@ -216,9 +218,8 @@ def transform_course(course_data: dict, runs_data: dict) -> dict:
216218
"certification_type": CertificationType.professional.name,
217219
"professional": True,
218220
"published": True,
219-
"learning_format": list(
220-
{transform_format(run["Delivery"])[0] for run in course_runs_data}
221-
),
221+
"learning_format": format_delivery,
222+
"delivery": format_delivery,
222223
"topics": parse_topics(course_data["Topics"]),
223224
"course": {
224225
"course_numbers": [],

learning_resources/etl/sloan_test.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
parse_datetime,
1616
parse_image,
1717
transform_course,
18-
transform_format,
18+
transform_delivery,
1919
transform_run,
2020
)
2121
from learning_resources.factories import (
@@ -139,8 +139,9 @@ def test_transform_course(mock_sloan_courses_data, mock_sloan_runs_data):
139139
transform_run(run, course_data) for run in course_runs_data
140140
]
141141
assert transformed["learning_format"] == list(
142-
{transform_format(run["Delivery"])[0] for run in course_runs_data}
142+
{transform_delivery(run["Delivery"])[0] for run in course_runs_data}
143143
)
144+
assert transformed["delivery"] == transformed["learning_format"]
144145
assert transformed["image"] == parse_image(course_data)
145146
assert transformed["availability"] == parse_availability(course_runs_data)
146147
assert sorted(transformed.keys()) == sorted(
@@ -158,6 +159,7 @@ def test_transform_course(mock_sloan_courses_data, mock_sloan_runs_data):
158159
"professional",
159160
"published",
160161
"learning_format",
162+
"delivery",
161163
"topics",
162164
"course",
163165
"runs",

learning_resources/etl/utils.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -687,7 +687,7 @@ def most_common_topics(
687687
return [{"name": topic} for topic in common_topics]
688688

689689

690-
def transform_format(resource_format: str) -> list[str]:
690+
def transform_delivery(resource_delivery: str) -> list[str]:
691691
"""
692692
Return the correct format of the resource
693693
@@ -699,9 +699,9 @@ def transform_format(resource_format: str) -> list[str]:
699699
700700
"""
701701
try:
702-
return [RESOURCE_FORMAT_MAPPING[resource_format]]
702+
return [RESOURCE_FORMAT_MAPPING[resource_delivery]]
703703
except KeyError:
704-
log.exception("Invalid format %s", resource_format)
704+
log.exception("Invalid format %s", resource_delivery)
705705
return [LearningResourceFormat.online.name]
706706

707707

learning_resources/etl/utils_test.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -371,13 +371,15 @@ def test_most_common_topics():
371371
)
372372
def test_parse_format(original, expected):
373373
"""parse_format should return expected format"""
374-
assert utils.transform_format(original) == [expected]
374+
assert utils.transform_delivery(original) == [expected]
375375

376376

377377
def test_parse_bad_format(mocker):
378378
"""An exception log should be called for invalid formats"""
379379
mock_log = mocker.patch("learning_resources.etl.utils.log.exception")
380-
assert utils.transform_format("bad_format") == [LearningResourceFormat.online.name]
380+
assert utils.transform_delivery("bad_format") == [
381+
LearningResourceFormat.online.name
382+
]
381383
mock_log.assert_called_once_with("Invalid format %s", "bad_format")
382384

383385

0 commit comments

Comments
 (0)