{"id":22371387,"url":"https://github.com/manukumarnm/openwisp-notification","last_synced_at":"2025-07-14T00:04:22.721Z","repository":{"id":108754093,"uuid":"505698607","full_name":"ManukumarNM/openwisp-notification","owner":"ManukumarNM","description":null,"archived":false,"fork":false,"pushed_at":"2022-06-21T05:14:11.000Z","size":153,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-26T16:53:11.171Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ManukumarNM.png","metadata":{"files":{"readme":"README.rst","changelog":"CHANGES.rst","contributing":"CONTRIBUTING.rst","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-06-21T05:12:17.000Z","updated_at":"2022-06-21T05:14:15.000Z","dependencies_parsed_at":"2023-06-04T19:00:53.536Z","dependency_job_id":null,"html_url":"https://github.com/ManukumarNM/openwisp-notification","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ManukumarNM/openwisp-notification","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ManukumarNM%2Fopenwisp-notification","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ManukumarNM%2Fopenwisp-notification/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ManukumarNM%2Fopenwisp-notification/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ManukumarNM%2Fopenwisp-notification/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ManukumarNM","download_url":"https://codeload.github.com/ManukumarNM/openwisp-notification/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ManukumarNM%2Fopenwisp-notification/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265224095,"owners_count":23730336,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-12-04T20:19:50.426Z","updated_at":"2025-07-14T00:04:22.688Z","avatar_url":"https://github.com/ManukumarNM.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"**********************\nOpenWISP Notifications\n**********************\n\n.. image:: https://github.com/openwisp/openwisp-notifications/workflows/OpenWISP%20CI%20Build/badge.svg?branch=master\n   :target: https://github.com/openwisp/openwisp-notifications/actions?query=workflow%3A%22OpenWISP+CI+Build%22\n   :alt: CI build status\n\n.. image:: https://coveralls.io/repos/github/openwisp/openwisp-notifications/badge.svg?branch=master\n   :target: https://coveralls.io/github/openwisp/openwisp-notifications?branch=master\n   :alt: Test Coverage\n\n.. image:: https://requires.io/github/openwisp/openwisp-notifications/requirements.svg?branch=master\n   :target: https://requires.io/github/openwisp/openwisp-notifications/requirements/?branch=master\n   :alt: Requirements Status\n\n.. image:: https://img.shields.io/gitter/room/nwjs/nw.js.svg\n   :target: https://gitter.im/openwisp/general\n   :alt: chat\n\n.. image:: https://badge.fury.io/py/openwisp-notifications.svg\n   :target: http://badge.fury.io/py/openwisp-notifications\n   :alt: Pypi Version\n\n.. image:: https://pepy.tech/badge/openwisp-notifications\n   :target: https://pepy.tech/project/openwisp-notifications\n   :alt: downloads\n\n.. image:: https://img.shields.io/badge/code%20style-black-000000.svg\n   :target: https://pypi.org/project/black/\n   :alt: code style: black\n\n------------\n\n**openwisp-notifications** provides email and web notifications for `OpenWISP \u003chttp://openwisp.org\u003e`_.\n\nIts main goal is to allow the other OpenWISP modules to notify users about\nmeaningful events that happen in their network.\n\n.. figure:: https://drive.google.com/uc?export=view\u0026id=1d603-pemsop1nnJeN49Y04hOcg8aHkoW\n   :align: center\n\n------------\n\n.. contents:: **Table of Contents**:\n   :backlinks: none\n   :depth: 3\n\n------------\n\nAvailable features\n------------------\n\n- `Sending notifications \u003c#sending-notifications\u003e`_\n- `Web notifications \u003c#web-notifications\u003e`_\n- `Email notifications \u003c#email-notifications\u003e`_\n- `Notification types \u003c#notification-types\u003e`_\n- `Registering new notification types \u003c#registering--unregistering-notification-types\u003e`_\n- `User notification preferences \u003c#notification-preferences\u003e`_\n- `Silencing notifications for specific objects temporarily or permanently \u003c#silencing-notifications-for-specific-objects-temporarily-or-permanently\u003e`_\n- `Automatic cleanup of old notifications \u003c#scheduled-deletion-of-notifications\u003e`_\n- `Configurable host for API endpoints \u003c#openwisp_notifications_host\u003e`_\n\nInstall stable version from pypi\n--------------------------------\n\nInstall from pypi:\n\n.. code-block:: shell\n\n    pip install openwisp-notifications\n\nInstall development version\n---------------------------\n\nInstall tarball:\n\n.. code-block:: shell\n\n    pip install https://github.com/openwisp/openwisp-notifications/tarball/master\n\nAlternatively, you can install via pip using git:\n\n.. code-block:: shell\n\n    pip install -e git+git://github.com/openwisp/openwisp-notifications#egg=openwisp_notifications\n\nSetup (integrate into an existing Django project)\n-------------------------------------------------\n\n``INSTALLED_APPS`` in ``settings.py`` should look like the following:\n\n.. code-block:: python\n\n    INSTALLED_APPS = [\n        'django.contrib.auth',\n        'django.contrib.contenttypes',\n        'django.contrib.sessions',\n        'django.contrib.messages',\n        'django.contrib.staticfiles',\n        'django.contrib.sites',\n        'django_extensions',\n        'allauth',\n        'allauth.account',\n        'allauth.socialaccount',\n        # rest framework\n        'rest_framework',\n        'rest_framework.authtoken',\n        'drf_yasg',\n        'django_filters',\n        'openwisp_users',\n        # notifications module\n        'openwisp_notifications',\n        # add openwisp theme\n        # (must be loaded here)\n        'openwisp_utils.admin_theme',\n        'django.contrib.admin',\n        # channels\n        'channels',\n    ]\n\n**Note**: ``openwisp_utils.admin_theme`` and ``django.contrib.admin`` should always\nfollow ``openwisp_notifications`` in ``INSTALLED_APPS`` as shown in the example above.\nIt might result in undesired behavior otherwise, e.g. notification bell not being\nshown on admin site.\n\nAdd ``notification_api_settings`` context processor:\n\n.. code-block:: python\n\n    TEMPLATES = [\n        {\n            # ...\n            'OPTIONS': {\n                # ...\n                'context_processors': [\n                    # ...\n                    'openwisp_notifications.context_processors.notification_api_settings',\n                    # ...\n                ],\n            },\n        },\n    ]\n\n``urls.py``:\n\n.. code-block:: python\n\n    from django.contrib import admin\n    from django.urls import include, path\n    from django.contrib.staticfiles.urls import staticfiles_urlpatterns\n\n    urlpatterns = [\n        path('admin/', admin.site.urls),\n        path('api/v1/', include(('openwisp_users.api.urls', 'users'), namespace='users')),\n        path('', include('openwisp_notifications.urls', namespace='notifications')),\n    ]\n\n    urlpatterns += staticfiles_urlpatterns()\n\nAdd routes for websockets:\n\n.. code-block:: python\n\n    # In yourproject/asgi.py\n    from channels.auth import AuthMiddlewareStack\n    from channels.routing import ProtocolTypeRouter, URLRouter\n    from django.core.asgi import get_asgi_application\n    from openwisp_notifications.websockets.routing import get_routes\n\n    application = ProtocolTypeRouter(\n        {'websocket': AuthMiddlewareStack(URLRouter(get_routes()))}\n    )\n\nConfigure caching (you may use a different cache storage if you want):\n\n.. code-block:: python\n\n    CACHES = {\n        'default': {\n            'BACKEND': 'django_redis.cache.RedisCache',\n            'LOCATION': 'redis://localhost/0',\n            'OPTIONS': {\n                'CLIENT_CLASS': 'django_redis.client.DefaultClient',\n            }\n        }\n    }\n\n    SESSION_ENGINE = 'django.contrib.sessions.backends.cache'\n    SESSION_CACHE_ALIAS = 'default'\n\nConfigure celery:\n\n.. code-block:: python\n\n    # Here we are showing how to configure celery with Redis but you can\n    # use other brokers if you want, consult the celery docs\n    CELERY_BROKER_URL = 'redis://localhost/1'\n\nConfigure celery beat:\n\n.. code-block:: python\n\n    CELERY_BEAT_SCHEDULE = {\n        'delete_old_notifications': {\n            'task': 'openwisp_notifications.tasks.delete_old_notifications',\n            'schedule': timedelta(days=1),\n            'args': (90,),\n        },\n    }\n\n**Note**: You will only need to add ``CELERY_BEAT_SCHEDULE`` setting if you want\nautomatic deletion of old notifications. Please read\n`Scheduled deletion of notifications \u003c#scheduled-deletion-of-notifications\u003e`_\nsection to learn more about this feature.\n\nIf you decide to use redis (as shown in these examples), make sure the python\ndependencies are installed in your system:\n\n.. code-block:: shell\n\n    pip install redis django-redis\n\nConfigure ``ASGI_APPLICATION``:\n\n.. code-block:: python\n\n    ASGI_APPLICATION = 'yourproject.asgi.application'\n\nConfigure channel layers (you may use a `different channel layer \u003chttps://channels.readthedocs.io/en/latest/topics/channel_layers.html#configuration\u003e`_):\n\n.. code-block:: python\n\n    CHANNEL_LAYERS = {\n        'default': {\n            'BACKEND': 'channels_redis.core.RedisChannelLayer',\n            'CONFIG': {\n                'hosts': ['redis://localhost/7'],\n            },\n        },\n    }\n\nWhile development, you can configure it to localhost as shown below:\n\n.. code-block:: python\n\n    INTERNAL_IPS = ['127.0.0.1']\n\nRun migrations\n\n.. code-block:: shell\n\n    ./manage.py migrate\n\n**Note**: Running migrations is also required for creating `notification settings \u003c#notification-preferences\u003e`_\napart from creating database schema.\n\nSending notifications\n---------------------\n\nNotifications can be created using the ``notify`` signal. Eg:\n\n.. code-block:: python\n\n    from django.contrib.auth import get_user_model\n    from swapper import load_model\n\n    from openwisp_notifications.signals import notify\n\n    User = get_user_model()\n    Group = load_model('openwisp_users', 'Group')\n    admin = User.objects.get(email='admin@admin.com')\n    operators = Group.objects.get(name='Operator')\n\n    notify.send(\n        sender=admin,\n        recipient=operators,\n        description=\"Test Notification\",\n        verb=\"Test Notification\",\n        email_subject='Test Email Subject',\n        url='https://localhost:8000/admin',\n    )\n\nThe above code snippet creates and sends a notification to all users belonging to the ``Operators``\ngroup if they have opted-in to receive notifications. Non-superusers receive notifications\nonly for organizations which they are a member of.\n\n**Note**: If recipient is not provided, it defaults to all superusers. If the target is provided, users\nof same organization of the target object are added to the list of recipients given that they have staff\nstatus and opted-in to receive notifications.\n\nThe complete syntax for ``notify`` is:\n\n.. code-block:: python\n\n    notify.send(\n        actor,\n        recipient,\n        verb,\n        action_object,\n        target,\n        level,\n        description,\n        **kwargs\n    )\n\n**Note**: Since ``openwisp-notifications`` uses ``django-notifications`` under the hood, usage of the\n``notify signal`` has been kept unaffected to maintain consistency with ``django-notifications``.\nYou can learn more about accepted parameters from `django-notifications documentation\n\u003chttps://github.com/django-notifications/django-notifications#generating-notifications\u003e`_.\n\nAdditional ``notify`` keyword arguments\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+-------------------+-------------------------------------------------------------------+\n| **Parameter**     | **Description**                                                   |\n+-------------------+-------------------------------------------------------------------+\n| ``email_subject`` | Sets subject of email notification to be sent.                    |\n|                   |                                                                   |\n|                   | Defaults to the notification message.                             |\n+-------------------+-------------------------------------------------------------------+\n| ``url``           | Adds a URL in the email text, eg:                                 |\n|                   |                                                                   |\n|                   | ``For more information see \u003curl\u003e.``                               |\n|                   |                                                                   |\n|                   | Defaults to ``None``, meaning the above message would             |\n|                   | not be added to the email text.                                   |\n+-------------------+-------------------------------------------------------------------+\n| ``type``          | Set values of other parameters based on registered                |\n|                   | `notification types \u003c#notification-types\u003e`_                       |\n|                   |                                                                   |\n|                   | Defaults to ``None`` meaning you need to provide other arguments. |\n+-------------------+-------------------------------------------------------------------+\n\nWeb Notifications\n-----------------\n\n*Openwisp Notifications* send a web notification to the recipients through\ndjango's admin site. Following are the components which allows browsing\nweb notifications:\n\nNotification Widget\n~~~~~~~~~~~~~~~~~~~\n\n.. figure:: https://drive.google.com/uc?export=view\u0026id=1EnjVxp_hjJEmUOTGyaqhd5uYCcyWnRaY\n   :align: center\n\nA javascript widget has been added to make consuming notifications easy for users.\nThe notification widget provides following features:\n\n- A minimalistic UI to help getting things done quickly.\n- Dynamically loading notifications with infinite scrolling to prevent unnecessary\n  network requests.\n- Option to filter unread notifications.\n- Option to mark all notifications as read on a single click.\n\nNotification Toasts\n~~~~~~~~~~~~~~~~~~~\n\n.. figure:: https://drive.google.com/uc?export=view\u0026id=1Lm0caDi3_DdiYzKc02DX3Cn2smj6ptWG\n   :align: center\n\nA notification toast delivers notifications at real-time. This allows\nusers to read notifications without even opening the notification widget.\nA notification bell is also played to alert each time a notification is\ndisplayed through notification toast.\n\nEmail Notifications\n-------------------\n\n.. figure:: https://github.com/openwisp/openwisp-notifications/raw/docs/docs/images/email-template.png\n\nAlong with web notifications *OpenWISP Notification* also sends notifications\nthrough emails.\n\nFollowing notification email is generated by OpenWISP Notification in *OpenWISP Monitoring*:\n\nNotification Cache\n------------------\n\nIn a typical OpenWISP installation, ``actor``, ``action_object`` and ``target`` objects are same\nfor a number of notifications. To optimize database queries, these objects are cached using\n`Django's cache framework \u003chttps://docs.djangoproject.com/en/3.0/topics/cache/\u003e`_.\nThe cached values are updated automatically to reflect actual data from database. You can control\nthe duration of caching these objects using\n`OPENWISP_NOTIFICATIONS_CACHE_TIMEOUT setting \u003c#OPENWISP_NOTIFICATIONS_CACHE_TIMEOUT\u003e`_.\n\nCache invalidation\n~~~~~~~~~~~~~~~~~~\n\nThe function ``register_notification_cache_update`` can be used to register a signal of a model which is being used as an\n``actor``, ``action_object`` and ``target`` objects. As these values are cached for the optimization purpose so their cached\nvalues are need to be changed when they are changed. You can register any signal you want which will delete the cached value.\nTo register a signal you need to include following code in your ``apps.py``.\n\n.. code-block:: python\n\n    from django.db.models.signals import post_save\n    from swapper import load_model\n\n    def ready(self):\n        super().ready()\n\n        # Include lines after this inside\n        # ready function of you app config class\n        from openwisp_notifications.handlers import register_notification_cache_update\n\n        model = load_model('app_name', 'model_name')\n        register_notification_cache_update(model, post_save, dispatch_uid=\"myapp_mymodel_notification_cache_invalidation\")\n\n**Note**: You need to import ``register_notification_cache_update`` inside the ``ready`` function or\nyou can define another funtion to register signals which will be called in ``ready`` and then it will be\nimported in this function. Also ``dispatch_uid`` is unique identifier of a signal. You can pass any\nvalue you want but it needs to be unique. For more details read `preventing duplicate signals section of Django documentation \u003chttps://docs.djangoproject.com/en/dev/topics/signals/#preventing-duplicate-signals\u003e`_\n\nNotification Types\n------------------\n\n**OpenWISP Notifications** simplifies configuring individual notification by\nusing notification types. You can think of a notification type as a template\nfor notifications.\n\nThese properties can be configured for each notification type:\n\n+------------------------+----------------------------------------------------------------+\n| **Property**           | **Description**                                                |\n+------------------------+----------------------------------------------------------------+\n| ``level``              | Sets ``level`` attribute of the notification.                  |\n+------------------------+----------------------------------------------------------------+\n| ``verb``               | Sets ``verb`` attribute of the notification.                   |\n+------------------------+----------------------------------------------------------------+\n| ``verbose_name``       | Sets display name of notification type.                        |\n+------------------------+----------------------------------------------------------------+\n| ``message``            | Sets ``message`` attribute of the notification.                |\n+------------------------+----------------------------------------------------------------+\n| ``email_subject``      | Sets subject of the email notification.                        |\n+------------------------+----------------------------------------------------------------+\n| ``message_template``   | Path to file having template for message of the notification.  |\n+------------------------+----------------------------------------------------------------+\n| ``email_notification`` | Sets preference for email notifications. Defaults to ``True``. |\n+------------------------+----------------------------------------------------------------+\n| ``web_notification``   | Sets preference for web notifications. Defaults to ``True``.   |\n+------------------------+----------------------------------------------------------------+\n\n**Note**: A notification type configuration should contain atleast one of ``message`` or ``message_template``\nsettings. If both of them are present, ``message`` is given preference over ``message_template``.\n\nDefining ``message_template``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nYou can either extend default message template or write your own markdown formatted message template\nfrom scratch. An example to extend default message template is shown below.\n\n.. code-block:: django\n\n    # In templates/your_notifications/your_message_template.md\n    {% extends 'openwisp_notifications/default_message.md' %}\n    {% block body %}\n        [{{ notification.target }}]({{ notification.target_link }}) has malfunctioned.\n    {% endblock body %}\n\n**Note**: You can access all attributes of the notification using ``notification`` variables in your message\ntemplate as shown above. Additional attributes ``actor_link``, ``action_link`` and ``target_link`` are\nalso available for providing hyperlinks to respective object.\n\n**Note**: After writing code for registering or unregistering notification types, it is recommended to run\ndatabase migrations to create `notification settlings \u003c#notification-preferences\u003e`_ for these notification types.\n\nRegistering / Unregistering Notification Types\n----------------------------------------------\n\n**OpenWISP Notifications** provides registering and unregistering notifications through utility functions\n``openwisp_notifications.types.register_notification_type`` and ``openwisp_notifications.types.unregister_notification_type``.\nUsing these functions you can register or unregister notification types from your code.\n\nregister_notification_type\n~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThis function is used to register a new notification type from your code.\n\nSyntax:\n\n.. code-block:: python\n\n    register_notification_type(type_name, type_config, models)\n\n+---------------+-------------------------------------------------------------+\n| **Parameter** | **Description**                                             |\n+---------------+-------------------------------------------------------------+\n| type_name     | A ``str`` defining name of the notification type.           |\n+---------------+-------------------------------------------------------------+\n| type_config   | A ``dict`` defining configuration of the notification type. |\n+---------------+-------------------------------------------------------------+\n| models        | An optional ``list`` of models that can be associated with  |\n|               | the notification type.                                      |\n+---------------+-------------------------------------------------------------+\n\nAn example usage has been shown below.\n\n.. code-block:: python\n\n    from openwisp_notifications.types import register_notification_type\n    from django.contrib.auth import get_user_model\n\n    User = get_user_model()\n\n    # Define configuration of your notification type\n    custom_type = {\n        'level': 'info',\n        'verb': 'added',\n        'verbose_name': 'device added',\n        'message': '[{notification.target}]({notification.target_link}) was {notification.verb} at {notification.timestamp}',\n        'email_subject' : '[{site.name}] A device has been added',\n        'web_notification': True,\n        'email_notification': True,\n    }\n\n    # Register your custom notification type\n    register_notification_type('custom_type', custom_type, models=[User])\n\n**Note**: It will raise ``ImproperlyConfigured`` exception if a notification type is already registered\nwith same name(not to be confused with ``verbose_name``).\n\n**Note**: You can use ``site`` and ``notification`` variables while defining ``message`` and\n``email_subject`` configuration of notification type. They refer to objects of\n``django.contrib.sites.models.Site`` and ``openwisp_notifications.models.Notification`` respectively.\nThis allows you to use any of their attributes in your configuration. Similarly to ``message_template``,\n``message`` property can also be formatted using markdown.\n\nunregister_notification_type\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThis function is used to unregister a notification type from anywhere in your code.\n\nSyntax:\n\n.. code-block:: python\n\n    unregister_notification_type(type_name)\n\n+---------------+---------------------------------------------------+\n| **Parameter** | **Description**                                   |\n+---------------+---------------------------------------------------+\n| type_name     | A ``str`` defining name of the notification type. |\n+---------------+---------------------------------------------------+\n\nAn example usage is shown below.\n\n.. code-block:: python\n\n    from openwisp_notifications.types import unregister_notification_type\n\n    # Unregister previously registered notification type\n    unregister_notification_type('custom type')\n\n**Note**: It will raise ``ImproperlyConfigured`` exception if the concerned\nnotification type is not registered.\n\nPassing extra data to notifications\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nIf needed, additional data, not known beforehand, can be included in the notification message.\n\nA perfect example for this case is an error notification, the error message will vary\ndepending on what has happened, so we cannot know until the notification is generated.\n\nHere's how to do it:\n\n.. code-block:: python\n\n    from openwisp_notifications.types import register_notification_type\n\n    register_notification_type('error_type', {\n        'verbose_name': 'Error',\n        'level': 'error',\n        'verb': 'error',\n        'message': 'Error: {error}',\n        'email_subject': 'Error subject: {error}',\n    })\n\nThen in the application code:\n\n.. code-block:: python\n\n    from openwisp_notifications.signals import notify\n\n    try:\n        operation_which_can_fail()\n    except Exception as error:\n        notify.send(\n            type='error_type',\n            sender=sender,\n            error=str(error)\n        )\n\n**Note**: It is recommended that all notification types are registered or\nunregistered in ``ready`` method of your Django application's ``AppConfig``.\n\nNotification Preferences\n------------------------\n\n.. image:: https://github.com/openwisp/openwisp-notifications/raw/docs/docs/images/notification-settings.png\n\n*openwisp-notifications* allows users to select their preferred way of receiving notifications.\nUsers can choose from web or email notifications. These settings have been categorized\nover notification type and organization, therefore allowing users to only receive notifications\nfrom selected organization or notification type.\n\nNotification settings are automatically created for all notification types and organizations for all users.\nWhile superusers can add or delete notification settings for everyone, staff users can only modify their\npreferred ways for receiving notifications. With provided functionality, users can choose to receive both\nweb and email notifications or only web notifications. Users can also stop receiving notifications\nby disabling both web and email option for a notification setting.\n\n**Note**: If a user has not configured their email or web preference for a particular notification setting,\nthen ``email_notification`` or ``web_notification`` option of concerned notification type will be used\nrespectively.\n\nDeleting Notification Preferences\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nDeleting the notification preferences is an advanced option. Users should turn off web and email\nnotifications instead of deleting notification preferences. Deleted notification preferences\nmay be re-created automatically if the system needs it.\n\nSilencing notifications for specific objects temporarily or permanently\n-----------------------------------------------------------------------\n\n.. image:: https://github.com/openwisp/openwisp-notifications/raw/docs/docs/images/silence-notifications.png\n   :align: center\n\n*OpenWISP Notifications* allows users to silence all notifications generated by\nspecific objects they are not interested in for a desired period of time or even permanently,\nwhile other users will keep receiving notifications normally.\n\nUsing the widget on an object's admin change form, a user can disable all notifications\ngenerated by that object for a day, week, month or permanently.\n\n**Note**: This feature requires configuring\n`\"OPENWISP_NOTIFICATIONS_IGNORE_ENABLED_ADMIN\" \u003c#openwisp_notifications_ignore_enabled_admin\u003e`_\nto enable the widget in the admin section of the required models.\n\nScheduled deletion of notifications\n-----------------------------------\n\n*OpenWISP Notifications* provides a celery task to automatically delete\nnotifications older than a pre-configured number of days. In order to run this\ntask periodically, you will need to configure ``CELERY_BEAT_SCHEDULE`` setting as shown\nin `setup instructions \u003c#setup-integrate-into-an-existing-django-project\u003e`_.\n\nThe celery task takes only one argument, i.e. number of days. You can provide\nany number of days in `args` key while configuring ``CELERY_BEAT_SCHEDULE`` setting.\n\nE.g., if you want notifications older than 10 days to get deleted automatically,\nthen configure ``CELERY_BEAT_SCHEDULE`` as follows:\n\n.. code-block:: python\n\n    CELERY_BEAT_SCHEDULE = {\n        'delete_old_notifications': {\n            'task': 'openwisp_notifications.tasks.delete_old_notifications',\n            'schedule': timedelta(days=1),\n            'args': (10,), # Here we have defined 10 instead of 90 as shown in setup instructions\n        },\n    }\n\nPlease refer to `\"Periodic Tasks\" section of Celery's documentation \u003chttps://docs.celeryproject.org/en/stable/userguide/periodic-tasks.html\u003e`_\nto learn more.\n\nSettings\n--------\n\n``OPENWISP_NOTIFICATIONS_HOST``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+---------+----------------------------------------+\n| type    | ``str``                                |\n+---------+----------------------------------------+\n| default | Any domain defined in ``ALLOWED_HOST`` |\n+---------+----------------------------------------+\n\nThis setting defines the domain at which API and Web Socket communicate for\nworking of notification widget.\n\n**Note**: You don't need to configure this setting if you\ndon't host your API endpoints on a different sub-domain.\n\nIf your root domain is ``example.com`` and API and Web Socket are hosted at\n``api.example.com``, then configure setting as follows:\n\n.. code-block:: python\n\n    OPENWISP_NOTIFICATIONS_HOST = 'https://api.example.com'\n\nThis feature requires you to allow `CORS \u003chttps://developer.mozilla.org/en-US/docs/Web/HTTP/CORS\u003e`_\non your server. We use ``django-cors-headers`` module to easily setup CORS headers.\nPlease refer `django-core-headers' setup documentation \u003chttps://github.com/adamchainz/django-cors-headers#setup\u003e`_.\n\nConfigure ``django-cors-headers`` settings as follows:\n\n.. code-block:: python\n\n    CORS_ALLOW_CREDENTIALS = True\n    CORS_ORIGIN_WHITELIST = ['https://www.example.com']\n\nConfigure Django's settings as follows:\n\n.. code-block:: python\n\n    SESSION_COOKIE_DOMAIN = 'example.com'\n    CSRF_COOKIE_DOMAIN = 'example.com'\n\nPlease refer to `Django's settings documentation \u003chttps://docs.djangoproject.com/en/3.0/ref/settings/\u003e`_\nfor more information on ``SESSION_COOKIE_DOMAIN`` and ``CSRF_COOKIE_DOMAIN`` settings.\n\n``OPENWISP_NOTIFICATIONS_SOUND``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+---------+-------------------------------------------------------------------------------------------+\n| type    | ``str``                                                                                   |\n+---------+-------------------------------------------------------------------------------------------+\n| default | `notification_bell.mp3 \u003chttps://github.com/openwisp/openwisp-notifications/tree/master/ \\ |\n|         | openwisp_notifications/static/openwisp-notifications/audio/notification_bell.mp3\u003e`_       |\n+---------+-------------------------------------------------------------------------------------------+\n\nThis setting defines notification sound to be played when notification is received\nin real-time on admin site.\n\nProvide an absolute or relative path(hosted on your webserver) to audio file as show below.\n\n.. code-block:: python\n\n    OPENWISP_NOTIFICATIONS_SOUND = '/static/your-appname/audio/notification.mp3'\n\n``OPENWISP_NOTIFICATIONS_CACHE_TIMEOUT``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+---------+-----------------------------------+\n| type    | ``int``                           |\n+---------+-----------------------------------+\n| default | ``172800`` `(2 days, in seconds)` |\n+---------+-----------------------------------+\n\nIt sets the number of seconds the notification contents should be stored in the cache.\nIf you want cached notification content to never expire, then set it to ``None``.\nSet it to ``0`` if you don't want to store notification contents in cache at all.\n\n``OPENWISP_NOTIFICATIONS_IGNORE_ENABLED_ADMIN``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+-----------+-----------+\n|   type    |  ``list`` |\n+-----------+-----------+\n|  default  |  []       |\n+-----------+-----------+\n\nThis setting enables the widget which allows users to\n`silence notifications for specific objects temporarily or permanently. \u003c#silencing-notifications-for-specific-objects-temporarily-or-permanently\u003e`_\nin the change page of the specified ``ModelAdmin`` classes.\n\nE.g., if you want to enable the widget for objects of ``openwisp_users.models.User``\nmodel, then configure the setting as following:\n\n.. code-block:: python\n\n    OPENWISP_NOTIFICATIONS_IGNORE_ENABLED_ADMIN = ['openwisp_users.admin.UserAdmin']\n\n``OPENWISP_NOTIFICATIONS_POPULATE_PREFERENCES_ON_MIGRATE``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+---------+----------+\n| type    | ``bool`` |\n+---------+----------+\n| default | ``True`` |\n+---------+----------+\n\nThis setting allows to disable creating `notification preferences \u003c#notification-preferences\u003e`_\non running migrations.\n\n``OPENWISP_NOTIFICATIONS_NOTIFICATION_STORM_PREVENTION``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nWhen the system starts creating a lot of notifications because of a\ngeneral network outage (e.g.: a power outage, a global misconfiguration),\nthe notification storm prevention mechanism avoids the constant displaying\nof new notification alerts as well as their sound, only the notification\ncounter will continue updating periodically, although it won't emit any\nsound or create any other visual element until the\nnotification storm is over.\n\nThis setting allows tweaking how this mechanism works.\n\nThe default configuration is as follows:\n\n.. code-block:: python\n\n    OPENWISP_NOTIFICATIONS_NOTIFICATION_STORM_PREVENTION = {\n        # Time period for tracking burst of notifications (in seconds)\n        'short_term_time_period': 10,\n        # Number of notifications considered as a notification burst\n        'short_term_notification_count': 6,\n        # Time period for tracking notifications in long time interval (in seconds)\n        'long_term_time_period': 180,\n        # Number of notifications in long time interval to be considered as a notification storm\n        'long_term_notification_count': 30,\n        # Initial time for which notification updates should be skipped (in seconds)\n        'initial_backoff': 1,\n        # Time by which skipping of notification updates should be increased (in seconds)\n        'backoff_increment': 1,\n        # Maximum interval after which the notification widget should get updated (in seconds)\n        'max_allowed_backoff': 15,\n    }\n\nExceptions\n----------\n\n``NotificationRenderException``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n.. code-block:: python\n\n    openwisp_notifications.exceptions.NotificationRenderException\n\nRaised when notification properties(``email`` or ``message``) cannot be rendered from\nconcerned *notification type*. It sub-classes ``Exception`` class.\n\nIt can be raised due to accessing non-existing keys like missing related objects\nin ``email`` or ``message`` setting of concerned *notification type*.\n\nREST API\n--------\n\nLive documentation\n~~~~~~~~~~~~~~~~~~\n\n.. image:: https://github.com/openwisp/openwisp-notifications/raw/docs/docs/images/api-docs.png\n\nA general live API documentation (following the OpenAPI specification) is available at ``/api/v1/docs/``.\n\nBrowsable web interface\n~~~~~~~~~~~~~~~~~~~~~~~\n\n.. image:: https://github.com/openwisp/openwisp-notifications/raw/docs/docs/images/api-ui.png\n\nAdditionally, opening any of the endpoints `listed below \u003c#list-of-endpoints\u003e`_\ndirectly in the browser will show the `browsable API interface of Django-REST-Framework\n\u003chttps://www.django-rest-framework.org/topics/browsable-api/\u003e`_,\nwhich makes it even easier to find out the details of each endpoint.\n\nAuthentication\n~~~~~~~~~~~~~~\n\nSee openwisp-users: `authenticating with the user token\n\u003chttps://github.com/openwisp/openwisp-users#authenticating-with-the-user-token\u003e`_.\n\nWhen browsing the API via the `Live documentation \u003c#live-documentation\u003e`_\nor the `Browsable web interface \u003c#browsable-web-interface\u003e`_, you can use\nthe session authentication by logging in the django admin.\n\nPagination\n~~~~~~~~~~\n\nThe *list* endpoint support the ``page_size`` parameter that allows paginating\nthe results in conjunction with the ``page`` parameter.\n\n.. code-block:: text\n\n    GET /api/v1/notification/?page_size=10\n    GET /api/v1/notification/?page_size=10\u0026page=2\n\nList of endpoints\n~~~~~~~~~~~~~~~~~\n\nSince the detailed explanation is contained in the `Live documentation \u003c#live-documentation\u003e`_\nand in the `Browsable web page \u003c#browsable-web-interface\u003e`_ of each endpoint,\nhere we'll provide just a list of the available endpoints,\nfor further information please open the URL of the endpoint in your browser.\n\nList user's notifications\n#########################\n\n.. code-block:: text\n\n    GET /api/v1/notification/\n\nMark all user's notifications as read\n#####################################\n\n.. code-block:: text\n\n    POST /api/v1/notification/read/\n\nGet notification details\n########################\n\n.. code-block:: text\n\n    GET /api/v1/notification/{pk}/\n\nMark a notification read\n########################\n\n.. code-block:: text\n\n    PATCH /api/v1/notification/{pk}/\n\nDelete a notification\n#####################\n\n.. code-block:: text\n\n    DELETE /api/v1/notification/{pk}/\n\nList user's notification setting\n################################\n\n.. code-block:: text\n\n    GET /api/v1/notification/user-setting/\n\nGet notification setting details\n################################\n\n.. code-block:: text\n\n    GET /api/v1/notification/user-setting/{pk}/\n\nUpdate notification setting details\n###################################\n\n.. code-block:: text\n\n    PATCH /api/v1/notification/user-setting/{pk}/\n\nList user's object notification setting\n#######################################\n\n.. code-block:: text\n\n    GET /api/v1/notification/ignore/\n\nGet object notification setting details\n#######################################\n\n.. code-block:: text\n\n    GET /api/v1/notification/ignore/{app_label}/{model_name}/{object_id}/\n\nCreate object notification setting\n##################################\n\n.. code-block:: text\n\n    PUT /api/v1/notification/ignore/{app_label}/{model_name}/{object_id}/\n\nDelete object notification setting\n##################################\n\n.. code-block:: text\n\n    DELETE /api/v1/notification/ignore/{app_label}/{model_name}/{object_id}/\n\n\nManagement Commands\n-------------------\n\n``populate_notification_preferences``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThis command will populate notification preferences for all users for organizations\nthey are member of.\n\nExample usage:\n\n.. code-block:: shell\n\n    # cd tests/\n    ./manage.py populate_notification_preferences\n\n**Note**: Before running this command make sure that the celery broker is\nrunning and **reachable** by celery workers.\n\n``create_notification``\n~~~~~~~~~~~~~~~~~~~~~~~\n\nThis command will create a dummy notification with ``default`` notification type\nfor the members of ``default`` organization.\nThis command is primarily provided for the sole purpose of testing notification\nin development only.\n\nExample usage:\n\n.. code-block:: shell\n\n    # cd tests/\n    ./manage.py create_notification\n\nInstalling for development\n--------------------------\n\nWe use Redis as celery broker (you can use a different broker if you want).\nThe recommended way for development is running it using Docker so you will need to\n`install docker and docker-compose \u003chttps://docs.docker.com/engine/install/\u003e`_ beforehand.\n\nIn case you prefer not to use Docker you can\n`install Redis from your repositories \u003chttps://redis.io/download\u003e`_, but keep in mind that\nthe version packaged by your distribution may be different.\n\nInstall SQLite:\n\n.. code-block:: shell\n\n    sudo apt install sqlite3 libsqlite3-dev openssl libssl-dev\n\nFork and clone the forked repository:\n\n.. code-block:: shell\n\n    git clone git://github.com/\u003cyour_fork\u003e/openwisp-notifications\n\nNavigate into the cloned repository:\n\n.. code-block:: shell\n\n    cd openwisp-notifications/\n\nSetup and activate a virtual-environment. (we'll be using  `virtualenv \u003chttps://pypi.org/project/virtualenv/\u003e`_)\n\n.. code-block:: shell\n\n    python -m virtualenv env\n    source env/bin/activate\n\nMake sure that you are using pip version 20.2.4 before moving to the next step:\n\n.. code-block:: shell\n\n    pip install -U \"pip==20.2.4\" wheel\n\nInstall development dependencies:\n\n.. code-block:: shell\n\n    pip install -e .\n    pip install -r requirements-test.txt\n    npm install -g jslint stylelint\n\nStart Redis using docker-compose:\n\n.. code-block:: shell\n\n    docker-compose up -d\n\nCreate a database:\n\n.. code-block:: shell\n\n    cd tests/\n    ./manage.py migrate\n    ./manage.py createsuperuser\n\nLaunch the development server:\n\n.. code-block:: shell\n\n    ./manage.py runserver\n\nYou can access the admin interface at http://127.0.0.1:8000/admin/.\n\nRun celery  worker (separate terminal window is needed):\n\n.. code-block:: shell\n\n    # (cd tests)\n    celery -A openwisp2 worker -l info\n\nRun tests with:\n\n.. code-block:: shell\n\n    # run qa checks\n    ./run-qa-checks\n\n    # standard tests\n    ./runtests.py\n\n    # tests for the sample app\n    SAMPLE_APP=1 ./runtests.py\n\nWhen running the last line of the previous example, the environment variable ``SAMPLE_APP`` activates\nthe sample app in ``/tests/openwisp2/`` which is a simple django app that extend ``openwisp-notifications``\nwith the sole purpose of testing its extensibility, for more information regarding this concept,\nread the following section.\n\nExtending openwisp-notifications\n--------------------------------\n\nOne of the core values of the OpenWISP project is `Software Reusability \u003chttp://openwisp.io/docs/general/values.html#software-reusability-means-long-term-sustainability\u003e`_,\nfor this reason *openwisp-notification* provides a set of base classes which can be imported, extended\nand reused to create derivative apps.\n\nIn order to implement your custom version of *openwisp-notifications*, you need to perform the steps\ndescribed in the rest of this section.\n\nWhen in doubt, the code in `test project \u003chttps://github.com/openwisp/openwisp-notifications/tree/master/tests/openwisp2/\u003e`_\nand `sample_notifications \u003chttps://github.com/openwisp/openwisp-notifications/tree/master/tests/openwisp2/sample_notifications/\u003e`_\nwill guide you in the correct direction: just replicate and adapt that code to get a basic derivative of\n*openwisp-notifications* working.\n\n**Premise**: if you plan on using a customized version of this module, we suggest to start with it since\nthe beginning, because migrating your data from the default module to your extended version may be time\nconsuming.\n\n1. Initialize your custom module\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThe first thing you need to do in order to extend *openwisp-notifications* is create a new django app which\nwill contain your custom version of that *openwisp-notifications* app.\n\nA django app is nothing more than a `python package \u003chttps://docs.python.org/3/tutorial/modules.html#packages\u003e`_\n(a directory of python scripts), in the following examples we'll call this django app as ``mynotifications``\nbut you can name it how you want:\n\n.. code-block:: shell\n\n    django-admin startapp mynotifications\n\nKeep in mind that the command mentioned above must be called from a directory which is available in your\n`PYTHON_PATH \u003chttps://docs.python.org/3/using/cmdline.html#envvar-PYTHONPATH\u003e`_ so that you can then import\nthe result into your project.\n\nNow you need to add ``mynotifications`` to ``INSTALLED_APPS`` in your ``settings.py``, ensuring also that\n``openwisp_notifications`` has been removed:\n\n.. code-block:: python\n\n    INSTALLED_APPS = [\n        # ... other apps ...\n        # 'openwisp_notifications',        \u003c-- comment out or delete this line\n        'mynotifications',\n    ]\n\nFor more information about how to work with django projects and django apps, please refer to the\n`django documentation \u003chttps://docs.djangoproject.com/en/dev/intro/tutorial01/\u003e`_.\n\n2. Install ``openwisp-notifications``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nInstall (and add to the requirement of your project) *openwisp-notifications*:\n\n.. code-block:: shell\n\n    pip install -U https://github.com/openwisp/openwisp-notifications/tarball/master\n\n3. Add ``EXTENDED_APPS``\n~~~~~~~~~~~~~~~~~~~~~~~~\n\nAdd the following to your ``settings.py``:\n\n.. code-block:: python\n\n    EXTENDED_APPS = ['openwisp_notifications']\n\n4. Add ``openwisp_utils.staticfiles.DependencyFinder``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nAdd ``openwisp_utils.staticfiles.DependencyFinder`` to ``STATICFILES_FINDERS`` in your ``settings.py``:\n\n.. code-block:: python\n\n    STATICFILES_FINDERS = [\n        'django.contrib.staticfiles.finders.FileSystemFinder',\n        'django.contrib.staticfiles.finders.AppDirectoriesFinder',\n        'openwisp_utils.staticfiles.DependencyFinder',\n    ]\n\n5. Add ``openwisp_utils.loaders.DependencyLoader``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nAdd ``openwisp_utils.loaders.DependencyLoader`` to ``TEMPLATES`` in your ``settings.py``:\n\n.. code-block:: python\n\n    TEMPLATES = [\n        {\n            'BACKEND': 'django.template.backends.django.DjangoTemplates',\n            'OPTIONS': {\n                'loaders': [\n                    'django.template.loaders.filesystem.Loader',\n                    'django.template.loaders.app_directories.Loader',\n                    'openwisp_utils.loaders.DependencyLoader',\n                ],\n                'context_processors': [\n                    'django.template.context_processors.debug',\n                    'django.template.context_processors.request',\n                    'django.contrib.auth.context_processors.auth',\n                    'django.contrib.messages.context_processors.messages',\n                ],\n            },\n        }\n    ]\n\n6. Inherit the AppConfig class\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nPlease refer to the following files in the sample app of the test project:\n\n- `sample_notifications/__init__.py \u003chttps://github.com/openwisp/openwisp-notifications/blob/master/tests/openwisp2/sample_notifications/__init__.py\u003e`_.\n- `sample_notifications/apps.py \u003chttps://github.com/openwisp/openwisp-notifications/blob/master/tests/openwisp2/sample_notifications/apps.py\u003e`_.\n\nFor more information regarding the concept of ``AppConfig`` please refer to the\n`\"Applications\" section in the django documentation \u003chttps://docs.djangoproject.com/en/dev/ref/applications/\u003e`_.\n\n7. Create your custom models\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nFor the purpose of showing an example, we added a simple \"details\" field to the\n`models of the sample app in the test project \u003chttps://github.com/openwisp/openwisp-notifications/blob/master/tests/openwisp2/sample_notifications/models.py\u003e`_.\n\nYou can add fields in a similar way in your ``models.py`` file.\n\n**Note**: For doubts regarding how to use, extend or develop models please refer to\nthe `\"Models\" section in the django documentation \u003chttps://docs.djangoproject.com/en/dev/topics/db/models/\u003e`_.\n\n8. Add swapper configurations\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nAdd the following to your ``settings.py``:\n\n.. code-block:: python\n\n    # Setting models for swapper module\n    OPENWISP_NOTIFICATIONS_NOTIFICATION_MODEL = 'mynotifications.Notification'\n    OPENWISP_NOTIFICATIONS_NOTIFICATIONSETTING_MODEL = 'mynotifications.NotificationSetting'\n    OPENWISP_NOTIFICATIONS_IGNOREOBJECTNOTIFICATION_MODEL = 'mynotifications.IgnoreObjectNotification'\n\n9. Create database migrations\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nCreate and apply database migrations::\n\n    ./manage.py makemigrations\n    ./manage.py migrate\n\nFor more information, refer to the\n`\"Migrations\" section in the django documentation \u003chttps://docs.djangoproject.com/en/dev/topics/migrations/\u003e`_.\n\n10. Create your custom admin\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nRefer to the `admin.py file of the sample app \u003chttps://github.com/openwisp/openwisp-notifications/tests/openwisp2/sample_firmware_upgrader/admin.py\u003e`_.\n\nTo introduce changes to the admin, you can do it in two main ways which are described below.\n\n**Note**: For more information regarding how the django admin works, or how it can be customized,\nplease refer to `\"The django admin site\" section in the django documentation \u003chttps://docs.djangoproject.com/en/dev/ref/contrib/admin/\u003e`_.\n\n1. Monkey patching\n##################\n\nIf the changes you need to add are relatively small, you can resort to monkey patching.\n\nFor example:\n\n.. code-block:: python\n\n    from openwisp_notifications.admin import NotificationSettingInline\n\n    NotificationSettingInline.list_display.insert(1, 'my_custom_field')\n    NotificationSettingInline.ordering = ['-my_custom_field']\n\n2. Inheriting admin classes\n###########################\n\nIf you need to introduce significant changes and/or you don't want to resort to\nmonkey patching, you can proceed as follows:\n\n.. code-block:: python\n\n    from django.contrib import admin\n    from openwisp_notifications.admin import (\n        NotificationSettingInline as BaseNotificationSettingInline,\n    )\n    from openwisp_notifications.swapper import load_model\n\n    NotificationSetting = load_model('NotificationSetting')\n\n    admin.site.unregister(NotificationSettingAdmin)\n    admin.site.unregister(NotificationSettingInline)\n\n\n    @admin.register(NotificationSetting)\n    class NotificationSettingInline(BaseNotificationSettingInline):\n        # add your changes here\n        pass\n\n11. Create root URL configuration\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nPlease refer to the `urls.py \u003chttps://github.com/openwisp/openwisp-notifications/blob/master/tests/openwisp2/urls.py\u003e`_\nfile in the test project.\n\nFor more information about URL configuration in django, please refer to the\n`\"URL dispatcher\" section in the django documentation \u003chttps://docs.djangoproject.com/en/dev/topics/http/urls/\u003e`_.\n\n12. Create root routing configuration\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nPlease refer to the `routing.py \u003chttps://github.com/openwisp/openwisp-notifications/blob/master/tests/openwisp2/routing.py\u003e`_\nfile in the test project.\n\nFor more information about URL configuration in django, please refer to the\n`\"Routing\" section in the Channels documentation \u003chttps://channels.readthedocs.io/en/latest/topics/routing.html\u003e`_.\n\n13. Create celery.py\n~~~~~~~~~~~~~~~~~~~~\n\nPlease refer to the `celery.py \u003chttps://github.com/openwisp/openwisp-notifications/blob/master/tests/openwisp2/celery.py\u003e`_\nfile in the test project.\n\nFor more information about the usage of celery in django, please refer to the\n`\"First steps with Django\" section in the celery documentation \u003chttps://docs.celeryproject.org/en/master/django/first-steps-with-django.html\u003e`_.\n\n14. Import Celery Tasks\n~~~~~~~~~~~~~~~~~~~~~~~\n\nAdd the following in your settings.py to import celery tasks from ``openwisp_notifications`` app.\n\n.. code-block:: python\n\n    CELERY_IMPORTS = ('openwisp_notifications.tasks',)\n\n15. Register Template Tags\n~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nIf you need to use template tags of *openwisp_notifications*, you will need to register as shown in\n`\"templatetags/notification_tags.py\" of sample_notifications\n\u003chttps://github.com/openwisp/openwisp-notifications/blob/master/tests/openwisp2/sample_notifications/templatetags/notification_tags.py\u003e`_.\n\nFor more information about template tags in django, please refer to the\n`\"Custom template tags and filters\" section in the django documentation \u003chttps://docs.djangoproject.com/en/dev/topics/http/urls/\u003e`_.\n\n16. Register Notification Types\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nYou can register notification types as shown in the `section for registering notification types \u003c#register_notification_type\u003e`_.\n\nA reference for registering a notification type is also provided in\n`sample_notifications/apps.py \u003chttps://github.com/openwisp/openwisp-notifications/blob/master/tests/openwisp2/sample_notifications/apps.py\u003e`_.\nThe registered notification type of ``sample_notifications`` app is used for creating notifications\nwhen an object of ``TestApp`` model is created. You can use\n`sample_notifications/models.py \u003chttps://github.com/openwisp/openwisp-notifications/blob/master/tests/openwisp2/sample_notifications/models.py\u003e`_\nas reference for your implementation.\n\n17. Import the automated tests\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nWhen developing a custom application based on this module, it's a good idea to import and run the base tests\ntoo, so that you can be sure the changes you're introducing are not breaking some of the existing feature\nof openwisp-notifications.\n\nIn case you need to add breaking changes, you can overwrite the tests defined in the base classes to test\nyour own behavior.\n\nSee the `tests of the sample_notifications\n\u003chttps://github.com/openwisp/openwisp-notifications/blob/master/tests/openwisp2/sample_notifications/tests.py\u003e`_\nto find out how to do this.\n\n**Note**: Some tests will fail if ``templatetags`` and ``admin/base.html`` are not configured properly.\nSee preceeding sections to configure them properly.\n\nOther base classes that can be inherited and extended\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThe following steps are not required and are intended for more advanced customization.\n\nAPI views\n#########\n\nThe API view classes can be extended into other django applications as well. Note\nthat it is not required for extending openwisp-notifications to your app and this change\nis required only if you plan to make changes to the API views.\n\nCreate a view file as done in `sample_notifications/views.py \u003chttps://github.com/openwisp/openwisp-notifications/blob/master/tests/openwisp2/sample_notifications/views.py\u003e`_\n\nFor more information regarding Django REST Framework API views, please refer to the\n`\"Generic views\" section in the Django REST Framework documentation \u003chttps://www.django-rest-framework.org/api-guide/generic-views/\u003e`_.\n\nWeb Socket Consumers\n####################\n\nThe Web Socket Consumer classes can be extended into other django applications as well. Note\nthat it is not required for extending openwisp-notifications to your app and this change\nis required only if you plan to make changes to the consumers.\n\nCreate a consumer file as done in `sample_notifications/consumers.py \u003chttps://github.com/openwisp/openwisp-notifications/blob/master/tests/openwisp2/sample_notifications/consumers.py\u003e`_\n\nFor more information regarding Channels' Consumers, please refer to the\n`\"Consumers\" section in the Channels documentation \u003chttps://channels.readthedocs.io/en/latest/topics/consumers.html\u003e`_.\n\nContributing\n------------\n\nPlease read the `OpenWISP contributing guidelines \u003chttp://openwisp.io/docs/developer/contributing.html\u003e`_.\n\nLicense\n-------\n\nSee `LICENSE \u003chttps://github.com/openwisp/openwisp-notifications/blob/master/LICENSE\u003e`_.\n\nSupport\n-------\n\nSee `OpenWISP Support Channels \u003chttp://openwisp.org/support.html\u003e`_.\n\nAttributions\n------------\n\nIcons\n~~~~~\n\n`Icons \u003chttps://github.com/openwisp/openwisp-notifications/tree/master/openwisp_notifications/static/openwisp-notifications/images/icons/\u003e`_\nused are taken from `Font Awesome \u003chttps://fontawesome.com/\u003e`_ project.\n\nLICENSE: `https://fontawesome.com/license \u003chttps://fontawesome.com/license\u003e`_\n\nSound\n~~~~~\n\n`Notification sound \u003chttps://github.com/openwisp/openwisp-notifications/tree/master/openwisp_notifications/static/openwisp-notifications/audio\u003e`_\nis taken from `Notification Sounds \u003chttps://notificationsounds.com/\u003e`_.\n\nLICENSE: `Creative Commons Attribution license \u003chttps://creativecommons.org/licenses/by/4.0/legalcode\u003e`_\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmanukumarnm%2Fopenwisp-notification","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmanukumarnm%2Fopenwisp-notification","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmanukumarnm%2Fopenwisp-notification/lists"}