Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
C
course-discovery
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
course-discovery
Commits
fecf558a
Commit
fecf558a
authored
Apr 15, 2016
by
Peter Fogg
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #72 from edx/peter-fogg/add-sponsors-staff
Add sponsors and staff to the Drupal data loader.
parents
f420a67c
691bdcfa
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
126 additions
and
3 deletions
+126
-3
course_discovery/apps/course_metadata/data_loaders.py
+39
-1
course_discovery/apps/course_metadata/tests/test_data_loaders.py
+87
-2
No files found.
course_discovery/apps/course_metadata/data_loaders.py
View file @
fecf558a
...
...
@@ -12,7 +12,7 @@ from opaque_keys.edx.keys import CourseKey
from
course_discovery.apps.core.models
import
Currency
from
course_discovery.apps.course_metadata.models
import
(
Course
,
CourseOrganization
,
CourseRun
,
Image
,
LanguageTag
,
LevelType
,
Organization
,
Seat
,
Subject
,
Video
Course
,
CourseOrganization
,
CourseRun
,
Image
,
LanguageTag
,
LevelType
,
Organization
,
Person
,
Seat
,
Subject
,
Video
)
logger
=
logging
.
getLogger
(
__name__
)
...
...
@@ -241,6 +241,10 @@ class DrupalApiDataLoader(AbstractDataLoader):
course
=
self
.
update_course
(
cleaned_body
)
self
.
update_course_run
(
course
,
cleaned_body
)
# Clean up orphan data on the end of many-to-many relationships
Person
.
objects
.
filter
(
courses_staffed
=
None
)
.
delete
()
Organization
.
objects
.
filter
(
courseorganization__isnull
=
True
)
.
delete
()
logger
.
info
(
'Retrieved
%
d course runs from
%
s.'
,
len
(
data
),
self
.
api_url
)
def
update_course
(
self
,
body
):
...
...
@@ -260,6 +264,7 @@ class DrupalApiDataLoader(AbstractDataLoader):
course
.
level_type
=
level_type
self
.
set_subjects
(
course
,
body
)
self
.
set_sponsors
(
course
,
body
)
course
.
save
()
return
course
...
...
@@ -273,6 +278,23 @@ class DrupalApiDataLoader(AbstractDataLoader):
subject
,
__
=
Subject
.
objects
.
get_or_create
(
name
=
subject_name
.
title
())
course
.
subjects
.
add
(
subject
)
def
set_sponsors
(
self
,
course
,
body
):
"""Update `course` with sponsors from `body`."""
course
.
courseorganization_set
.
filter
(
relation_type
=
CourseOrganization
.
SPONSOR
)
.
delete
()
for
sponsor_body
in
body
[
'sponsors'
]:
image
,
__
=
Image
.
objects
.
get_or_create
(
src
=
sponsor_body
[
'image'
])
defaults
=
{
'name'
:
sponsor_body
[
'title'
],
'logo_image'
:
image
,
'homepage_url'
:
urljoin
(
settings
.
MARKETING_URL_ROOT
,
sponsor_body
[
'uri'
])
}
organization
,
__
=
Organization
.
objects
.
update_or_create
(
key
=
sponsor_body
[
'uuid'
],
defaults
=
defaults
)
CourseOrganization
.
objects
.
create
(
course
=
course
,
organization
=
organization
,
relation_type
=
CourseOrganization
.
SPONSOR
)
def
update_course_run
(
self
,
course
,
body
):
"""
Create or update a run of `course` from Drupal data given by `body`.
...
...
@@ -285,9 +307,25 @@ class DrupalApiDataLoader(AbstractDataLoader):
return
None
course_run
.
language
=
self
.
get_language_tag
(
body
)
course_run
.
course
=
course
self
.
set_staff
(
course_run
,
body
)
course_run
.
save
()
return
course_run
def
set_staff
(
self
,
course_run
,
body
):
"""Update `course_run` with staff from `body`."""
course_run
.
staff
.
clear
()
for
staff_body
in
body
[
'staff'
]:
image
,
__
=
Image
.
objects
.
get_or_create
(
src
=
staff_body
[
'image'
])
defaults
=
{
'name'
:
staff_body
[
'title'
],
'profile_image'
:
image
,
'title'
:
staff_body
[
'display_position'
][
'title'
],
}
person
,
__
=
Person
.
objects
.
update_or_create
(
key
=
staff_body
[
'uuid'
],
defaults
=
defaults
)
course_run
.
staff
.
add
(
person
)
def
get_language_tag
(
self
,
body
):
"""Get a language tag from Drupal data given by `body`."""
iso_code
=
body
[
'current_language'
]
...
...
course_discovery/apps/course_metadata/tests/test_data_loaders.py
View file @
fecf558a
...
...
@@ -15,7 +15,7 @@ from course_discovery.apps.course_metadata.data_loaders import (
OrganizationsApiDataLoader
,
CoursesApiDataLoader
,
DrupalApiDataLoader
,
EcommerceApiDataLoader
,
AbstractDataLoader
)
from
course_discovery.apps.course_metadata.models
import
(
Course
,
Course
Run
,
Image
,
LanguageTag
,
Organizati
on
,
Seat
,
Subject
Course
,
Course
Organization
,
CourseRun
,
Image
,
LanguageTag
,
Organization
,
Pers
on
,
Seat
,
Subject
)
from
course_discovery.apps.course_metadata.tests.factories
import
CourseRunFactory
,
SeatFactory
...
...
@@ -390,6 +390,9 @@ class DrupalApiDataLoaderTests(DataLoaderTestMixin, TestCase):
'title'
:
'A partial course'
,
}
ORPHAN_ORGANIZATION_KEY
=
'orphan_org'
ORPHAN_STAFF_KEY
=
'orphan_staff'
api_url
=
MARKETING_API_URL
loader_class
=
DrupalApiDataLoader
...
...
@@ -397,13 +400,25 @@ class DrupalApiDataLoaderTests(DataLoaderTestMixin, TestCase):
super
(
DrupalApiDataLoaderTests
,
self
)
.
setUp
()
for
course_dict
in
self
.
EXISTING_COURSE_AND_RUN_DATA
:
course
=
Course
.
objects
.
create
(
key
=
course_dict
[
'course_key'
],
title
=
course_dict
[
'title'
])
CourseRun
.
objects
.
create
(
course_run
=
CourseRun
.
objects
.
create
(
key
=
course_dict
[
'course_run_key'
],
language
=
self
.
loader
.
get_language_tag
(
course_dict
),
course
=
course
)
# Add some data that doesn't exist in Drupal already
person
=
Person
.
objects
.
create
(
key
=
'orphan_staff_'
+
course_run
.
key
)
course_run
.
staff
.
add
(
person
)
organization
=
Organization
.
objects
.
create
(
key
=
'orphan_org_'
+
course
.
key
)
CourseOrganization
.
objects
.
create
(
organization
=
organization
,
course
=
course
,
relation_type
=
CourseOrganization
.
SPONSOR
)
Course
.
objects
.
create
(
key
=
self
.
EXISTING_COURSE
[
'course_key'
],
title
=
self
.
EXISTING_COURSE
[
'title'
])
Person
.
objects
.
create
(
key
=
self
.
ORPHAN_STAFF_KEY
)
Organization
.
objects
.
create
(
key
=
self
.
ORPHAN_ORGANIZATION_KEY
)
def
mock_api
(
self
):
"""Mock out the Drupal API. Returns a list of mocked-out course runs."""
...
...
@@ -421,6 +436,27 @@ class DrupalApiDataLoaderTests(DataLoaderTestMixin, TestCase):
'current_language'
:
self
.
EXISTING_COURSE_AND_RUN_DATA
[
0
][
'current_language'
],
'subtitle'
:
'Learn about Bread'
,
'description'
:
'<p><b>Bread</b> is a <a href="/wiki/Staple_food" title="Staple food">staple food</a>.'
,
'sponsors'
:
[{
'uuid'
:
'abc123'
,
'title'
:
'Tatte'
,
'image'
:
'http://example.com/tatte.jpg'
,
'uri'
:
'sponsor/tatte'
}],
'staff'
:
[{
'uuid'
:
'staff123'
,
'title'
:
'The Muffin Man'
,
'image'
:
'http://example.com/muffinman.jpg'
,
'display_position'
:
{
'title'
:
'Baker'
}
},
{
'uuid'
:
'staffZYX'
,
'title'
:
'Arthur'
,
'image'
:
'http://example.com/kingarthur.jpg'
,
'display_position'
:
{
'title'
:
'King'
}
}]
},
{
'title'
:
self
.
EXISTING_COURSE_AND_RUN_DATA
[
1
][
'title'
],
'level'
:
{
...
...
@@ -434,6 +470,15 @@ class DrupalApiDataLoaderTests(DataLoaderTestMixin, TestCase):
'current_language'
:
self
.
EXISTING_COURSE_AND_RUN_DATA
[
1
][
'current_language'
],
'subtitle'
:
'Testing 201'
,
'description'
:
"how to test better"
,
'sponsors'
:
[],
'staff'
:
[{
'uuid'
:
'432staff'
,
'title'
:
'Test'
,
'image'
:
'http://example.com/test.jpg'
,
'display_position'
:
{
'title'
:
'Tester'
}
}]
},
{
# Create a course which exists in LMS/Otto, but without course runs
'title'
:
self
.
EXISTING_COURSE
[
'title'
],
'level'
:
{
...
...
@@ -447,6 +492,18 @@ class DrupalApiDataLoaderTests(DataLoaderTestMixin, TestCase):
'current_language'
:
'en-us'
,
'subtitle'
:
'Nope'
,
'description'
:
'what is fake?'
,
'sponsors'
:
[{
'uuid'
:
'123abc'
,
'title'
:
'Fake'
,
'image'
:
'http://example.com/fake.jpg'
,
'uri'
:
'sponsor/fake'
},
{
'uuid'
:
'qwertyuiop'
,
'title'
:
'Faux'
,
'image'
:
'http://example.com/faux.jpg'
,
'uri'
:
'sponsor/faux'
}],
'staff'
:
[],
},
{
# Create a fake course run which doesn't exist in LMS/Otto
'title'
:
'A partial course'
,
'level'
:
{
...
...
@@ -460,6 +517,8 @@ class DrupalApiDataLoaderTests(DataLoaderTestMixin, TestCase):
'current_language'
:
'en-us'
,
'subtitle'
:
'Nope'
,
'description'
:
'what is real?'
,
'sponsors'
:
[],
'staff'
:
[],
}]
}
...
...
@@ -486,12 +545,22 @@ class DrupalApiDataLoaderTests(DataLoaderTestMixin, TestCase):
self
.
assertEqual
(
course_run
.
course
,
course
)
self
.
assert_course_loaded
(
course
,
body
)
self
.
assert_staff_loaded
(
course_run
,
body
)
if
course_run
.
language
:
self
.
assertEqual
(
course_run
.
language
.
code
,
body
[
'current_language'
])
else
:
self
.
assertEqual
(
body
[
'current_language'
],
''
)
def
assert_staff_loaded
(
self
,
course_run
,
body
):
"""Verify that staff have been loaded correctly."""
course_run_staff
=
course_run
.
staff
.
all
()
api_staff
=
body
[
'staff'
]
self
.
assertEqual
(
len
(
course_run_staff
),
len
(
api_staff
))
for
api_staff_member
in
api_staff
:
loaded_staff_member
=
Person
.
objects
.
get
(
key
=
api_staff_member
[
'uuid'
])
self
.
assertIn
(
loaded_staff_member
,
course_run_staff
)
def
assert_course_loaded
(
self
,
course
,
body
):
"""Verify that the course has been loaded correctly."""
self
.
assertEqual
(
course
.
title
,
body
[
'title'
])
...
...
@@ -501,6 +570,7 @@ class DrupalApiDataLoaderTests(DataLoaderTestMixin, TestCase):
self
.
assertEqual
(
course
.
level_type
.
name
,
body
[
'level'
][
'title'
])
self
.
assert_subjects_loaded
(
course
,
body
)
self
.
assert_sponsors_loaded
(
course
,
body
)
def
assert_subjects_loaded
(
self
,
course
,
body
):
"""Verify that subjects have been loaded correctly."""
...
...
@@ -511,6 +581,15 @@ class DrupalApiDataLoaderTests(DataLoaderTestMixin, TestCase):
loaded_subject
=
Subject
.
objects
.
get
(
name
=
api_subject
[
'title'
]
.
title
())
self
.
assertIn
(
loaded_subject
,
course_subjects
)
def
assert_sponsors_loaded
(
self
,
course
,
body
):
"""Verify that sponsors have been loaded correctly."""
course_sponsors
=
course
.
sponsors
.
all
()
api_sponsors
=
body
[
'sponsors'
]
self
.
assertEqual
(
len
(
course_sponsors
),
len
(
api_sponsors
))
for
api_sponsor
in
api_sponsors
:
loaded_sponsor
=
Organization
.
objects
.
get
(
key
=
api_sponsor
[
'uuid'
])
self
.
assertIn
(
loaded_sponsor
,
course_sponsors
)
@responses.activate
def
test_ingest
(
self
):
"""Verify the data loader ingests data from Drupal."""
...
...
@@ -533,6 +612,12 @@ class DrupalApiDataLoaderTests(DataLoaderTestMixin, TestCase):
# Verify multiple calls to ingest data do NOT result in data integrity errors.
self
.
loader
.
ingest
()
# Verify that orphan data is deleted
self
.
assertFalse
(
Person
.
objects
.
filter
(
key
=
self
.
ORPHAN_STAFF_KEY
)
.
exists
())
self
.
assertFalse
(
Organization
.
objects
.
filter
(
key
=
self
.
ORPHAN_ORGANIZATION_KEY
)
.
exists
())
self
.
assertFalse
(
Person
.
objects
.
filter
(
key__startswith
=
'orphan_staff_'
)
.
exists
())
self
.
assertFalse
(
Organization
.
objects
.
filter
(
key__startswith
=
'orphan_org_'
)
.
exists
())
@ddt.data
(
(
''
,
''
),
(
'<h1>foo</h1>'
,
'# foo'
),
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment