Commit e3547044 by Muzaffar yousaf

Merge pull request #31 from edx/ammar/add-pagination-to-api

Paginate API
parents 05dba592 6412c4ff
...@@ -13,7 +13,7 @@ test: clean ...@@ -13,7 +13,7 @@ test: clean
./manage.py test --settings=$(test_settings) --with-coverage --with-ignore-docstrings \ ./manage.py test --settings=$(test_settings) --with-coverage --with-ignore-docstrings \
--exclude-dir=notesserver/settings --cover-inclusive --cover-branches \ --exclude-dir=notesserver/settings --cover-inclusive --cover-branches \
--cover-html --cover-html-dir=build/coverage/html/ \ --cover-html --cover-html-dir=build/coverage/html/ \
--cover-xml --cover-xml-file=build/coverage/coverage.xml \ --cover-xml --cover-xml-file=build/coverage/coverage.xml --verbosity=2 \
$(foreach package,$(PACKAGES),--cover-package=$(package)) \ $(foreach package,$(PACKAGES),--cover-package=$(package)) \
$(PACKAGES) $(PACKAGES)
......
...@@ -35,25 +35,6 @@ class Note(models.Model): ...@@ -35,25 +35,6 @@ class Note(models.Model):
note_dict['ranges'] = json.dumps(ranges) note_dict['ranges'] = json.dumps(ranges)
note_dict['user_id'] = note_dict.pop('user', None) note_dict['user_id'] = note_dict.pop('user', None)
note_dict['tags'] = json.dumps(note_dict.get('tags', list())) note_dict['tags'] = json.dumps(note_dict.get('tags', list()), ensure_ascii=False)
return cls(**note_dict) return cls(**note_dict)
def as_dict(self):
"""
Returns the note object as a dictionary.
"""
created = self.created.isoformat() if self.created else None
updated = self.updated.isoformat() if self.updated else None
return {
'id': str(self.pk),
'user': self.user_id,
'course_id': self.course_id,
'usage_id': self.usage_id,
'text': self.text,
'quote': self.quote,
'ranges': json.loads(self.ranges),
'created': created,
'updated': updated,
'tags': json.loads(self.tags)
}
"""
Paginator for Notes.
"""
from rest_framework import pagination
from rest_framework.response import Response
from django.conf import settings
class NotesPaginator(pagination.PageNumberPagination):
"""
Student Notes Paginator.
"""
page_size = settings.DEFAULT_NOTES_PAGE_SIZE
page_size_query_param = "page_size"
def get_paginated_response(self, data):
"""
Annotate the response with pagination information.
"""
return Response({
'start': (self.page.number - 1) * self.get_page_size(self.request),
'current_page': self.page.number,
'next': self.get_next_link(),
'previous': self.get_previous_link(),
'total': self.page.paginator.count,
'num_pages': self.page.paginator.num_pages,
'rows': data
})
...@@ -21,7 +21,7 @@ class HasAccessToken(BasePermission): ...@@ -21,7 +21,7 @@ class HasAccessToken(BasePermission):
"typ": "JWT" "typ": "JWT"
} }
Claims { Claims {
"sub": "<USER_ID>", "sub": "<USER ANONYMOUS ID>",
"exp": <EXPIRATION TIMESTAMP>, "exp": <EXPIRATION TIMESTAMP>,
"iat": <ISSUED TIMESTAMP>, "iat": <ISSUED TIMESTAMP>,
"aud": "<CLIENT ID" "aud": "<CLIENT ID"
......
"""
Serializers for Notes API.
"""
import json
from rest_framework import serializers
from notesapi.v1.models import Note
class NoteSerializer(serializers.ModelSerializer):
"""
Student Notes Model Serializer.
"""
class Meta(object):
"""
Model Serializer Meta Class.
"""
model = Note
exclude = ('user_id',)
id = serializers.CharField(source='pk')
user = serializers.CharField(source='user_id')
ranges = serializers.SerializerMethodField()
tags = serializers.SerializerMethodField()
def get_ranges(self, note):
"""
Return note ranges.
"""
return json.loads(note.ranges)
def get_tags(self, note):
"""
Return note tags.
"""
return json.loads(note.tags)
class NotesElasticSearchSerializer(serializers.Serializer): # pylint: disable=abstract-method
"""
Student Notes Elastic Search Serializer.
"""
id = serializers.CharField(source='pk')
user = serializers.CharField()
created = serializers.DateTimeField()
updated = serializers.DateTimeField()
quote = serializers.CharField()
course_id = serializers.CharField()
usage_id = serializers.CharField()
text = serializers.SerializerMethodField()
ranges = serializers.SerializerMethodField()
tags = serializers.SerializerMethodField()
def get_text(self, note):
"""
Return note text.
"""
if note.highlighted:
return note.highlighted[0]
return note.text
def get_ranges(self, note):
"""
Return note ranges.
"""
return json.loads(note.ranges)
def get_tags(self, note):
"""
Return note tags.
"""
if note.highlighted_tags:
return json.loads(note.highlighted_tags[0])
return json.loads(note.tags) if note.tags else []
from unittest import TestCase from unittest import TestCase
from notesapi.v1.models import Note from notesapi.v1.models import Note
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from notesapi.v1.serializers import NoteSerializer
class NoteTest(TestCase): class NoteTest(TestCase):
...@@ -27,7 +28,8 @@ class NoteTest(TestCase): ...@@ -27,7 +28,8 @@ class NoteTest(TestCase):
note = Note.create(self.note_dict.copy()) note = Note.create(self.note_dict.copy())
note.save() note.save()
result_note = note.as_dict() serializer = NoteSerializer(note)
result_note = serializer.data
del result_note['id'] del result_note['id']
del result_note['created'] del result_note['created']
del result_note['updated'] del result_note['updated']
...@@ -56,5 +58,6 @@ class NoteTest(TestCase): ...@@ -56,5 +58,6 @@ class NoteTest(TestCase):
note = Note.create(payload) note = Note.create(payload)
note.full_clean() note.full_clean()
note.save() note.save()
self.assertEqual("[]", note.tags) self.assertEqual("[]", note.tags)
self.assertEqual([], note.as_dict()["tags"]) self.assertEqual([], NoteSerializer(note).data["tags"])
...@@ -31,7 +31,7 @@ class UpdateIndexTest(BaseAnnotationViewTests): ...@@ -31,7 +31,7 @@ class UpdateIndexTest(BaseAnnotationViewTests):
end = datetime.datetime.now() end = datetime.datetime.now()
results = self._get_search_results(text='note') results = self._get_search_results(text='note')
self.assertEqual(results, {'rows': [], 'total': 0}) self.assertDictContainsSubset({'rows': [], 'total': 0}, results)
# When second note was created. # When second note was created.
call_command('update_index', start_date=second_start.isoformat(), end_date=second_end.isoformat(), verbosity=0) call_command('update_index', start_date=second_start.isoformat(), end_date=second_end.isoformat(), verbosity=0)
......
...@@ -63,7 +63,8 @@ LOGGING = get_logger_config() ...@@ -63,7 +63,8 @@ LOGGING = get_logger_config()
REST_FRAMEWORK = { REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [ 'DEFAULT_PERMISSION_CLASSES': [
'notesapi.v1.permissions.HasAccessToken' 'notesapi.v1.permissions.HasAccessToken'
] ],
'DEFAULT_PAGINATION_CLASS': 'notesapi.v1.paginators.NotesPaginator',
} }
CORS_ORIGIN_ALLOW_ALL = True CORS_ORIGIN_ALLOW_ALL = True
...@@ -80,3 +81,8 @@ CORS_ALLOW_HEADERS = ( ...@@ -80,3 +81,8 @@ CORS_ALLOW_HEADERS = (
TEMPLATE_DIRS = ( TEMPLATE_DIRS = (
'templates', 'templates',
) )
DEFAULT_NOTES_PAGE_SIZE = 25
### Maximum number of allowed notes for each student per course ###
MAX_NOTES_PER_COURSE = 500
...@@ -42,3 +42,5 @@ LOGGING = { ...@@ -42,3 +42,5 @@ LOGGING = {
} }
}, },
} }
DEFAULT_NOTES_PAGE_SIZE = 10
...@@ -10,3 +10,4 @@ MySQL-python==1.2.5 # GPL License ...@@ -10,3 +10,4 @@ MySQL-python==1.2.5 # GPL License
gunicorn==19.1.1 # MIT gunicorn==19.1.1 # MIT
path.py==3.0.1 path.py==3.0.1
python-dateutil==2.4.0 python-dateutil==2.4.0
newrelic==2.40.0.34 # New Relic
newrelic==2.40.0.34 # New Relic
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment