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
a4bed7bd
Commit
a4bed7bd
authored
Jul 13, 2016
by
Clinton Blackburn
Committed by
GitHub
Jul 13, 2016
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #159 from edx/clintonb/programs-search
Added programs search
parents
55d6f543
4439723f
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
129 additions
and
29 deletions
+129
-29
course_discovery/apps/api/filters.py
+1
-1
course_discovery/apps/api/serializers.py
+23
-5
course_discovery/apps/api/tests/test_serializers.py
+29
-4
course_discovery/apps/api/v1/urls.py
+1
-0
course_discovery/apps/api/v1/views.py
+18
-11
course_discovery/apps/course_metadata/models.py
+10
-0
course_discovery/apps/course_metadata/search_indexes.py
+25
-6
course_discovery/apps/course_metadata/tests/test_models.py
+13
-2
course_discovery/templates/search/indexes/course_metadata/program_text.txt
+9
-0
No files found.
course_discovery/apps/api/filters.py
View file @
a4bed7bd
...
@@ -46,7 +46,7 @@ class FacetQueryBuilderWithQueries(FacetQueryBuilder):
...
@@ -46,7 +46,7 @@ class FacetQueryBuilderWithQueries(FacetQueryBuilder):
def
build_query
(
self
,
**
filters
):
def
build_query
(
self
,
**
filters
):
query
=
super
(
FacetQueryBuilderWithQueries
,
self
)
.
build_query
(
**
filters
)
query
=
super
(
FacetQueryBuilderWithQueries
,
self
)
.
build_query
(
**
filters
)
facet_serializer_cls
=
self
.
view
.
get_facet_serializer_class
()
facet_serializer_cls
=
self
.
view
.
get_facet_serializer_class
()
query
[
'query_facets'
]
=
facet_serializer_cls
.
Meta
.
field_queries
query
[
'query_facets'
]
=
getattr
(
facet_serializer_cls
.
Meta
,
'field_queries'
,
{})
return
query
return
query
...
...
course_discovery/apps/api/serializers.py
View file @
a4bed7bd
...
@@ -12,7 +12,7 @@ from course_discovery.apps.catalogs.models import Catalog
...
@@ -12,7 +12,7 @@ from course_discovery.apps.catalogs.models import Catalog
from
course_discovery.apps.course_metadata.models
import
(
from
course_discovery.apps.course_metadata.models
import
(
Course
,
CourseRun
,
Image
,
Organization
,
Person
,
Prerequisite
,
Seat
,
Subject
,
Video
Course
,
CourseRun
,
Image
,
Organization
,
Person
,
Prerequisite
,
Seat
,
Subject
,
Video
)
)
from
course_discovery.apps.course_metadata.search_indexes
import
CourseIndex
,
CourseRunIndex
from
course_discovery.apps.course_metadata.search_indexes
import
CourseIndex
,
CourseRunIndex
,
ProgramIndex
User
=
get_user_model
()
User
=
get_user_model
()
...
@@ -415,7 +415,7 @@ class BaseHaystackFacetSerializer(HaystackFacetSerializer):
...
@@ -415,7 +415,7 @@ class BaseHaystackFacetSerializer(HaystackFacetSerializer):
_abstract
=
True
_abstract
=
True
def
get_fields
(
self
):
def
get_fields
(
self
):
query_facet_counts
=
self
.
instance
.
pop
(
'queries'
)
query_facet_counts
=
self
.
instance
.
pop
(
'queries'
,
{}
)
field_mapping
=
super
(
BaseHaystackFacetSerializer
,
self
)
.
get_fields
()
field_mapping
=
super
(
BaseHaystackFacetSerializer
,
self
)
.
get_fields
()
...
@@ -432,7 +432,7 @@ class BaseHaystackFacetSerializer(HaystackFacetSerializer):
...
@@ -432,7 +432,7 @@ class BaseHaystackFacetSerializer(HaystackFacetSerializer):
def
format_query_facet_data
(
self
,
query_facet_counts
):
def
format_query_facet_data
(
self
,
query_facet_counts
):
query_data
=
{}
query_data
=
{}
for
field
,
options
in
self
.
Meta
.
field_queries
.
items
():
# pylint: disable=no-member
for
field
,
options
in
getattr
(
self
.
Meta
,
'field_queries'
,
{})
.
items
():
# pylint: disable=no-member
count
=
query_facet_counts
.
get
(
field
,
0
)
count
=
query_facet_counts
.
get
(
field
,
0
)
if
count
:
if
count
:
query_data
[
field
]
=
{
query_data
[
field
]
=
{
...
@@ -468,8 +468,6 @@ class CourseFacetSerializer(BaseHaystackFacetSerializer):
...
@@ -468,8 +468,6 @@ class CourseFacetSerializer(BaseHaystackFacetSerializer):
class
CourseRunSearchSerializer
(
HaystackSerializer
):
class
CourseRunSearchSerializer
(
HaystackSerializer
):
content_type
=
serializers
.
CharField
(
source
=
'model_name'
)
class
Meta
:
class
Meta
:
field_aliases
=
COMMON_SEARCH_FIELD_ALIASES
field_aliases
=
COMMON_SEARCH_FIELD_ALIASES
fields
=
COURSE_RUN_SEARCH_FIELDS
fields
=
COURSE_RUN_SEARCH_FIELDS
...
@@ -487,6 +485,24 @@ class CourseRunFacetSerializer(BaseHaystackFacetSerializer):
...
@@ -487,6 +485,24 @@ class CourseRunFacetSerializer(BaseHaystackFacetSerializer):
ignore_fields
=
COMMON_IGNORED_FIELDS
ignore_fields
=
COMMON_IGNORED_FIELDS
class
ProgramSearchSerializer
(
HaystackSerializer
):
class
Meta
:
field_aliases
=
COMMON_SEARCH_FIELD_ALIASES
fields
=
(
'uuid'
,
'name'
,
'subtitle'
,
'category'
,
'marketing_url'
,
'organizations'
,
'text'
,)
ignore_fields
=
COMMON_IGNORED_FIELDS
index_classes
=
[
ProgramIndex
]
class
ProgramFacetSerializer
(
BaseHaystackFacetSerializer
):
serialize_objects
=
True
class
Meta
:
field_aliases
=
COMMON_SEARCH_FIELD_ALIASES
fields
=
(
'uuid'
,
'name'
,
'subtitle'
,
'category'
,
'marketing_url'
,
'organizations'
,
'text'
,)
ignore_fields
=
COMMON_IGNORED_FIELDS
index_classes
=
[
ProgramIndex
]
class
AggregateSearchSerializer
(
HaystackSerializer
):
class
AggregateSearchSerializer
(
HaystackSerializer
):
class
Meta
:
class
Meta
:
field_aliases
=
COMMON_SEARCH_FIELD_ALIASES
field_aliases
=
COMMON_SEARCH_FIELD_ALIASES
...
@@ -495,6 +511,7 @@ class AggregateSearchSerializer(HaystackSerializer):
...
@@ -495,6 +511,7 @@ class AggregateSearchSerializer(HaystackSerializer):
serializers
=
{
serializers
=
{
CourseRunIndex
:
CourseRunSearchSerializer
,
CourseRunIndex
:
CourseRunSearchSerializer
,
CourseIndex
:
CourseSearchSerializer
,
CourseIndex
:
CourseSearchSerializer
,
ProgramIndex
:
ProgramSearchSerializer
,
}
}
...
@@ -509,4 +526,5 @@ class AggregateFacetSearchSerializer(BaseHaystackFacetSerializer):
...
@@ -509,4 +526,5 @@ class AggregateFacetSearchSerializer(BaseHaystackFacetSerializer):
serializers
=
{
serializers
=
{
CourseRunIndex
:
CourseRunFacetSerializer
,
CourseRunIndex
:
CourseRunFacetSerializer
,
CourseIndex
:
CourseFacetSerializer
,
CourseIndex
:
CourseFacetSerializer
,
ProgramIndex
:
ProgramFacetSerializer
,
}
}
course_discovery/apps/api/tests/test_serializers.py
View file @
a4bed7bd
...
@@ -11,14 +11,16 @@ from course_discovery.apps.api.serializers import (
...
@@ -11,14 +11,16 @@ from course_discovery.apps.api.serializers import (
CatalogSerializer
,
CourseSerializer
,
CourseRunSerializer
,
ContainedCoursesSerializer
,
ImageSerializer
,
CatalogSerializer
,
CourseSerializer
,
CourseRunSerializer
,
ContainedCoursesSerializer
,
ImageSerializer
,
SubjectSerializer
,
PrerequisiteSerializer
,
VideoSerializer
,
OrganizationSerializer
,
SeatSerializer
,
SubjectSerializer
,
PrerequisiteSerializer
,
VideoSerializer
,
OrganizationSerializer
,
SeatSerializer
,
PersonSerializer
,
AffiliateWindowSerializer
,
ContainedCourseRunsSerializer
,
PersonSerializer
,
AffiliateWindowSerializer
,
ContainedCourseRunsSerializer
,
CourseRunSearchSerializer
)
CourseRunSearchSerializer
,
ProgramSearchSerializer
)
from
course_discovery.apps.catalogs.tests.factories
import
CatalogFactory
from
course_discovery.apps.catalogs.tests.factories
import
CatalogFactory
from
course_discovery.apps.core.models
import
User
from
course_discovery.apps.core.models
import
User
from
course_discovery.apps.core.tests.factories
import
UserFactory
from
course_discovery.apps.core.tests.factories
import
UserFactory
from
course_discovery.apps.course_metadata.models
import
CourseRun
from
course_discovery.apps.course_metadata.models
import
CourseRun
,
Program
from
course_discovery.apps.course_metadata.search_indexes
import
OrganizationsMixin
from
course_discovery.apps.course_metadata.tests.factories
import
(
from
course_discovery.apps.course_metadata.tests.factories
import
(
CourseFactory
,
CourseRunFactory
,
SubjectFactory
,
PrerequisiteFactory
,
CourseFactory
,
CourseRunFactory
,
SubjectFactory
,
PrerequisiteFactory
,
ImageFactory
,
VideoFactory
,
ImageFactory
,
VideoFactory
,
OrganizationFactory
,
PersonFactory
,
Seat
Factory
OrganizationFactory
,
PersonFactory
,
SeatFactory
,
Program
Factory
)
)
...
@@ -358,3 +360,26 @@ class CourseRunSearchSerializerTests(TestCase):
...
@@ -358,3 +360,26 @@ class CourseRunSearchSerializerTests(TestCase):
'type'
:
course_run
.
type
,
'type'
:
course_run
.
type
,
}
}
self
.
assertDictEqual
(
serializer
.
data
,
expected
)
self
.
assertDictEqual
(
serializer
.
data
,
expected
)
class
ProgramSearchSerializerTests
(
TestCase
):
def
test_data
(
self
):
program
=
ProgramFactory
()
organization
=
OrganizationFactory
()
program
.
organizations
.
add
(
organization
)
program
.
save
()
# NOTE: This serializer expects SearchQuerySet results, so we run a search on the newly-created object
# to generate such a result.
result
=
SearchQuerySet
()
.
models
(
Program
)
.
filter
(
uuid
=
program
.
uuid
)[
0
]
serializer
=
ProgramSearchSerializer
(
result
)
expected
=
{
'uuid'
:
str
(
program
.
uuid
),
'name'
:
program
.
name
,
'subtitle'
:
program
.
subtitle
,
'category'
:
program
.
category
,
'marketing_url'
:
program
.
marketing_url
,
'organizations'
:
[
OrganizationsMixin
.
format_organization
(
organization
)],
}
self
.
assertDictEqual
(
serializer
.
data
,
expected
)
course_discovery/apps/api/v1/urls.py
View file @
a4bed7bd
...
@@ -19,5 +19,6 @@ router.register(r'management', views.ManagementViewSet, base_name='management')
...
@@ -19,5 +19,6 @@ router.register(r'management', views.ManagementViewSet, base_name='management')
router
.
register
(
r'search/all'
,
views
.
AggregateSearchViewSet
,
base_name
=
'search-all'
)
router
.
register
(
r'search/all'
,
views
.
AggregateSearchViewSet
,
base_name
=
'search-all'
)
router
.
register
(
r'search/courses'
,
views
.
CourseSearchViewSet
,
base_name
=
'search-courses'
)
router
.
register
(
r'search/courses'
,
views
.
CourseSearchViewSet
,
base_name
=
'search-courses'
)
router
.
register
(
r'search/course_runs'
,
views
.
CourseRunSearchViewSet
,
base_name
=
'search-course_runs'
)
router
.
register
(
r'search/course_runs'
,
views
.
CourseRunSearchViewSet
,
base_name
=
'search-course_runs'
)
router
.
register
(
r'search/programs'
,
views
.
ProgramSearchViewSet
,
base_name
=
'search-programs'
)
urlpatterns
+=
router
.
urls
urlpatterns
+=
router
.
urls
course_discovery/apps/api/v1/views.py
View file @
a4bed7bd
...
@@ -29,7 +29,7 @@ from course_discovery.apps.api.renderers import AffiliateWindowXMLRenderer, Cour
...
@@ -29,7 +29,7 @@ from course_discovery.apps.api.renderers import AffiliateWindowXMLRenderer, Cour
from
course_discovery.apps.catalogs.models
import
Catalog
from
course_discovery.apps.catalogs.models
import
Catalog
from
course_discovery.apps.core.utils
import
SearchQuerySetWrapper
from
course_discovery.apps.core.utils
import
SearchQuerySetWrapper
from
course_discovery.apps.course_metadata.constants
import
COURSE_ID_REGEX
,
COURSE_RUN_ID_REGEX
from
course_discovery.apps.course_metadata.constants
import
COURSE_ID_REGEX
,
COURSE_RUN_ID_REGEX
from
course_discovery.apps.course_metadata.models
import
Course
,
CourseRun
,
Seat
from
course_discovery.apps.course_metadata.models
import
Course
,
CourseRun
,
Seat
,
Program
logger
=
logging
.
getLogger
(
__name__
)
logger
=
logging
.
getLogger
(
__name__
)
User
=
get_user_model
()
User
=
get_user_model
()
...
@@ -360,14 +360,14 @@ class AffiliateWindowViewSet(viewsets.ViewSet):
...
@@ -360,14 +360,14 @@ class AffiliateWindowViewSet(viewsets.ViewSet):
return
Response
(
serializer
.
data
)
return
Response
(
serializer
.
data
)
class
Base
Course
HaystackViewSet
(
FacetMixin
,
HaystackViewSet
):
class
BaseHaystackViewSet
(
FacetMixin
,
HaystackViewSet
):
document_uid_field
=
'key'
document_uid_field
=
'key'
facet_filter_backends
=
[
HaystackFacetFilterWithQueries
,
HaystackFilter
]
facet_filter_backends
=
[
HaystackFacetFilterWithQueries
,
HaystackFilter
]
load_all
=
True
load_all
=
True
lookup_field
=
'key'
lookup_field
=
'key'
permission_classes
=
(
IsAuthenticated
,)
permission_classes
=
(
IsAuthenticated
,)
# NOTE: We use PageNumberPagination because drf-haytack's facet serializer relies on the page_query_param
# NOTE: We use PageNumberPagination because drf-hay
s
tack's facet serializer relies on the page_query_param
# attribute, and it is more appropriate for search results than our default limit-offset pagination.
# attribute, and it is more appropriate for search results than our default limit-offset pagination.
pagination_class
=
PageNumberPagination
pagination_class
=
PageNumberPagination
...
@@ -382,7 +382,7 @@ class BaseCourseHaystackViewSet(FacetMixin, HaystackViewSet):
...
@@ -382,7 +382,7 @@ class BaseCourseHaystackViewSet(FacetMixin, HaystackViewSet):
type: string
type: string
required: false
required: false
"""
"""
return
super
(
Base
Course
HaystackViewSet
,
self
)
.
list
(
request
,
*
args
,
**
kwargs
)
return
super
(
BaseHaystackViewSet
,
self
)
.
list
(
request
,
*
args
,
**
kwargs
)
@list_route
(
methods
=
[
"get"
],
url_path
=
"facets"
)
@list_route
(
methods
=
[
"get"
],
url_path
=
"facets"
)
def
facets
(
self
,
request
):
def
facets
(
self
,
request
):
...
@@ -412,13 +412,13 @@ class BaseCourseHaystackViewSet(FacetMixin, HaystackViewSet):
...
@@ -412,13 +412,13 @@ class BaseCourseHaystackViewSet(FacetMixin, HaystackViewSet):
pytype: str
pytype: str
required: false
required: false
"""
"""
return
super
(
Base
Course
HaystackViewSet
,
self
)
.
facets
(
request
)
return
super
(
BaseHaystackViewSet
,
self
)
.
facets
(
request
)
def
filter_facet_queryset
(
self
,
queryset
):
def
filter_facet_queryset
(
self
,
queryset
):
queryset
=
super
(
Base
Course
HaystackViewSet
,
self
)
.
filter_facet_queryset
(
queryset
)
queryset
=
super
(
BaseHaystackViewSet
,
self
)
.
filter_facet_queryset
(
queryset
)
facet_serializer_cls
=
self
.
get_facet_serializer_class
()
facet_serializer_cls
=
self
.
get_facet_serializer_class
()
field_queries
=
facet_serializer_cls
.
Meta
.
field_queries
field_queries
=
getattr
(
facet_serializer_cls
.
Meta
,
'field_queries'
,
{})
for
facet
in
self
.
request
.
query_params
.
getlist
(
'selected_query_facets'
):
for
facet
in
self
.
request
.
query_params
.
getlist
(
'selected_query_facets'
):
query
=
field_queries
.
get
(
facet
)
query
=
field_queries
.
get
(
facet
)
...
@@ -431,20 +431,27 @@ class BaseCourseHaystackViewSet(FacetMixin, HaystackViewSet):
...
@@ -431,20 +431,27 @@ class BaseCourseHaystackViewSet(FacetMixin, HaystackViewSet):
return
queryset
return
queryset
class
CourseSearchViewSet
(
Base
Course
HaystackViewSet
):
class
CourseSearchViewSet
(
BaseHaystackViewSet
):
facet_serializer_class
=
serializers
.
CourseFacetSerializer
facet_serializer_class
=
serializers
.
CourseFacetSerializer
index_models
=
(
Course
,)
index_models
=
(
Course
,)
serializer_class
=
serializers
.
CourseSearchSerializer
serializer_class
=
serializers
.
CourseSearchSerializer
class
CourseRunSearchViewSet
(
Base
Course
HaystackViewSet
):
class
CourseRunSearchViewSet
(
BaseHaystackViewSet
):
facet_serializer_class
=
serializers
.
CourseRunFacetSerializer
facet_serializer_class
=
serializers
.
CourseRunFacetSerializer
index_models
=
(
CourseRun
,)
index_models
=
(
CourseRun
,)
serializer_class
=
serializers
.
CourseRunSearchSerializer
serializer_class
=
serializers
.
CourseRunSearchSerializer
# TODO Remove the detail routes. They don't work, and make no sense here given that we cannot specify the type.
class
ProgramSearchViewSet
(
BaseHaystackViewSet
):
class
AggregateSearchViewSet
(
BaseCourseHaystackViewSet
):
document_uid_field
=
'uuid'
lookup_field
=
'uuid'
facet_serializer_class
=
serializers
.
ProgramFacetSerializer
index_models
=
(
Program
,)
serializer_class
=
serializers
.
ProgramSearchSerializer
class
AggregateSearchViewSet
(
BaseHaystackViewSet
):
""" Search all content types. """
""" Search all content types. """
facet_serializer_class
=
serializers
.
AggregateFacetSearchSerializer
facet_serializer_class
=
serializers
.
AggregateFacetSearchSerializer
serializer_class
=
serializers
.
AggregateSearchSerializer
serializer_class
=
serializers
.
AggregateSearchSerializer
course_discovery/apps/course_metadata/models.py
View file @
a4bed7bd
import
datetime
import
datetime
import
logging
import
logging
from
urllib.parse
import
urljoin
from
uuid
import
uuid4
from
uuid
import
uuid4
import
pytz
import
pytz
from
django.conf
import
settings
from
django.db
import
models
from
django.db
import
models
from
django.db.models.query_utils
import
Q
from
django.db.models.query_utils
import
Q
from
django.utils.translation
import
ugettext_lazy
as
_
from
django.utils.translation
import
ugettext_lazy
as
_
...
@@ -425,5 +427,13 @@ class Program(TimeStampedModel):
...
@@ -425,5 +427,13 @@ class Program(TimeStampedModel):
organizations
=
models
.
ManyToManyField
(
Organization
,
blank
=
True
)
organizations
=
models
.
ManyToManyField
(
Organization
,
blank
=
True
)
@property
def
marketing_url
(
self
):
if
self
.
marketing_slug
:
path
=
'{category}/{slug}'
.
format
(
category
=
self
.
category
,
slug
=
self
.
marketing_slug
)
return
urljoin
(
settings
.
MARKETING_URL_ROOT
,
path
)
return
None
def
__str__
(
self
):
def
__str__
(
self
):
return
self
.
name
return
self
.
name
course_discovery/apps/course_metadata/search_indexes.py
View file @
a4bed7bd
from
haystack
import
indexes
from
haystack
import
indexes
from
opaque_keys.edx.keys
import
CourseKey
from
opaque_keys.edx.keys
import
CourseKey
from
course_discovery.apps.course_metadata.models
import
Course
,
CourseRun
from
course_discovery.apps.course_metadata.models
import
Course
,
CourseRun
,
Program
class
OrganizationsMixin
:
@classmethod
def
format_organization
(
cls
,
organization
):
return
'{key}: {name}'
.
format
(
key
=
organization
.
key
,
name
=
organization
.
name
)
def
prepare_organizations
(
self
,
obj
):
return
[
self
.
format_organization
(
organization
)
for
organization
in
obj
.
organizations
.
all
()]
class
BaseIndex
(
indexes
.
SearchIndex
):
class
BaseIndex
(
indexes
.
SearchIndex
):
...
@@ -23,7 +32,7 @@ class BaseIndex(indexes.SearchIndex):
...
@@ -23,7 +32,7 @@ class BaseIndex(indexes.SearchIndex):
return
self
.
model
.
objects
.
all
()
return
self
.
model
.
objects
.
all
()
class
BaseCourseIndex
(
BaseIndex
):
class
BaseCourseIndex
(
OrganizationsMixin
,
BaseIndex
):
key
=
indexes
.
CharField
(
model_attr
=
'key'
,
stored
=
True
)
key
=
indexes
.
CharField
(
model_attr
=
'key'
,
stored
=
True
)
title
=
indexes
.
CharField
(
model_attr
=
'title'
)
title
=
indexes
.
CharField
(
model_attr
=
'title'
)
short_description
=
indexes
.
CharField
(
model_attr
=
'short_description'
,
null
=
True
)
short_description
=
indexes
.
CharField
(
model_attr
=
'short_description'
,
null
=
True
)
...
@@ -31,10 +40,6 @@ class BaseCourseIndex(BaseIndex):
...
@@ -31,10 +40,6 @@ class BaseCourseIndex(BaseIndex):
subjects
=
indexes
.
MultiValueField
(
faceted
=
True
)
subjects
=
indexes
.
MultiValueField
(
faceted
=
True
)
organizations
=
indexes
.
MultiValueField
(
faceted
=
True
)
organizations
=
indexes
.
MultiValueField
(
faceted
=
True
)
def
prepare_organizations
(
self
,
obj
):
return
[
'{key}: {name}'
.
format
(
key
=
organization
.
key
,
name
=
organization
.
name
)
for
organization
in
obj
.
organizations
.
all
()]
def
prepare_subjects
(
self
,
obj
):
def
prepare_subjects
(
self
,
obj
):
return
[
subject
.
name
for
subject
in
obj
.
subjects
.
all
()]
return
[
subject
.
name
for
subject
in
obj
.
subjects
.
all
()]
...
@@ -97,3 +102,17 @@ class CourseRunIndex(BaseCourseIndex, indexes.Indexable):
...
@@ -97,3 +102,17 @@ class CourseRunIndex(BaseCourseIndex, indexes.Indexable):
def
prepare_transcript_languages
(
self
,
obj
):
def
prepare_transcript_languages
(
self
,
obj
):
return
[
self
.
_prepare_language
(
language
)
for
language
in
obj
.
transcript_languages
.
all
()]
return
[
self
.
_prepare_language
(
language
)
for
language
in
obj
.
transcript_languages
.
all
()]
class
ProgramIndex
(
OrganizationsMixin
,
BaseIndex
,
indexes
.
Indexable
):
model
=
Program
uuid
=
indexes
.
CharField
(
model_attr
=
'uuid'
)
name
=
indexes
.
CharField
(
model_attr
=
'name'
)
subtitle
=
indexes
.
CharField
(
model_attr
=
'subtitle'
)
category
=
indexes
.
CharField
(
model_attr
=
'category'
,
faceted
=
True
)
marketing_url
=
indexes
.
CharField
(
model_attr
=
'marketing_url'
,
null
=
True
)
organizations
=
indexes
.
MultiValueField
(
faceted
=
True
)
def
prepare_content_type
(
self
,
obj
):
return
'program_{category}'
.
format
(
category
=
obj
.
category
)
course_discovery/apps/course_metadata/tests/test_models.py
View file @
a4bed7bd
...
@@ -3,6 +3,7 @@ import datetime
...
@@ -3,6 +3,7 @@ import datetime
import
ddt
import
ddt
import
mock
import
mock
import
pytz
import
pytz
from
django.conf
import
settings
from
django.test
import
TestCase
from
django.test
import
TestCase
from
course_discovery.apps.core.utils
import
SearchQuerySetWrapper
from
course_discovery.apps.core.utils
import
SearchQuerySetWrapper
...
@@ -243,6 +244,16 @@ class ProgramTests(TestCase):
...
@@ -243,6 +244,16 @@ class ProgramTests(TestCase):
def
test_str
(
self
):
def
test_str
(
self
):
"""Verify that a program is properly converted to a str."""
"""Verify that a program is properly converted to a str."""
program
=
factories
.
ProgramFactory
()
program
=
factories
.
ProgramFactory
()
program_str
=
program
.
name
self
.
assertEqual
(
str
(
program
),
program
.
name
)
self
.
assertEqual
(
str
(
program
),
program_str
)
def
test_marketing_url
(
self
):
""" Verify the property creates a complete marketing URL. """
program
=
factories
.
ProgramFactory
()
expected
=
'{root}/{category}/{slug}'
.
format
(
root
=
settings
.
MARKETING_URL_ROOT
.
strip
(
'/'
),
category
=
program
.
category
,
slug
=
program
.
marketing_slug
)
self
.
assertEqual
(
program
.
marketing_url
,
expected
)
def
test_marketing_url_without_slug
(
self
):
""" Verify the property returns None if the Program has no marketing_slug set. """
program
=
factories
.
ProgramFactory
(
marketing_slug
=
''
)
self
.
assertIsNone
(
program
.
marketing_url
)
course_discovery/templates/search/indexes/course_metadata/program_text.txt
0 → 100644
View file @
a4bed7bd
{{ object.uuid }}
{{ object.name }}
{{ object.category }}
{{ object.status }}
{{ object.marketing_slug|default:'' }}
{% for organization in object.organizations.all %}
{{ organization.key }}: {{ organization.name }}
{% endfor %}
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