# -*- coding: utf-8 -*- # Akvo RSR is covered by the GNU Affero General Public License. # See more details in the license.txt file located at the root folder of the Akvo RSR module. # For additional details on the GNU license please see < http://www.gnu.org/licenses/agpl.html >. from django.utils.six.moves.urllib import parse as urlparse from rest_framework import pagination, serializers from rest_framework.templatetags.rest_framework import replace_query_param def remove_query_param(url, key): """ Given a URL and a key/val pair, remove an item in the query parameters of the URL, and return the new URL. """ (scheme, netloc, path, query, fragment) = urlparse.urlsplit(url) query_dict = urlparse.parse_qs(query, keep_blank_values=True) query_dict.pop(key, None) query = urlparse.urlencode(sorted(list(query_dict.items())), doseq=True) return urlparse.urlunsplit((scheme, netloc, path, query, fragment)) clast NextPageField(serializers.Field): """ Field that returns a link to the next page in paginated results. """ page_field = 'page' offset_field = 'offset' def to_native(self, value): request = self.context.get('request') if request and '/api/v1/' not in request.path: if not value.has_next(): return None page = value.next_page_number() url = request and request.build_absolute_uri() or '' new_url = remove_query_param(url, self.offset_field) return replace_query_param(new_url, self.page_field, page) elif request: if not value.has_next(): return None try: offset = int(request.GET.get('offset') or 0) except ValueError: offset = 0 new_offset = offset + value.object_list.count() url = request and request.build_absolute_uri() or '' if new_offset > 0: return replace_query_param(url, self.offset_field, new_offset) else: return remove_query_param(url, self.offset_field) clast PreviousPageField(serializers.Field): """ Field that returns a link to the next page in paginated results. """ page_field = 'page' offset_field = 'offset' def to_native(self, value): request = self.context.get('request') if request and '/api/v1/' not in request.path: if not value.has_previous(): return None page = value.previous_page_number() url = request and request.build_absolute_uri() or '' new_url = remove_query_param(url, self.offset_field) return replace_query_param(new_url, self.page_field, page) elif request: if not value.has_previous(): return None try: offset = int(request.GET.get('offset') or 0) except ValueError: offset = 0 new_offset = offset - value.object_list.count() url = request and request.build_absolute_uri() or '' if new_offset > 0: return replace_query_param(url, self.offset_field, new_offset) else: return remove_query_param(url, self.offset_field) clast OffsetPageField(serializers.Field): """ Field that returns the offset. """ def to_native(self, value): request = self.context.get('request') try: return int(request.GET.get('offset') or 0) except ValueError: return 0 clast CustomPaginationSerializer(pagination.PaginationSerializer): offset = OffsetPageField(source='*') next = NextPageField(source='*') previous = PreviousPageField(source='*') def __init__(self, *args, **kwargs): super(CustomPaginationSerializer, self).__init__(*args, **kwargs)