Skip to content

Commit

Permalink
Merge pull request #1259 from eciis/notification-body-structure
Browse files Browse the repository at this point in the history
Notification body structure
  • Loading branch information
Luiz-FS authored Oct 15, 2018
2 parents 337b58d + 93f5aff commit 3dc8756
Show file tree
Hide file tree
Showing 23 changed files with 605 additions and 71 deletions.
45 changes: 30 additions & 15 deletions backend/fcm.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

import json

from utils import validate_object

FIREBASE_TOKENS_ENDPOINT = "%s/pushNotifications.json" % FIREBASE_URL

ICON_URL = "https://firebasestorage.googleapis.com/v0/b/eciis-splab.appspot.com/o/images%2FLOGO-E-CIS-1510941864112?alt=media&token=ca197614-ad60-408e-b21e-0ebe258c4a80"
Expand All @@ -17,50 +19,63 @@
push_service = FCMNotification(api_key=SERVER_KEY)


def notify_single_user(title, body, user_key):
def notify_single_user(data, user_key):
"""Notify a single user.
It sends a notification to each user's device.
Args:
title: A string that represents the notification's title.
body: The body message of the notification, the information
you want pass to the users.
data: An object that has the title, body and click_action
as properties. title is a string that represents the
notification's title. body is the body message of the notification,
the information you want pass to the users. click_action is the url
that the user is gonna be redirected when he click on the notification.
user_key: user's urlsafe key.
"""
tokens = get_single_user_tokens(user_key)
send_push_notifications(title, body, tokens)
send_push_notifications(data, tokens)


def notify_multiple_users(title, body, user_keys):
def notify_multiple_users(data, user_keys):
"""Notify multiple users.
This function receives a list of user_keys
and use it to retrieve the tokens.
Args:
title: A string that represents the notification's title.
body: The body message of the notification, the information
you want pass to the users.
data: An object that has the title, body and click_action
as properties. title is a string that represents the
notification's title. body is the body message of the notification,
the information you want pass to the users. click_action is the url
that the user is gonna be redirected when he click on the notification.
user_keys: A list with all users' urlsafe keys that will
receive the notification.
"""
tokens = get_multiple_user_tokens(user_keys)
send_push_notifications(title, body, tokens)
send_push_notifications(data, tokens)


def send_push_notifications(title, body, tokens):
def send_push_notifications(data, tokens):
"""It wraps the call to pyfcm notify function.
Args:
title: A string that represents the notification's title.
body: The body message of the notification, the information
you want pass to the users.
data: An object that has the title, body and click_action
as properties. title is a string that represents the
notification's title. body is the body message of the notification,
the information you want pass to the users. click_action is the url
that the user is gonna be redirected when he click on the notification.
tokens: The devices' tokens that will receive
the notification.
"""
validate_object(data, ['title', 'body', 'click_action'])

title = data['title']
body = data['body']
click_action = data['click_action']

if tokens:
result = push_service.notify_multiple_devices(
registration_ids=tokens, message_title=title,
message_body=body, message_icon=ICON_URL
message_body=body, message_icon=ICON_URL,
click_action=click_action
)
return result

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,25 @@
from models import Institution
from models import InviteFactory
from models import RequestInstitutionChildren
from service_entities import enqueue_task
from push_notification import NotificationType

__all__ = ['InstitutionChildrenRequestCollectionHandler']


def enqueue_push_notification(requested_inst_key):
"""Get the necessary parameters and insert
a new push notification in the queue.
"""
requested_inst = requested_inst_key.get()
receiver = requested_inst.admin.urlsafe()

enqueue_task('send-push-notification', {
'type': NotificationType.link.value,
'receivers': [receiver],
'entity': requested_inst_key.urlsafe()
})

class InstitutionChildrenRequestCollectionHandler(BaseHandler):
"""Institution Children Request Collection Handler."""

Expand Down Expand Up @@ -72,4 +88,6 @@ def post(self, user, institution_urlsafe):

request.send_invite(host, user.current_institution)

enqueue_push_notification(requested_inst_key)

self.response.write(json.dumps(request.make()))
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
from util import Notification, NotificationsQueueManager
from service_entities import enqueue_task
from service_messages import create_message

from service_entities import enqueue_task
from push_notification import NotificationType

__all__ = ['InstitutionParentRequestCollectionHandler']

Expand All @@ -40,6 +41,20 @@ def remake_link(request, requested_inst_key, child_institution, user):

request.change_status('accepted')


def enqueue_push_notification(requested_inst_key):
"""Get the necessary parameters and insert
a new push notification in the queue.
"""
requested_inst = requested_inst_key.get()
receiver = requested_inst.admin.urlsafe()

enqueue_task('send-push-notification', {
'type': NotificationType.link.value,
'receivers': [receiver],
'entity': requested_inst_key.urlsafe()
})

class InstitutionParentRequestCollectionHandler(BaseHandler):
"""Institution Parent Collectcion Request Handler."""

Expand Down Expand Up @@ -125,4 +140,6 @@ def main_operations(request, requested_inst_key, child_institution, user, host):

request = main_operations(request, requested_inst_key, child_institution, user, host)

enqueue_push_notification(requested_inst_key)

self.response.write(json.dumps(request.make()))
14 changes: 11 additions & 3 deletions backend/handlers/invite_user_collection_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,16 @@ def process_invites(emails, invite, current_institution_key):
else:
invite = createInvite(invite)
invites.append({'email': invite.invitee, 'key': invite.key.urlsafe()})
enqueue_task('send-invite', {'invites_keys': json.dumps([invite.key.urlsafe()]), 'host': host,
'current_institution': user.current_institution.urlsafe()})
enqueue_task('send-invite', {
'invites_keys': json.dumps([invite.key.urlsafe()]),
'host': host,
'current_institution': user.current_institution.urlsafe()
})

enqueue_task('send-push-notification', {
'type': type_of_invite,
'invites': json.dumps(map(lambda invite: invite['key'], invites))
})

self.response.write(json.dumps(
{'msg': 'The invites are being processed.', 'invites' : invites}))
Expand All @@ -97,4 +105,4 @@ def createInvite(data):
invite = InviteFactory.create(data, data['type_of_invite'])
invite.put()

return invite
return invite
8 changes: 8 additions & 0 deletions backend/handlers/like_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,14 @@ def post(self, user, post_key, comment_id=None, reply_id=None):

enqueue_task('post-notification', params)

is_first_like = post.get_number_of_likes() == 1
if is_first_like:
enqueue_task('send-push-notification', {
'type': entity_type,
'receivers': [subscriber.urlsafe() for subscriber in post.subscribers],
'entity': post.key.urlsafe()
})

@json_response
@login_required
def delete(self, user, post_key, comment_id=None, reply_id=None):
Expand Down
8 changes: 8 additions & 0 deletions backend/handlers/post_comment_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,14 @@ def post(self, user, post_key):
}
enqueue_task('post-notification', params)

is_first_comment = post.get_number_of_comment() == 1
if is_first_comment:
enqueue_task('send-push-notification', {
'type': entity_type,
'receivers': [subscriber.urlsafe() for subscriber in post.subscribers],
'entity': post.key.urlsafe()
})

self.response.write(json.dumps(Utils.toJson(comment)))

@json_response
Expand Down
5 changes: 2 additions & 3 deletions backend/models/post.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,9 +259,8 @@ def get_number_of_comment(self):
@ndb.transactional(retries=10)
def add_comment(self, comment):
"""Add a comment to the post."""
post = self.key.get()
post.comments[comment.id] = Utils.toJson(comment)
post.put()
self.comments[comment.id] = Utils.toJson(comment)
self.put()

def remove_comment(self, comment):
"""Remove a commet from post."""
Expand Down
10 changes: 10 additions & 0 deletions backend/push_notification/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
"""Initialize push_notification module."""

from .push_notification_service import *
from .send_push_notification_worker_handler import *

notifications = [
push_notification_service, send_push_notification_worker_handler
]

__all__ = [prop for notification in notifications for prop in notification.__all__]
136 changes: 136 additions & 0 deletions backend/push_notification/push_notification_service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# -*- coding: utf-8 -*-
"""Push Notification Service."""
from custom_exceptions import EntityException
from enum import Enum

__all__ = ['get_notification_props', 'NotificationType']

def get_notification_props(_type, entity=None):
"""This function represents the interface
the service's provides to the application
to get the notification properties.
Args:
_type -- the notification's type. type: NotificationType
entity -- an optional parameter that can be
used to determine the click_action property
"""
notification = NotificationProperties(_type, entity)
return notification.get_props()

class NotificationType(Enum):
"""This Enum wraps the
possible notification's type
to make them more maintable
"""
like = 'LIKE_POST'
comment = 'COMMENT'
invite_user = 'USER'
invite_user_adm = 'USER_ADM'
link = 'LINK'

class NotificationProperties(object):
"""This class has several private
methods, each one for an especific
notification's type. These methods
return an object with the notification
properties.
To access them, the instance is initialized
with a notification_method which is set based on
the _type property received by the constructor,
this method is called in get_props, the unique
public method.
"""

def __init__(self, _type, entity):
"""Set the notification_method based on the _type.
types object helps this operation by maping a notification's
type to its especific method.
The entity, also, is set here.
"""
types = {
NotificationType.like: self.__get_like_props,
NotificationType.comment: self.__get_comment_props,
NotificationType.invite_user: self.__get_invite_user_props,
NotificationType.invite_user_adm: self.__get_invite_user_adm_props,
NotificationType.link: self.__get_link_props
}
self.entity = entity
self.notification_method = types[_type]

def get_props(self):
"""Just returns the result of
notification_method().
"""
return self.notification_method()

def __get_like_props(self):
"""Responsible for return the right
properties for the like notification.
self.entity can't be None once it is
used to set the url of the click_action property.
"""
if not self.entity:
raise EntityException(
'A LIKE_POST notification requires the entity.')

url = '/posts/%s' % self.entity.key.urlsafe()

return {
'title': 'Publicação curtida',
'body': 'Uma publicação de seu interesse foi curtida',
'click_action': url
}

def __get_comment_props(self):
"""Responsible for return the right
properties for the comment notification.
self.entity can't be None once it is
used to set the url of the click_action property.
"""
if not self.entity:
raise EntityException(
'A COMMENT notification requires the entity.')
url = "/posts/%s" % self.entity.key.urlsafe()

return {
'title': 'Publicação comentada',
'body': 'Uma publicação do seu interesse foi comentada',
'click_action': url
}

def __get_invite_user_props(self):
"""Responsible for return the right
properties for the invite_user notification.
"""
url = "/notifications"

return {
'title': 'Novo convite',
'body': 'Você recebeu um novo convite para ser membro de uma instituição',
'click_action': url
}

def __get_invite_user_adm_props(self):
"""Responsible for return the right
properties for the invite_user_adm notification.
"""
url = "/notifications"

return {
'title': 'Novo convite',
'body': 'Você recebeu um novo convite para ser administrador de uma instituição',
'click_action': url
}

def __get_link_props(self):
"""Responsible for return the right
properties for the link notification.
"""
url = "/institution/%s/inviteInstitution" % self.entity.key.urlsafe()

return {
'title': 'Solicitação de vínculo',
'body': 'Uma instituição que você administra recebeu uma nova solicitação de vínculo',
'click_action': url
}
Loading

0 comments on commit 3dc8756

Please sign in to comment.