Coder Social home page Coder Social logo

universal_notifications's Introduction

universal_notifications

travis pypi codecov

High-level framework for notifications

This project is intended to provide a convenient way to send notifications using multiple notification backends (e.g., e-mail, SMS, push).


Setting up

To start using universal_notifications please add universal_notifications to INSTALLED_APPS in your Django project, and then migrate the app: ./manage.py migrate universal_notifications.

If you intend to use any other type of notification than WS, then UNIVERSAL_NOTIFICATIONS_CATEGORIES must be defined (see Unsubscriber)

Basic usage

WebSocket notifications

To have Universal Notifications receive WS notifications (ie. to mark notification as received) add to your settings.py:

WS4REDIS_SUBSCRIBER = 'universal_notifications.backends.websockets.RedisSignalSubscriber'

Upon receiving a WS, "ws_received" signal will be emitted with json data received in the message, and all emails subscribed to that channel. Sample usage:

from universal_notifications.signals import ws_received

def your_handler(sender, message_data, channel_emails, **kwargs):
    pass
ws_received.connect(your_handler)

Simple example of using WS notifications:

class OrderShippedWS(WSNotification):
    message = 'order_shipped'
    serializer_class = OrderSerializer

# ... somewhere in a view
OrderShippedWS(item=order, receivers=[user], context={}).send()

E-mail notifications

class OrderShippedEmail(EmailNotification):
    email_name = 'order_shipped'
    email_subject = _('Order no. {{item.pk}} has been shipped.')
    categories = ["newsletter"]
    sendgrid_asm = {
        "group_id": 1
    }
    use_premailer = False  # disable Premailer for this email

# ... somewhere in a view
OrderShippedEmail(item=order, receivers=[user], context={}, attachments=[
    ("invoice.pdf", open("invoice.pdf").read(), "application/pdf")
]).send()

Attachements parameter has to be a list of (filename, content, mime_type) triples. categories, sendgrid_asm, use_premailer fields are optional, they can be used with django-sendgrid to enable metrics by category and unsubscribe groups.

Email subject will be taken from the <title></title> tags in the template if it is not set in notification class.

Settings
  • UNIVERSAL_NOTIFICATIONS_IS_SECURE (bool, default: False) - set https protocol and is_secure variable
  • UNIVERSAL_NOTIFICATIONS_USE_PREMAILER (bool, default: True) - use premailer to append CSS styles inline (speedup tests a lot when False)

SMS notifications

Supported platforms:
Settings
  • UNIVERSAL_NOTIFICATIONS_SMS_ENGINE - set engine
  • UNIVERSAL_NOTIFICATIONS_VALIDATE_MOBILE (bool)
  • UNIVERSAL_NOTIFICATIONS_SMS_SEND_IN_TASK (bool, default True)
Engine settinsgs:
  • Twilio
    • UNIVERSAL_NOTIFICATIONS_TWILIO_API_ENABLED (bool)
    • UNIVERSAL_NOTIFICATIONS_TWILIO_ENABLE_PROXY (bool)
    • UNIVERSAL_NOTIFICATIONS_TWILIO_ACCOUNT (string)
    • UNIVERSAL_NOTIFICATIONS_TWILIO_TOKEN (string)
    • UNIVERSAL_NOTIFICATIONS_TWILIO_REPORT_ERRORS (list of integers)
  • Amazon SNS
    • UNIVERSAL_NOTIFICATIONS_AMAZON_SNS_API_ENABLED (bool)
    • AWS_ACCESS_KEY_ID (string)
    • AWS_SECRET_ACCESS_KEY (string)
    • AWS_DEFAULT_REGION (string) - default us-east-1

Simple example of use:

class OrderShippedSMS(SMSNotification):
    message = _('{{receiver.first_name}}, order no. {{item.pk}} has been shipped.')

    def prepare_receivers(self):
        return {x.shipping_address.phone for x in self.receivers}

class SyncOrderShippedSMS(OrderShippedSMS):
    send_async = False  # by default taken from UNIVERSAL_NOTIFICATIONS_SMS_SEND_IN_TASK

# ... somewhere in a view
OrderShippedSMS(item=order, receivers=[user], context={}).send(

Push notifications

First of all, to use push notifications, you must provide a list of available devices linked to users. For more information, please check out sources.

Supported platforms:
  • FCM - Android, iOS, Web
  • GCM - Android, iOS, Web
  • APNS - iOS
To make push notifications work on all supported platforms, a few properties need to be set:
  • UNIVERSAL_NOTIFICATIONS_MOBILE_APPS[app_id]
    • APNS_CERTIFICATE - APNS certificate file (.pem)
    • FCM_API_KEY - Firebase API key
    • GCM_API_KEY - Google Cloud Messaging API key
  • GCM_POST_URL - Google Cloud Messaging post url
Settings related to Apple Push Notification service:
  • APNS_HOST
  • APNS_PORT
  • APNS_FEEDBACK_HOST
  • APNS_FEEDBACK_PORT
  • APNS_ERROR_TIMEOUT
  • APNS_MAX_NOTIFICATION_SIZE

Simple example of use:

class OrderShippedPush(PushNotification):
    title = _('Order no. {{item.pk}} has been shipped.')
    description = _('This can also use {{item.pk}}')  # optional

# ... somewhere in a view
OrderShippedPush(item=order, receivers=[user], context={}).send()

Unsubscriber

This section refers to all notifications except WebSockets, which by default are not prone to unsubscriptions (however this can be changed by setting check_subscription to True).

Each category for each type must be explicitly declared in config (with label). If it is not there, exception will be raised on attempt to send such notification. This requirement is to prevent situation, that notification of given type is send to user who would not wish to receive it, but cannot unsubscribe from it (since it is not present in the config).

Since categories can be changed with configuration, labels should be specified for them, since they can't be hardcoded in client's app.

There is one special category: "system". This category should not be declared in configuration, and notification with such category will always pass.

Sample configuration:

UNIVERSAL_NOTIFICATIONS_CATEGORIES={
    "push": {
        "default": _("This is a label for default category you'll send to FE"),
        "chat": _('Category for chat messages'),
        "promotions": _('Promotions',)
    },
    "email": {
        "default": _("This is a label for default category you'll send to FE"),
        "chat": _('Category for chat messages'),
        "newsletter": _('Newsletter',)
    },
    "sms": {
        "default": _("This is a label for default category you'll send to FE"),
        "chat": _('Category for chat messages'),
        "newsletter": _('Newsletter',)
    },
    "test": {
        "default": _("This is a label for default category you'll send to FE"),
    },
},

If you want to allow different types of users to have different categories of notifications, you can do it with configuration:

# not required. If defined, specific types of users will only get notifications from allowed categories.
# requires a bit more configuration - helper function to check if notification category is allowed for user
UNIVERSAL_NOTIFICATIONS_USER_CATEGORIES_MAPPING={
    "for_admin": {
        "push": ["default", "chat", "promotions"],
        "email": ["default", "chat", "newsletter"],
        "sms": ["default", "chat", "newsletter"]
    },
    "for_user": {
        "push": ["default", "chat", "promotions"],
        "email": ["default", "newsletter"],  # chat skipped
        "sms": ["default", "chat", "newsletter"]
    }
},
# path to the file we will import user definitions for UNIVERSAL_NOTIFICATIONS_USER_CATEGORIES_MAPPING
UNIVERSAL_NOTIFICATIONS_USER_DEFINITIONS_FILE='tests.user_conf'

# from file: tests/user_conf.py
def for_admin(user):
    return user.is_superuser

def for_user(user):
    return not user.is_superuser

In the example above, functions "for_admin" & "for_user" should be defined in file tests/user_conf.py. Each function takes user as a parameter, and should return either True or False.

If given notification type is not present for given user, user will neither be able to receive it nor unsubscribe it.

Unsubscriber API

The current subscriptions can be obtained with a API described below. Please note, that API does not provide label for "unsubscribe_from_all", since is always present and can be hardcoded in FE module. Categories however may vary, that's why labels for them must be returned from BE.

# GET /subscriptions

return {
    "unsubscribe_from_all": bool,  # False by default
    "each_type_for_given_user": {
        "each_category_for_given_type_for_given_user": bool,  # True(default) if subscribed, False if unsubscribed
        "unsubscribe_from_all": bool  # False by default
    }
    "labels": {
        "each_type_for_given_user": {
            "each_category_for_given_type_for_given_user": string,
        }
    }
}

Unsubscriptions may be edited using following API:

# PUT /subscriptions

data = {
    "unsubscribe_from_all": bool,  # False by default
    "each_type_for_given_user": {
        "each_category_for_given_type_for_given_user": bool,  # True(default) if subscribed, False if unsubscribed
        "unsubscribe_from_all": bool  # False by default
    }
}

Please note, that if any type/category for type is ommited, it is reseted to default value.

FakeEmailSend view

universal_notifications.backends.emails.views.FakeEmailSend is a view that helps testing email templates. To start using it, add url(r'^emails/', include('universal_notifications.backends.emails.urls')) to your urls.py, and specify receiver email address using UNIVERSAL_NOTIFICATIONS_FAKE_EMAIL_TO.

After that you can make a request to the new url with template parameter, for instance: http://localhost:8000/emails/?template=reset_password, which will send an email using emails/email_reset_password.html as the template.

Notification history

By default all notifications that have been sent are stored in the NotificationHistory object in the database, but this behavior can be changed, and therefore the database will not be used to store notification history (but you will still receive notification history in your app log, on the info level).

To disable using database, set UNIVERSAL_NOTIFICATIONS_HISTORY_USE_DATABASE to False (default: True), and to disable any history tracking, set UNIVERSAL_NOTIFICATIONS_HISTORY to False (default: True).

universal_notifications's People

Contributors

pkrzyzaniak avatar poxip avatar remik avatar jacoor avatar wasimafser avatar lukaszb avatar marcinkaszynski avatar xiv avatar dependabot-preview[bot] avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.