Skip to content

Latest commit

 

History

History

README.md

BaseApp Notifications

Reusable app to have in-app notifications integrated with email and push notifications. Its built on top of django-notifications-hq.

Based on the Activity Streams Spec.

Notifications are actually actions events, which are categorized by four main components.

  • Actor. The object that performed the activity.
  • Verb. The verb phrase that identifies the action of the activity.
  • Action Object. (Optional) The object linked to the action itself.
  • Target. (Optional) The object to which the activity was performed.

Actor, Action Object and Target are GenericForeignKeys to any arbitrary Django object. An action is a description of an action that was performed (Verb) at some instant in time by some Actor on some optional Target that results in an Action Object getting created/updated/deleted.

For example: nossila (actor) opened (verb) pull request 18 (action_object) on baseapp-backend (target) 12 hours ago

Whats missing

  • Finish implementing push notifications
  • DRF views and serializers

How to install:

Install the package with pip install baseapp-backend[notifications].

If you want to develop, install using this other guide.

Setup In-App notifications

1 - Add to your INSTALLED_APPS:

INSTALLED_APPS = [
  "baseapp_notifications",
]

2 - Set the notification and notification settings model in your settings/base.py:

NOTIFICATIONS_NOTIFICATION_MODEL = "notifications.Notification"
BASEAPP_NOTIFICATIONS_NOTIFICATIONSETTING_MODEL = "baseapp_notifications.NotificationSetting"

Check how to customize to your own model bellow.

3 - Make sure to add the task routing for send_push_notification

CELERY_TASK_ROUTES = {
    "baseapp_notifications.tasks.send_push_notification": {
        "exchange": "default",
        "routing_key": "default",
    },
}

4 - Make sure that your main User's DjangoObjectType implements interface NotificationsInterface:

from baseapp_core.graphql import DjangoObjectType, Node as RelayNode
from baseapp_core.plugins import graphql_shared_interfaces

class UserNode(DjangoObjectType):
    class Meta:
        interfaces = graphql_shared_interfaces.get(RelayNode, "NotificationsInterface")

5 - Make sure your graphql.py main file is loading queries, mutations and subscriptions from plugin_registry.

Setup Push notifications (apple and google)

1 - Add to your INSTALLED_APPS:

INSTALLED_APPS = [
  "push_notifications"
]

2 - Set the push notifications settings in your settings/base.py:

PUSH_NOTIFICATIONS_SETTINGS = {
  "FCM_API_KEY": "[your api key]",
  "APNS_CERTIFICATE": ["Absolute path to your APNS certificate file"],
  "APNS_TOPIC": ["Apple app ID"],
  "DEFAULT_CLOUD_MESSAGE_TYPE": "FCM",
  "UPDATE_ON_DUPLICATE_REG_ID": True/False,
  "APNS_USE_SANDBOX": True/False,
}

You can get more details about this settings dict at the django-push-notifications oficial repository.

3 - Make sure your urls.py main file loads routes from the plugin_registry. The following push-notifications endpoints will be loaded automatically when push_notifications is installed:

  • "push-notifications/apns": push_notifications.api.rest_framework.APNSDeviceAuthorizedViewSet
  • "push-notifications/gcm": push_notifications.api.rest_framework.GCMDeviceAuthorizedViewSet
  • "push-notifications/wns": push_notifications.api.rest_framework.WNSDeviceAuthorizedViewSet
  • "push-notifications/web": push_notifications.api.rest_framework.WebPushDeviceAuthorizedViewSet

How to send a notification

from baseapp_core.plugins import shared_services

if service := shared_services.get("notifications"):
    service.send_notification(
        add_to_history=True,
        send_push=True,
        send_email=True,
        sender=user,
        recipient=user,
        verb="opened",
        action_object=pr,
        target=repository,
        level="info",
        description=_("{user_name} opened a pull request in {repository_name}").format(
          user_name=user.name,
          repository_name=repository.name
        ),
        push_title=_("title"),
        push_description=_("description"),
        extra={},
    )

Arguments:

  • add_to_history: A boolean (default=True). True to add this to your in-app notifications history.
  • send_push: A boolean (default=True). True to send push notification to user's devices.
  • send_email: A boolean (default=True). True to send notification via email.
  • sender: An object of any type. (Required)
  • recipient: A Group or a User QuerySet or a list of User. (Required)
  • verb: An string. (Required)
  • action_object: An object of any type. (Optional)
  • target: An object of any type. (Optional)
  • level: One of Notification.LEVELS ('success', 'info', 'warning', 'error') (default=info). (Optional)
  • description: An string. (Optional)
  • notification_url: URL used in emails so users can open this notification's page. (Optional)
  • email_subject: An string (default=description). This will override the email's subject message. (Optional)
  • email_message: An string (default=description). This will override the email's body message. (Optional)
  • public: An boolean (default=True). (Optional)
  • timestamp: An tzinfo (default=timezone.now()). (Optional)
  • push_description: A string. (Required). The "body" of your push notification
  • push_title: A string. (Optional). This will override the Title of your notification (apple and google uses the name defined on your build files)
  • extra: A dict with data of any type. (Optional)

Extra data: ou can also send any arbitrary kwargs and they will be added to Notification.data JSONField.

Email notifications

To send email notifications make sure to set send_email=True argument and notification_url so users can open the notification in the browser. The description will be used both as email's subject and email's body by default, check how to customize bellow.

from baseapp_core.plugins import shared_services

if service := shared_services.get("notifications"):
    service.send_notification(
        sender=user,
        recipient=user,
        verb="opened",
        description="email's subject",
        add_to_history=True,
        send_email=True,
        notification_url="https://github.com/silverlogic/baseapp-backend/pull/18",
    )

How to customize email notification templates

By default it will look for templates based in your slugify(verb), so can implement the following files with the following content in your project for each verb you want to customize:

Create templates/emails/notifications/{verb_slugified}-subject.txt.j2 with:

{% include "emails/notification-subject.txt.j2" %}

Create templates/emails/notifications/{verb_slugified}-body.txt.j2 with:

{% extends "emails/notification-body.txt.j2" %}

Create templates/emails/notifications/{verb_slugified}-body.html.j2 with:

{% extends "emails/notification-body.html.j2" %}

You can copy the extended templates over to your project to customize them as well.

Also check their source code in this repository to understand how you can customize the notification message for each notification's verb, for example, to customize the email notification message (defaults to what you passed to description):

Example

{# templates/emails/notifications/opened-body.html.j2 #}
{% extends "emails/notification-body.html.j2" %}

{% block notification_message %}
    {{ recipient.name }}'s custom notification message.
{% endblock %}

Public API documentation

In you GraphQL schema it will expose the following queries, mutations and subscriptions. Please check your GraphiQL playground for better understanding

Queries

query {
  me {
    notificationsUnreadCount
    notifications
  }
}

Mutations

mutation {
  notificationsMarkAllAsRead(input: { read: true }) {
    recipient { notificationsUnreadCount }
  }

  notificationsMarkAsRead(input: { notificationIds: ["b3b6c8e2-8f2a-4e3a-9c1d-2f7e6a1b2c3d", "a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d"], read: true }) {
    recipient { notificationsUnreadCount }
  }
}

Subscriptions

subscription {
  onNotificationChange {
    createdNotification @prependEdge(connections: $connections) {
      node {
        ...NotificationCardFragment

        recipient {
          id
          notificationsUnreadCount
        }
      }
    }

    updatedNotification {
      ...NotificationCardFragment
    }

    deletedNotificationId @deleteRecord
  }
}

How to develop

General development instructions can be found in main README.