Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-notes-api
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
edx-notes-api
Commits
7fcd875e
Commit
7fcd875e
authored
Oct 21, 2016
by
Mushtaq Ali
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add support to search by multiple usage ids in /search end point - TNL-5604
parent
eae09382
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
70 additions
and
17 deletions
+70
-17
AUTHORS
+1
-0
notesapi/v1/tests/test_views.py
+26
-0
notesapi/v1/views.py
+43
-17
No files found.
AUTHORS
View file @
7fcd875e
...
...
@@ -2,3 +2,4 @@ Oleg Marshev <oleg@edx.org>
Tim Babych <tim.babych@gmail.com>
Christina Roberts <christina@edx.org>
Ben McMorran <ben.mcmorran@gmail.com>
Mushtaq Ali <mushtaak@gmail.com>
notesapi/v1/tests/test_views.py
View file @
7fcd875e
...
...
@@ -680,6 +680,32 @@ class AnnotationSearchViewTests(BaseAnnotationViewTests):
search_and_verify
(
"First"
,
"First one"
,
[])
search_and_verify
(
"Second"
,
"Second note"
,
[
"tag1"
,
"tag2"
])
@ddt.data
(
True
,
False
)
def
test_usage_id_search
(
self
,
is_es_disabled
):
"""
Verifies the search with usage id.
"""
self
.
_create_annotation
(
text
=
u'First one'
,
usage_id
=
'test-1'
)
self
.
_create_annotation
(
text
=
u'Second note'
,
usage_id
=
'test-2'
)
self
.
_create_annotation
(
text
=
u'Third note'
,
usage_id
=
'test-3'
)
@patch
(
'django.conf.settings.ES_DISABLED'
,
is_es_disabled
)
def
verify_usage_id_search
(
usage_ids
):
"""
Verify search results based on usage id operation.
Arguments:
usage_ids: List. The identifier string of the annotations XBlock(s).
"""
results
=
self
.
_get_search_results
(
usage_id
=
usage_ids
)
self
.
assertEqual
(
len
(
results
),
len
(
usage_ids
))
# Here we are reverse-traversing usage_ids because response has descending ordered rows.
for
index
,
u_id
in
enumerate
(
usage_ids
[::
-
1
]):
self
.
assertEqual
(
results
[
index
][
'usage_id'
],
u_id
)
verify_usage_id_search
(
usage_ids
=
[
'test-1'
])
verify_usage_id_search
(
usage_ids
=
[
'test-1'
,
'test-2'
,
'test-3'
])
def
test_search_deleted
(
self
):
"""
Tests for search method to not return deleted notes.
...
...
notesapi/v1/views.py
View file @
7fcd875e
...
...
@@ -35,10 +35,12 @@ class AnnotationSearchView(GenericAPIView):
"""
**Use Case**
* Search and return a
paginated
list of annotations for a user.
* Search and return a list of annotations for a user.
The annotations are always sorted in descending order by updated date.
Response is paginated by default except usage_id based search.
Each page in the list contains 25 annotations by default. The page
size can be altered by passing parameter "page_size=<page_size>".
...
...
@@ -58,6 +60,8 @@ class AnnotationSearchView(GenericAPIView):
GET /api/v1/search/
GET /api/v1/search/?course_id={course_id}&user={user_id}
GET /api/v1/search/?course_id={course_id}&user={user_id}&usage_id={usage_id}
GET /api/v1/search/?course_id={course_id}&user={user_id}&usage_id={usage_id}&usage_id={usage_id} ...
**Query Parameters for GET**
...
...
@@ -67,6 +71,8 @@ class AnnotationSearchView(GenericAPIView):
* user: Anonymized user id.
* usage_id: The identifier string of the annotations XBlock.
* text: Student's thoughts on the quote
* highlight: dict. Only used when search from ElasticSearch. It contains two keys:
...
...
@@ -109,31 +115,49 @@ class AnnotationSearchView(GenericAPIView):
* updated: DateTime. When was the last time annotation was updated.
"""
params
=
{}
query_params
=
{}
search_with_usage_id
=
False
def
get
(
self
,
*
args
,
**
kwargs
):
# pylint: disable=unused-argument
"""
Search annotations in most appropriate storage
"""
self
.
query_params
=
{}
self
.
search_with_usage_id
=
False
self
.
params
=
self
.
request
.
query_params
.
dict
()
usage_ids
=
self
.
request
.
query_params
.
getlist
(
'usage_id'
)
if
len
(
usage_ids
)
>
0
:
self
.
search_with_usage_id
=
True
self
.
query_params
[
'usage_id__in'
]
=
usage_ids
if
'course_id'
in
self
.
params
:
self
.
query_params
[
'course_id'
]
=
self
.
params
[
'course_id'
]
# search in DB when ES is not available or there is no need to bother it
if
settings
.
ES_DISABLED
or
'text'
not
in
self
.
request
.
query_params
.
dict
():
if
settings
.
ES_DISABLED
or
'text'
not
in
self
.
params
:
if
'user'
in
self
.
params
:
self
.
query_params
[
'user_id'
]
=
self
.
params
[
'user'
]
return
self
.
get_from_db
(
*
args
,
**
kwargs
)
else
:
if
'user'
in
self
.
params
:
self
.
query_params
[
'user'
]
=
self
.
params
[
'user'
]
return
self
.
get_from_es
(
*
args
,
**
kwargs
)
def
get_from_db
(
self
,
*
args
,
**
kwargs
):
# pylint: disable=unused-argument
"""
Search annotations in database.
"""
params
=
self
.
request
.
query_params
.
dict
()
query
=
Note
.
objects
.
filter
(
**
{
f
:
v
for
(
f
,
v
)
in
params
.
items
()
if
f
in
(
'course_id'
,
'usage_id'
)}
)
.
order_by
(
'-updated'
)
query
=
Note
.
objects
.
filter
(
**
self
.
query_params
)
.
order_by
(
'-updated'
)
if
'
user'
in
params
:
query
=
query
.
filter
(
user_id
=
params
[
'user'
]
)
if
'
text'
in
self
.
params
:
query
=
query
.
filter
(
Q
(
text__icontains
=
self
.
params
[
'text'
])
|
Q
(
tags__icontains
=
self
.
params
[
'text'
])
)
if
'text'
in
params
:
query
=
query
.
filter
(
Q
(
text__icontains
=
params
[
'text'
])
|
Q
(
tags__icontains
=
params
[
'text'
]))
# Do not send paginated result if usage id based search.
if
self
.
search_with_usage_id
:
serializer
=
NoteSerializer
(
query
,
many
=
True
)
return
Response
(
serializer
.
data
,
status
=
status
.
HTTP_200_OK
)
page
=
self
.
paginate_queryset
(
query
)
serializer
=
NoteSerializer
(
page
,
many
=
True
)
...
...
@@ -144,16 +168,13 @@ class AnnotationSearchView(GenericAPIView):
"""
Search annotations in ElasticSearch.
"""
params
=
self
.
request
.
query_params
.
dict
()
query
=
SearchQuerySet
()
.
models
(
Note
)
.
filter
(
**
{
f
:
v
for
(
f
,
v
)
in
params
.
items
()
if
f
in
(
'user'
,
'course_id'
,
'usage_id'
)}
)
query
=
SearchQuerySet
()
.
models
(
Note
)
.
filter
(
**
self
.
query_params
)
if
'text'
in
params
:
clean_text
=
query
.
query
.
clean
(
params
[
'text'
])
if
'text'
in
self
.
params
:
clean_text
=
query
.
query
.
clean
(
self
.
params
[
'text'
])
query
=
query
.
filter
(
SQ
(
data
=
clean_text
))
if
params
.
get
(
'highlight'
):
if
self
.
params
.
get
(
'highlight'
):
opts
=
{
'pre_tags'
:
[
'{elasticsearch_highlight_start}'
],
'post_tags'
:
[
'{elasticsearch_highlight_end}'
],
...
...
@@ -161,6 +182,11 @@ class AnnotationSearchView(GenericAPIView):
}
query
=
query
.
highlight
(
**
opts
)
# Do not send paginated result if usage id based search.
if
self
.
search_with_usage_id
:
serializer
=
NotesElasticSearchSerializer
(
query
,
many
=
True
)
return
Response
(
serializer
.
data
,
status
=
status
.
HTTP_200_OK
)
page
=
self
.
paginate_queryset
(
query
)
serializer
=
NotesElasticSearchSerializer
(
page
,
many
=
True
)
response
=
self
.
get_paginated_response
(
serializer
.
data
)
...
...
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