<commit_before>""" A mini API framework.
"""

import json
import re
from urllib import urlencode

from django.contrib.gis.measure import D
from django.core.exceptions import ObjectDoesNotExist
from django.http import HttpResponse, Http404, HttpResponseForbidden, HttpResponseBadRequest
from django.template import loader, RequestContext
from django.template.defaultfilters import escapejs
from django.utils import importlib
from django.views.generic import View

from tastypie.exceptions import BadRequest
from tastypie.paginator import Paginator

from boundaries import kml
from boundaries.models import app_settings

class RawJSONResponse(object):
    """APIView subclasses can return these if they have
    already-serialized JSON to return"""
    def __init__(self, content):
        self.content = content

throttle = None
if getattr(app_settings, 'THROTTLE', ''):
    def _import_from_string(val):
        parts = val.split('.')
        module_path, class_name = '.'.join(parts[:-1]), parts[-1]
        module = importlib.import_module(module_path)
        return getattr(module, class_name)
    throttle = _import_from_string(app_settings.THROTTLE)()


class APIView(View):
    """Base view class that serializes subclass responses to JSON.

    Subclasses should define get/post/etc. methods."""

    allow_jsonp = True
    content_type = 'application/json; charset=utf-8'

    def dispatch(self, request, *args, **kwargs):

        if throttle is not None:
            if not throttle.allow_request(request, self):
                msg = ("You've exceeded the request limit."
                    " Try again in %d seconds." % throttle.wait() +
                    " Contact us if you need to be able to exceed the default limit.")
                return HttpResponse(msg,
                    content_type='text/plain',
                    status=503)

        try:
            result = super(APIView, self).dispatch(request, *args, **kwargs)
        except BadRequest as e:
            return HttpResponseBadRequest(unicode(e), mimetype='text/plain')
        if isinstance(result, HttpResponse):
            return result
        if request.GET.get('format') == 'apibrowser':
            return self.apibrowser_response(request, result)
        resp = HttpResponse(content_type=self.content_type)
        callback = ''
        if self.allow_jsonp and 'callback' in request.GET:
            callback = re.sub(r'[^a-zA-Z0-9_]', '', request.GET['callback'])
            resp.write(callback + '(')
        if isinstance(result, RawJSONResponse):
            resp.write(result.content)
        else:
            json.dump(result, resp, indent=(4 if request.GET.get('pretty') else None))
        if callback:
            resp.write(');')
        return resp

    def apibrowser_response(self, request, result):
        """If format=apibrowser, return a prettified HTML reponse."""
        if isinstance(result, RawJSONResponse):
            result = json.loads(result.content)
        jsonresult = json.dumps(result, indent=4)
        t = loader.get_template('boundaries/apibrowser.html')
        json_url = request.path
        params = dict([k, v.encode('utf-8')] for k,v in request.GET.items())
        params.pop('format')
        if params:
            json_url += '?' + urlencode(params)
        ctx = {
            'json': jsonresult,
            'resource_name': self.model.__name__,
            'is_list': isinstance(self, ModelListView),
            'json_url': json_url
        }
        if ctx['is_list']:
            ctx['title'] = self.model._meta.verbose_name_plural
        else:
            ctx['title'] = self.model._meta.verbose_name
        c = RequestContext(request, ctx)
        return HttpResponse(t.render(c))

class ModelListView(APIView):
    """Base API class for a list of resources.

    Subclasses should set the 'model' attribute to the appropriate model class.
    Set the filterable_fields attribute to a list of field names users should
    be able to filter on.

    Compatible model classes should define a static method called get_dicts that,
    given a list of objects, returns a list of dicts suitable for serialization.
    By default, those will be model objects, but the model can also define a static
    method called 'prepare_queryset_for_get_dicts' that accepts a queryset and returns
    a sliceable iterable of objects that will later be passed to get_dicts."""

    filter_types = ['exact', 'iexact', 'contains', 'icontains',
                    'startswith', 'istartswith', 'endswith', 'iendswith', 'isnull']

    def get_qs(self, request):
        return self.model.objects.all()

    def filter(self, request, qs):
        for (f, val) in request.GET.items():
            if '__' in f:
                (filter_field, filter_type) = f.split('__')
            else:
                (filter_field, filter_type) = (f, 'exact')
            if filter_field in getattr(self, 'filterable_fields', []) and filter_type in self.filter_types:
                if val in ['true', 'True']:
                    val = True
                elif val in ['false', 'False']:
                    val = False
                qs = qs.filter(**{filter_field + '__' + filter_type: val})
        return qs

    def get_related_resources(self, request, qs, meta):
        return {}

    def get(self, request, **kwargs):
        qs = self.get_qs(request, **kwargs)
        qs = self.filter(request, qs)
        if hasattr(self.model, 'prepare_queryset_for_get_dicts'):
            qs = self.model.prepare_queryset_for_get_dicts(qs)
        paginator = Paginator(request.GET, qs, resource_uri=request.path)
        result = paginator.page()
        result['objects'] = self.model.get_dicts(result['objects'])
        related = self.get_related_resources(request, qs, result['meta'])
        if related:
            result['meta']['related'] = related
        return result

class ModelGeoListView(ModelListView):
    """Adds geospatial support to ModelListView.

    Subclasses must set the 'allowed_geo_fields' attribute to a list
    of geospatial field names which we're allowed to provide.

    'name_field' should be the name of the field on objects that
     contains a name value

    To enable a couple of default geospatial filters, the
    default_geo_filter_field attribute should be set to the name
    of the geometry field to filter on.

    To access a geospatial field, the field name must be provided
    by the URLconf in the 'geo_field' keyword argument."""

    name_field = 'name'
    default_geo_filter_field = None

    def filter(self, request, qs):
        qs = super(ModelGeoListView, self).filter(request, qs)

        if self.default_geo_filter_field:
            if 'contains' in request.GET:
                try:
                    lat, lon = re.sub(r'[^\d.,-]', '', request.GET['contains']).split(',')
                    wkt_pt = 'POINT(%s %s)' % (lon, lat)
                except ValueError:
                    raise BadRequest("Invalid lat/lon values")
                qs = qs.filter(**{self.default_geo_filter_field + "__contains" : wkt_pt})

            if 'near' in request.GET:
                lat, lon, range = request.GET['near'].split(',')
                wkt_pt = 'POINT(%s %s)' % (float(lon), float(lat))
                numeral = re.match('([0-9]+)', range).group(1)
                unit = range[len(numeral):]
                numeral = int(numeral)
                kwargs = {unit: numeral}
                qs = qs.filter(**{self.default_geo_filter_field + "__distance_lte" :(wkt_pt, D(**kwargs))})

        return qs

    def get(self, request, **kwargs):
        if 'geo_field' not in kwargs:
            # If it's not a geo request, let ModelListView handle it.
            return super(ModelGeoListView, self).get(request, **kwargs)

        field = kwargs.pop('geo_field')
        if field not in self.allowed_geo_fields:
            raise Http404
        qs = self.get_qs(request, **kwargs)
        qs = self.filter(request, qs)

        if qs.count() > app_settings.MAX_GEO_LIST_RESULTS:
            return HttpResponseForbidden(
                "Spatial-list queries cannot return more than %d resources; this query would return %s. Please filter your query."
                % (app_settings.MAX_GEO_LIST_RESULTS, qs.count()))

        format = request.GET.get('format', 'json')

        if format in ('json', 'apibrowser'):
            strings = [u'{ "objects" : [ ']
            strings.append(','.join( (u'{"name": "%s","%s":%s}' % (escapejs(x[1]),field,x[0].geojson)
                        for x in qs.values_list(field, self.name_field) )))
            strings.append(u']}')
            return RawJSONResponse(u''.join(strings))
        elif format == 'wkt':
            return HttpResponse("\n".join((geom.wkt for geom in qs.values_list(field, flat=True))), mimetype="text/plain")
        elif format == 'kml':
            placemarks = [kml.generate_placemark(x[1], x[0]) for x in qs.values_list(field, self.name_field)]
            return HttpResponse(kml.generate_kml_document(placemarks), mimetype="application/vnd.google-earth.kml+xml")
        else:
            raise NotImplementedError

class ModelDetailView(APIView):
    """Return the API representation of a single object.

    Subclasses must set the 'model' attribute to the appropriate model class.
    Subclasses must define a 'get_object' method to return a single model
      object. Its argument will be the request, a QuerySet of objects from
      which to select, and any keyword arguments provided by the URLconf.

    Compatible model classes must define an as_dict instance method which
    returns a serializable dict of the object's data."""

    def __init__(self):
        super(ModelDetailView, self).__init__()
        self.base_qs = self.model.objects.all()

    def get(self, request, **kwargs):
        try:
            return self.get_object(request, self.base_qs, **kwargs).as_dict()
        except ObjectDoesNotExist:
            raise Http404

class ModelGeoDetailView(ModelDetailView):
    """Adds geospatial support to ModelDetailView

    Subclasses must set the 'allowed_geo_fields' attribute to a list
    of geospatial field names which we're allowed to provide.

    To access a geospatial field, the field name must be provided
    by the URLconf in the 'geo_field' keyword argument."""

    name_field = 'name'

    def get(self, request, **kwargs):
        if 'geo_field' not in kwargs:
            # If it's not a geo request, let ModelDetailView handle it.
            return super(ModelGeoDetailView, self).get(request, **kwargs)

        field = kwargs.pop('geo_field')
        if field not in self.allowed_geo_fields:
            raise Http404

        try:
            obj = self.get_object(request, self.base_qs.only(field, self.name_field), **kwargs)
        except ObjectDoesNotExist:
            raise Http404

        geom = getattr(obj, field)
        name = getattr(obj, self.name_field)
        format = request.GET.get('format', 'json')
        if format in ('json', 'apibrowser'):
            return RawJSONResponse(geom.geojson)
        elif format == 'wkt':
            return HttpResponse(geom.wkt, mimetype="text/plain")
        elif format == 'kml':
            return HttpResponse(
                kml.generate_kml_document([kml.generate_placemark(name, geom)]),
                mimetype="application/vnd.google-earth.kml+xml")
        else:
            raise NotImplementedError
<commit_msg>Allow None values in filters<commit_after>""" A mini API framework.
"""

import json
import re
from urllib import urlencode

from django.contrib.gis.measure import D
from django.core.exceptions import ObjectDoesNotExist
from django.http import HttpResponse, Http404, HttpResponseForbidden, HttpResponseBadRequest
from django.template import loader, RequestContext
from django.template.defaultfilters import escapejs
from django.utils import importlib
from django.views.generic import View

from tastypie.exceptions import BadRequest
from tastypie.paginator import Paginator

from boundaries import kml
from boundaries.models import app_settings

class RawJSONResponse(object):
    """APIView subclasses can return these if they have
    already-serialized JSON to return"""
    def __init__(self, content):
        self.content = content

throttle = None
if getattr(app_settings, 'THROTTLE', ''):
    def _import_from_string(val):
        parts = val.split('.')
        module_path, class_name = '.'.join(parts[:-1]), parts[-1]
        module = importlib.import_module(module_path)
        return getattr(module, class_name)
    throttle = _import_from_string(app_settings.THROTTLE)()


class APIView(View):
    """Base view class that serializes subclass responses to JSON.

    Subclasses should define get/post/etc. methods."""

    allow_jsonp = True
    content_type = 'application/json; charset=utf-8'

    def dispatch(self, request, *args, **kwargs):

        if throttle is not None:
            if not throttle.allow_request(request, self):
                msg = ("You've exceeded the request limit."
                    " Try again in %d seconds." % throttle.wait() +
                    " Contact us if you need to be able to exceed the default limit.")
                return HttpResponse(msg,
                    content_type='text/plain',
                    status=503)

        try:
            result = super(APIView, self).dispatch(request, *args, **kwargs)
        except BadRequest as e:
            return HttpResponseBadRequest(unicode(e), mimetype='text/plain')
        if isinstance(result, HttpResponse):
            return result
        if request.GET.get('format') == 'apibrowser':
            return self.apibrowser_response(request, result)
        resp = HttpResponse(content_type=self.content_type)
        callback = ''
        if self.allow_jsonp and 'callback' in request.GET:
            callback = re.sub(r'[^a-zA-Z0-9_]', '', request.GET['callback'])
            resp.write(callback + '(')
        if isinstance(result, RawJSONResponse):
            resp.write(result.content)
        else:
            json.dump(result, resp, indent=(4 if request.GET.get('pretty') else None))
        if callback:
            resp.write(');')
        return resp

    def apibrowser_response(self, request, result):
        """If format=apibrowser, return a prettified HTML reponse."""
        if isinstance(result, RawJSONResponse):
            result = json.loads(result.content)
        jsonresult = json.dumps(result, indent=4)
        t = loader.get_template('boundaries/apibrowser.html')
        json_url = request.path
        params = dict([k, v.encode('utf-8')] for k,v in request.GET.items())
        params.pop('format')
        if params:
            json_url += '?' + urlencode(params)
        ctx = {
            'json': jsonresult,
            'resource_name': self.model.__name__,
            'is_list': isinstance(self, ModelListView),
            'json_url': json_url
        }
        if ctx['is_list']:
            ctx['title'] = self.model._meta.verbose_name_plural
        else:
            ctx['title'] = self.model._meta.verbose_name
        c = RequestContext(request, ctx)
        return HttpResponse(t.render(c))

class ModelListView(APIView):
    """Base API class for a list of resources.

    Subclasses should set the 'model' attribute to the appropriate model class.
    Set the filterable_fields attribute to a list of field names users should
    be able to filter on.

    Compatible model classes should define a static method called get_dicts that,
    given a list of objects, returns a list of dicts suitable for serialization.
    By default, those will be model objects, but the model can also define a static
    method called 'prepare_queryset_for_get_dicts' that accepts a queryset and returns
    a sliceable iterable of objects that will later be passed to get_dicts."""

    filter_types = ['exact', 'iexact', 'contains', 'icontains',
                    'startswith', 'istartswith', 'endswith', 'iendswith', 'isnull']

    def get_qs(self, request):
        return self.model.objects.all()

    def filter(self, request, qs):
        for (f, val) in request.GET.items():
            if '__' in f:
                (filter_field, filter_type) = f.split('__')
            else:
                (filter_field, filter_type) = (f, 'exact')
            if filter_field in getattr(self, 'filterable_fields', []) and filter_type in self.filter_types:
                if val in ['true', 'True']:
                    val = True
                elif val in ['false', 'False']:
                    val = False
                elif val in ['none', 'None']:
                    val = None
                qs = qs.filter(**{filter_field + '__' + filter_type: val})
        return qs

    def get_related_resources(self, request, qs, meta):
        return {}

    def get(self, request, **kwargs):
        qs = self.get_qs(request, **kwargs)
        qs = self.filter(request, qs)
        if hasattr(self.model, 'prepare_queryset_for_get_dicts'):
            qs = self.model.prepare_queryset_for_get_dicts(qs)
        paginator = Paginator(request.GET, qs, resource_uri=request.path)
        result = paginator.page()
        result['objects'] = self.model.get_dicts(result['objects'])
        related = self.get_related_resources(request, qs, result['meta'])
        if related:
            result['meta']['related'] = related
        return result

class ModelGeoListView(ModelListView):
    """Adds geospatial support to ModelListView.

    Subclasses must set the 'allowed_geo_fields' attribute to a list
    of geospatial field names which we're allowed to provide.

    'name_field' should be the name of the field on objects that
     contains a name value

    To enable a couple of default geospatial filters, the
    default_geo_filter_field attribute should be set to the name
    of the geometry field to filter on.

    To access a geospatial field, the field name must be provided
    by the URLconf in the 'geo_field' keyword argument."""

    name_field = 'name'
    default_geo_filter_field = None

    def filter(self, request, qs):
        qs = super(ModelGeoListView, self).filter(request, qs)

        if self.default_geo_filter_field:
            if 'contains' in request.GET:
                try:
                    lat, lon = re.sub(r'[^\d.,-]', '', request.GET['contains']).split(',')
                    wkt_pt = 'POINT(%s %s)' % (lon, lat)
                except ValueError:
                    raise BadRequest("Invalid lat/lon values")
                qs = qs.filter(**{self.default_geo_filter_field + "__contains" : wkt_pt})

            if 'near' in request.GET:
                lat, lon, range = request.GET['near'].split(',')
                wkt_pt = 'POINT(%s %s)' % (float(lon), float(lat))
                numeral = re.match('([0-9]+)', range).group(1)
                unit = range[len(numeral):]
                numeral = int(numeral)
                kwargs = {unit: numeral}
                qs = qs.filter(**{self.default_geo_filter_field + "__distance_lte" :(wkt_pt, D(**kwargs))})

        return qs

    def get(self, request, **kwargs):
        if 'geo_field' not in kwargs:
            # If it's not a geo request, let ModelListView handle it.
            return super(ModelGeoListView, self).get(request, **kwargs)

        field = kwargs.pop('geo_field')
        if field not in self.allowed_geo_fields:
            raise Http404
        qs = self.get_qs(request, **kwargs)
        qs = self.filter(request, qs)

        if qs.count() > app_settings.MAX_GEO_LIST_RESULTS:
            return HttpResponseForbidden(
                "Spatial-list queries cannot return more than %d resources; this query would return %s. Please filter your query."
                % (app_settings.MAX_GEO_LIST_RESULTS, qs.count()))

        format = request.GET.get('format', 'json')

        if format in ('json', 'apibrowser'):
            strings = [u'{ "objects" : [ ']
            strings.append(','.join( (u'{"name": "%s","%s":%s}' % (escapejs(x[1]),field,x[0].geojson)
                        for x in qs.values_list(field, self.name_field) )))
            strings.append(u']}')
            return RawJSONResponse(u''.join(strings))
        elif format == 'wkt':
            return HttpResponse("\n".join((geom.wkt for geom in qs.values_list(field, flat=True))), mimetype="text/plain")
        elif format == 'kml':
            placemarks = [kml.generate_placemark(x[1], x[0]) for x in qs.values_list(field, self.name_field)]
            return HttpResponse(kml.generate_kml_document(placemarks), mimetype="application/vnd.google-earth.kml+xml")
        else:
            raise NotImplementedError

class ModelDetailView(APIView):
    """Return the API representation of a single object.

    Subclasses must set the 'model' attribute to the appropriate model class.
    Subclasses must define a 'get_object' method to return a single model
      object. Its argument will be the request, a QuerySet of objects from
      which to select, and any keyword arguments provided by the URLconf.

    Compatible model classes must define an as_dict instance method which
    returns a serializable dict of the object's data."""

    def __init__(self):
        super(ModelDetailView, self).__init__()
        self.base_qs = self.model.objects.all()

    def get(self, request, **kwargs):
        try:
            return self.get_object(request, self.base_qs, **kwargs).as_dict()
        except ObjectDoesNotExist:
            raise Http404

class ModelGeoDetailView(ModelDetailView):
    """Adds geospatial support to ModelDetailView

    Subclasses must set the 'allowed_geo_fields' attribute to a list
    of geospatial field names which we're allowed to provide.

    To access a geospatial field, the field name must be provided
    by the URLconf in the 'geo_field' keyword argument."""

    name_field = 'name'

    def get(self, request, **kwargs):
        if 'geo_field' not in kwargs:
            # If it's not a geo request, let ModelDetailView handle it.
            return super(ModelGeoDetailView, self).get(request, **kwargs)

        field = kwargs.pop('geo_field')
        if field not in self.allowed_geo_fields:
            raise Http404

        try:
            obj = self.get_object(request, self.base_qs.only(field, self.name_field), **kwargs)
        except ObjectDoesNotExist:
            raise Http404

        geom = getattr(obj, field)
        name = getattr(obj, self.name_field)
        format = request.GET.get('format', 'json')
        if format in ('json', 'apibrowser'):
            return RawJSONResponse(geom.geojson)
        elif format == 'wkt':
            return HttpResponse(geom.wkt, mimetype="text/plain")
        elif format == 'kml':
            return HttpResponse(
                kml.generate_kml_document([kml.generate_placemark(name, geom)]),
                mimetype="application/vnd.google-earth.kml+xml")
        else:
            raise NotImplementedError

{'fraction_non_alphanumeric': 0.10537941787941787, 'fraction_numerical': 0.002858627858627859, 'mean_word_length': 2.516984006092917, 'pattern_counts': {'":': 4, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 4}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3708848', 'n_tokens_mistral': 6716, 'n_tokens_neox': 6435, 'n_words': 1919}