<commit_before>from datetime import datetime
from django.contrib.auth.signals import user_logged_out
from django.db.models import Q
from django.dispatch import receiver

from rest_framework import mixins, permissions, status, viewsets
from rest_framework.authentication import (
    SessionAuthentication, TokenAuthentication
)
from rest_framework.response import Response
from utils.user_utils import hash_value

from . import models
from . import serializers


class IsOwner(permissions.BasePermission):
    """Only allow owners of an object to view/edit it."""

    def has_object_permission(self, request, view, obj):
        return obj.user == request.user


class GCMDeviceViewSet(mixins.CreateModelMixin,
                       mixins.ListModelMixin,
                       viewsets.GenericViewSet):
    """This endpoint allows an Android client to register a User's device for
    notifications via
    [Google Cloud Messaging](https://developer.android.com/google/gcm).

    To register a device, you must POST the following information to
    `/api/notifications/devices/`:

    * `device_name`: (optional) a name for the device. It's best to provide
      this information if possible.
    * `registration_id`: This is the device's registration ID. For more info,
      see the [Register for GCM](https://developer.android.com/google/gcm/client.html#sample-register) section in the android developer documentation.
    * `is_active`: (optional) Defaults to True; whether or not the device accepts
      notifications.

    GET requests to this enpoint will list a user's registered devices, including
    the following information:

    * `id`: A unique Database ID for the device.
    * `user`: The device owner's database ID
    * `device_name`: The supplied name for the device
    * `device_id`: A unique identifier for the device. This is used along with
      the user's information to uniquely identify a device, and to update the
      registration id when it changes.
    * `registration_id`: This is the device's registration ID. For more info,
      see the [Register for GCM](https://developer.android.com/google/gcm/client.html#sample-register) section in the android developer documentation.
    * `is_active`: (optional) Defaults to True; whether or not the device accepts
      notifications.
    * `created_on`: The date the device data was created
    * `updated_on`: The date the device data was last updated
    * `object_type`: Will always be the string "gcmdevice"

    ----

    """
    authentication_classes = (TokenAuthentication, SessionAuthentication)
    queryset = models.GCMDevice.objects.all()
    serializer_class = serializers.GCMDeviceSerializer
    permission_classes = [IsOwner]

    def get_queryset(self):
        return self.queryset.filter(user__id=self.request.user.id)

    def _update(self, request, device_id=None):
        """Update the user's registration ID for the given device_id. If not
        device ID is given, or if the device ID is not found, this method will
        update any of the user's GCMDevice entries, and remove those without
        a device id.

        """
        device = None
        device_name = request.data.get('device_name', '')
        active = request.data.get('is_active', True)
        registration_id = request.data.get('registration_id', None)

        try:
            # Device ID is given, try updating that.
            params = {'user': request.user, 'device_id': device_id}
            device = models.GCMDevice.objects.get(**params)
        except (models.GCMDevice.DoesNotExist, models.GCMDevice.MultipleObjectsReturned):
            # Device ID doesn't exist in our DB, just pick a device who's
            # ID is None and update it.
            params = {'user': request.user, 'device_id': None}
            device = models.GCMDevice.objects.filter(**params).first()

        if device:
            device.device_id = device_id
            device.device_name = device_name
            device.is_active = active
            device.registration_id = registration_id
            device.save()

        if device and device_id:
            # If we _did_ update the device with an ID, remove all of the old
            # None-type device objects.
            params = {'user': request.user, 'device_id': None}
            models.GCMDevice.objects.filter(**params).delete()

        serializer_class = self.get_serializer_class()
        serializer = serializer_class(device)
        return Response(serializer.data)

    def create(self, request, *args, **kwargs):
        """Handles POST requests, but this method also checks for existing
        instances of a device, and calls `update` when appropriate."""

        # Only do this for authenticated users.
        if request.user.is_authenticated():
            request.data['user'] = request.user.id

            # Generate the device id (a hash of device name + user email)
            device_name = request.data.get('device_name', 'unkown')
            device_id = hash_value("{}+{}".format(request.user.email, device_name))
            request.data['device_id'] = device_id

            # Check to see if this already exists:
            reg_id = request.data.get('registration_id', None)
            params = {'user': request.user, 'registration_id': reg_id}
            if reg_id and models.GCMDevice.objects.filter(**params).exists():
                return Response(None, status=status.HTTP_304_NOT_MODIFIED)

            # Otherwise, calculate our device id hash and see if that exists.
            devices = models.GCMDevice.objects.filter(user=request.user).filter(
                Q(device_id=device_id) | Q(device_id=None)
            )
            if devices.exists():
                # The user's device already exists, let's update instead.
                return self._update(request, device_id=device_id)

        return super(GCMDeviceViewSet, self).create(request, *args, **kwargs)


@receiver(user_logged_out, dispatch_uid='remove_gcm_device_on_logout')
def remove_gcm_device_on_logout(sender, request, user, **kwargs):
    """When a user logs out, see if they sent a request to remove their
    GCM registration_id, as well.

    Since this signal fires AFTER logout, the user is None, and request.user
    is an AnonymousUser object.

    """
    # NOTE: request may be a rest_framework.request.Request object.<commit_msg>clarifying comments<commit_after>from datetime import datetime
from django.contrib.auth.signals import user_logged_out
from django.db.models import Q
from django.dispatch import receiver

from rest_framework import mixins, permissions, status, viewsets
from rest_framework.authentication import (
    SessionAuthentication, TokenAuthentication
)
from rest_framework.response import Response
from utils.user_utils import hash_value

from . import models
from . import serializers


class IsOwner(permissions.BasePermission):
    """Only allow owners of an object to view/edit it."""

    def has_object_permission(self, request, view, obj):
        return obj.user == request.user


class GCMDeviceViewSet(mixins.CreateModelMixin,
                       mixins.ListModelMixin,
                       viewsets.GenericViewSet):
    """This endpoint allows an Android client to register a User's device for
    notifications via
    [Google Cloud Messaging](https://developer.android.com/google/gcm).

    To register a device, you must POST the following information to
    `/api/notifications/devices/`:

    * `device_name`: (optional) a name for the device. It's best to provide
      this information if possible.
    * `registration_id`: This is the device's registration ID. For more info,
      see the [Register for GCM](https://developer.android.com/google/gcm/client.html#sample-register) section in the android developer documentation.
    * `is_active`: (optional) Defaults to True; whether or not the device accepts
      notifications.

    GET requests to this enpoint will list a user's registered devices, including
    the following information:

    * `id`: A unique Database ID for the device.
    * `user`: The device owner's database ID
    * `device_name`: The supplied name for the device
    * `device_id`: A unique identifier for the device. This is used along with
      the user's information to uniquely identify a device, and to update the
      registration id when it changes.
    * `registration_id`: This is the device's registration ID. For more info,
      see the [Register for GCM](https://developer.android.com/google/gcm/client.html#sample-register) section in the android developer documentation.
    * `is_active`: (optional) Defaults to True; whether or not the device accepts
      notifications.
    * `created_on`: The date the device data was created
    * `updated_on`: The date the device data was last updated
    * `object_type`: Will always be the string "gcmdevice"

    ----

    """
    authentication_classes = (TokenAuthentication, SessionAuthentication)
    queryset = models.GCMDevice.objects.all()
    serializer_class = serializers.GCMDeviceSerializer
    permission_classes = [IsOwner]

    def get_queryset(self):
        return self.queryset.filter(user__id=self.request.user.id)

    def _update(self, request, device_id=None):
        """Update the user's registration ID for the given device_id. If not
        device ID is given, or if the device ID is not found, this method will
        update any of the user's GCMDevice entries, and remove those without
        a device id.

        """
        device = None
        device_name = request.data.get('device_name', '')
        active = request.data.get('is_active', True)
        registration_id = request.data.get('registration_id', None)

        try:
            # Device ID is given, try updating that.
            params = {'user': request.user, 'device_id': device_id}
            device = models.GCMDevice.objects.get(**params)
        except (models.GCMDevice.DoesNotExist, models.GCMDevice.MultipleObjectsReturned):
            # Device ID doesn't exist in our DB, just pick a device who's
            # ID is None and update it.
            params = {'user': request.user, 'device_id': None}
            device = models.GCMDevice.objects.filter(**params).first()

        if device:
            device.device_id = device_id
            device.device_name = device_name
            device.is_active = active
            device.registration_id = registration_id
            device.save()

        if device and device_id:
            # If we _did_ update the device with an ID, remove all of the old
            # None-type device objects.
            params = {'user': request.user, 'device_id': None}
            models.GCMDevice.objects.filter(**params).delete()

        serializer_class = self.get_serializer_class()
        serializer = serializer_class(device)
        return Response(serializer.data)

    def create(self, request, *args, **kwargs):
        """Handles POST requests, but this method also checks for existing
        instances of a device, and calls `update` when appropriate."""

        # Only do this for authenticated users.
        if request.user.is_authenticated():
            request.data['user'] = request.user.id

            # Generate the device id (a hash of device name + user email)
            device_name = request.data.get('device_name', 'unkown')
            device_id = hash_value("{}+{}".format(request.user.email, device_name))
            request.data['device_id'] = device_id

            # Check to see if the Registration ID already exists:
            reg_id = request.data.get('registration_id', None)
            params = {'user': request.user, 'registration_id': reg_id}
            if reg_id and models.GCMDevice.objects.filter(**params).exists():
                return Response(None, status=status.HTTP_304_NOT_MODIFIED)

            # Otherwise, look for this device, and update its registration id
            devices = models.GCMDevice.objects.filter(user=request.user).filter(
                Q(device_id=device_id) | Q(device_id=None)
            )
            if devices.exists():
                return self._update(request, device_id=device_id)

        return super(GCMDeviceViewSet, self).create(request, *args, **kwargs)


@receiver(user_logged_out, dispatch_uid='remove_gcm_device_on_logout')
def remove_gcm_device_on_logout(sender, request, user, **kwargs):
    """When a user logs out, see if they sent a request to remove their
    GCM registration_id, as well.

    Since this signal fires AFTER logout, the user is None, and request.user
    is an AnonymousUser object.

    """
    # NOTE: request may be a rest_framework.request.Request object.
{'fraction_non_alphanumeric': 0.08715776261081039, 'fraction_numerical': 0.0004706989879971758, 'mean_word_length': 2.908031882280809, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 6, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, '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': '6522383', 'n_tokens_mistral': 3485, 'n_tokens_neox': 3310, 'n_words': 1141}