class IsOwner(permissions.IsAuthenticated):
    """
    Only the object's owner can view or edit
    Assumes the model instance has a `user` attribute.
    """

    def has_object_permission(self, request, view, obj):
        # Instance must have an attribute named `user`.
        if isinstance(obj, User):
            return obj == request.user
        else:
            return obj.user == request.user class NotificationSetting(CoreModel):
    notification_type = models.ForeignKey(NotificationType, related_name="user_settings", on_delete=models.CASCADE)
    user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='notification_settings', on_delete=models.CASCADE)
    allow_push = models.BooleanField(default=True)
    allow_email = models.BooleanField(default=True)

    class Meta:
        unique_together = ('notification_type', 'user')
        ordering = ['-created']

    def __unicode__(self):
        return "{}: {}".format(self.user, self.notification_type)

    def name(self):
        return "{}: {}".format(self.user, self.notification_type) class Notification(CoreModel):
    PUSH = "push"
    EMAIL = "email"

    notification_type = models.ForeignKey(NotificationType, related_name="notifications", on_delete=models.CASCADE)
    template_override = models.CharField(max_length=100, blank=True, null=True)
    deep_link = models.CharField(max_length=100, blank=True, null=True)
    user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name="notifications_received", null=True, blank=True,
                             on_delete=models.CASCADE)
    reporter = models.ForeignKey(settings.AUTH_USER_MODEL, related_name="notifications_sent", null=True, blank=True,
                                 on_delete=models.CASCADE)

    # The object that the notification is about, not the social model related to it
    # E.g., if you Like a Post, the content_object for the notification is the Post, not the Like
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField(db_index=True)
    content_object = GenericForeignKey()

    def message(self, location):
        """
        Takes our configured notifications and creates a message
        replacing the appropriate variables from the content object
        """

        data = defaultdict(str)
        # get the domain from Site
        data['domain'] = Site.objects.get_current().domain

        if self.content_object:
            data['identifier'] = self.content_object.identifier()
        if self.reporter:
            data['reporter'] = self.reporter.identifier()

        if hasattr(self.content_object, 'extra_notification_params'):
            data.update(self.content_object.extra_notification_params())

        configured_template_name = "{}.html".format(self.notification_type.slug)
        template_name = self.template_override if self.template_override else configured_template_name
        return render_to_string("notifications/{}/{}".format(location, template_name), data)

    def email_message(self):
        return self.message(Notification.EMAIL)

    def push_message(self):
        message = self.message(Notification.PUSH)
        if self.reporter:
            return "{0} {1}".format(self.reporter, message)
        else:
            return "{0}".format(message)

    @property
    def name(self):
        return "{}".format(self.notification_type)

    def __unicode__(self):
        return "{}: {}".format(self.user, self.notification_type)

    class Meta:
        ordering = ['-created'] @task
def create_notification(receiver, reporter, content_object, notification_type, template_override=None, reply_to=None,
                        deep_link=None):
    # If the receiver of this notification is the same as the reporter or
    # if the user has blocked this type, then don't create
    if receiver == reporter:
        return

    notification = Notification.objects.create(user=receiver,
                                               reporter=reporter,
                                               content_object=content_object,
                                               notification_type=notification_type,
                                               template_override=template_override,
                                               deep_link=deep_link)
    notification.save()

    notification_setting = NotificationSetting.objects.get(notification_type=notification_type, user=receiver)
    if notification_setting.allow_push and yak_settings.ALLOW_PUSH:
        from .utils import send_push_notification
        send_push_notification(receiver, notification.push_message(), deep_link=deep_link)

    if notification_setting.allow_email and yak_settings.ALLOW_EMAIL and receiver.email:
        from .utils import send_email_notification
        send_email_notification(receiver, notification.email_message(), reply_to=reply_to) class PushwooshToken(CoreModel):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name="pushwoosh_tokens", on_delete=models.CASCADE)
    token = models.CharField(max_length=255) class NotificationType(CachingMixin, CoreModel):
    name = models.CharField(max_length=32)
    slug = models.SlugField(unique=True)
    description = models.CharField(max_length=255)
    is_active = models.BooleanField(default=True)

    objects = CachingManager()

    def __unicode__(self):
        return "{}".format(self.name) class NotificationSettingSerializer(serializers.ModelSerializer):
    notification_type = NotificationTypeSerializer(read_only=True)
    # This allows us to pass `id` for bulk updates
    id = serializers.IntegerField(label='ID', read_only=False, required=False)

    class Meta:
        model = NotificationSetting
        fields = ('id', 'notification_type', 'allow_push', 'allow_email')
        list_serializer_class = NotificationSettingListSerializer class NotificationSerializer(serializers.ModelSerializer):
    message = serializers.SerializerMethodField()
    reporter = UserSerializer(read_only=True)
    content_object = serializers.SerializerMethodField()

    class Meta:
        model = Notification
        fields = ('created', 'name', 'message', 'reporter', 'content_object')

    def get_message(self, obj):
        return obj.message(Notification.PUSH)

    def get_content_object(self, obj):
        serializer_class = yak_settings.SERIALIZER_MAPPING[type(obj.content_object)]
        serializer = serializer_class(instance=obj.content_object, context=self.context)
        return serializer.data

    def to_representation(self, instance):
        ret = super(NotificationSerializer, self).to_representation(instance)
        ret[instance.content_object._meta.model_name] = ret.pop('content_object')
        return ret class PushwooshTokenSerializer(serializers.ModelSerializer):
    user = UserSerializer(read_only=True, default=serializers.CurrentUserDefault())
    hwid = serializers.CharField(write_only=True)
    language = serializers.CharField(write_only=True)
    platform = serializers.CharField(write_only=True)

    class Meta:
        model = PushwooshToken
        fields = '__all__' class CommentViewSet(viewsets.ModelViewSet):
    queryset = Comment.objects.all()
    serializer_class = CommentSerializer
    filter_fields = ('content_type', 'object_id')

    def perform_create(self, serializer):
        serializer.save(user=self.request.user)

    def perform_update(self, serializer):
        serializer.save(user=self.request.user) class FollowViewSet(viewsets.ModelViewSet):
    queryset = Follow.objects.all()
    serializer_class = FollowSerializer

    def perform_create(self, serializer):
        serializer.save(user=self.request.user)

    def perform_update(self, serializer):
        serializer.save(user=self.request.user)

    @list_route(methods=['post'])
    def bulk_create(self, request):
        serializer = self.get_serializer(data=request.data, many=True)
        if serializer.is_valid():
            serializer.save(user=self.request.user)
            return Response(serializer.data, status=status.HTTP_201_CREATED)

        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) class ShareViewSet(viewsets.ModelViewSet):
    queryset = Share.objects.all()
    serializer_class = ShareSerializer

    def perform_create(self, serializer):
        serializer.save(user=self.request.user)

    def perform_update(self, serializer):
        serializer.save(user=self.request.user) class LikeViewSet(viewsets.ModelViewSet):
    queryset = Like.objects.all()
    serializer_class = LikeSerializer

    def perform_create(self, serializer):
        serializer.save(user=self.request.user)

    def perform_update(self, serializer):
        serializer.save(user=self.request.user) def perform_import(val, setting_name):
    def __getattr__(self, attr):
class YAKAPISettings(APISettings):
USER_SETTINGS = getattr(settings, 'YAK', None)
DEFAULTS = {
    'USER_APP_LABEL': 'test_app',
    'USER_MODEL': 'user',
    'USER_SERIALIZER': "test_app.api.serializers.UserSerializer",
    'API_SCHEMA': 'api-schema-1.0.json',
    'SOCIAL_MODEL': "test_app.models.Post",
    'ALLOW_EMAIL': True,
    'ALLOW_PUSH': True,
    'EMAIL_NOTIFICATION_SUBJECT': 'Test Project Notification',
    'PUSHWOOSH_AUTH_TOKEN': "",
    'PUSHWOOSH_APP_CODE': "",
    'PUSH_NOTIFICATION_HANDLER': "yak.rest_notifications.utils.send_pushwoosh_notification",
    'SOCIAL_SHARE_DELAY': 60,
    'USE_FACEBOOK_OG': False,
    'FACEBOOK_OG_NAMESPACE': "",
    'SERIALIZER_MAPPING': {},
    'FLAKE8_CONFIG': None,
}
IMPORT_STRINGS = (
    "USER_SERIALIZER",
    "SOCIAL_MODEL",
    "SERIALIZER_MAPPING",
)
from rest_framework import mixins, generics, status
from rest_framework.exceptions import AuthenticationFailed
from rest_framework.response import Response
from rest_framework.viewsets import GenericViewSet
from pypushwoosh import client, constants
from pypushwoosh.command import RegisterDeviceCommand
from yak.rest_core.permissions import IsOwner
from yak.rest_notifications.models import NotificationSetting, Notification, create_notification, \
    PushwooshToken, NotificationType
from yak.rest_notifications.serializers import NotificationSettingSerializer, NotificationSerializer, \
    PushwooshTokenSerializer
from yak.rest_social_network.views import CommentViewSet, FollowViewSet, ShareViewSet, LikeViewSet
from yak.settings import yak_settings


class PushwooshTokenView(generics.CreateAPIView):
    queryset = PushwooshToken.objects.all()
    serializer_class = PushwooshTokenSerializer
    permission_classes = (IsOwner,)

    def perform_create(self, serializer):
        hwid = serializer.validated_data.pop("hwid")
        language = serializer.validated_data.pop("language")
        platform = serializer.validated_data.pop("platform")

        platform_code = constants.PLATFORM_IOS
        if platform == 'android':
            platform_code = constants.PLATFORM_ANDROID

        push_client = client.PushwooshClient()
        command = RegisterDeviceCommand(yak_settings.PUSHWOOSH_APP_CODE, hwid, platform_code,
                                        serializer.validated_data["token"], language)
        response = push_client.invoke(command)

        if response["status_code"] != 200:
            raise AuthenticationFailed("Authentication with notification service failed")

        serializer.save(user=self.request.user)


class NotificationSettingViewSet(mixins.UpdateModelMixin,
                                 mixins.ListModelMixin,
                                 GenericViewSet):
    queryset = NotificationSetting.objects.all()
    serializer_class = NotificationSettingSerializer
    permission_classes = (IsOwner,)

    def perform_update(self, serializer):
        serializer.save(user=self.request.user)

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

    def put(self, request, *args, **kwargs):
        queryset = self.get_queryset()
        serializer = self.get_serializer(queryset, data=request.data, many=True, partial=True)

        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response(serializer.data, status=status.HTTP_200_OK)


class NotificationView(generics.ListAPIView):
    queryset = Notification.objects.all()
    serializer_class = NotificationSerializer
    permission_classes = (IsOwner,)

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


class NotificationCommentViewSet(CommentViewSet):
    def perform_create(self, serializer):
        obj = serializer.save(user=self.request.user)
        notification_type = NotificationType.objects.get(slug="comment")
        create_notification(obj.content_object.user, obj.user, obj.content_object, notification_type)


class NotificationFollowViewSet(FollowViewSet):
    def perform_create(self, serializer):
        obj = serializer.save(user=self.request.user)
        notification_type = NotificationType.objects.get(slug="follow")
        create_notification(obj.content_object, obj.user, obj.content_object, notification_type)


class NotificationShareViewSet(ShareViewSet):
    def perform_create(self, serializer):
        obj = serializer.save(user=self.request.user)
        notification_type = NotificationType.objects.get(slug="share")
        for receiver in obj.shared_with.all():

            create_notification(receiver, obj.user, obj.content_object, notification_type)