Package: wc-django-notifications
Django notificationsModular notifications system for your django applications.Installationpipinstallwc-django-notificationsInsettings.py:INSTALLED_APPS+=[# ...'wcd_notifications',]WCD_NOTIFICATIONS={# Resolving function to get recipients from request.# Using mostly by API.'RECIPIENTS_RESOLVER':'wcd_notifications.utils.default_resolve_recipients',# Pipeline functions for additional notifications instances preparation.'PREPARATION_PIPELINE':[],# Pipeline functions that handle notification flag changes.'CHANGE_FLAG_PIPELINE':[# Resolves readability flags intersection.'wcd_notifications.services.manager.set_readability_flags_operation',],}UsageSending notificationsfromwcd_notifications.servicesimportnotifiernotifications=notifier.send(# Simple formattable string.# `.format` method will be applied during `send` event.# For example:'{actor}created new{action}after leaving{target}.',# Recipient objects list.[user1,user2],# Optional. Object that performed activity.actor=None,# Optional. Action object, that was performed.action=None,# Optional. Target object to which the activity was performed.target=None,# Parent notification. If exists.parent=None,# Initial flags that a message will have.flags=[],# Some extra data that will be stored in json field.extra={},# Recipients can receive this message after some time.send_at=None,# Can send with different current time.now=None,batch_size=None,)Notifications send method has several breakpoints on which you may add additional handlers for a common situations:Each notification separately will run preparation pipeline fromNotification.preparator. It's an instances ofpx_pipline.Filterso you may add or remove them.Then preparation pipeline fromsettings.PREPARATION_PIPELINEwill be run on all new notification instances.Notifications will be created.For each notificationwcd_notifications.signals.notification_sentwill be sent.For all new notificationswcd_notifications.signals.notifications_sentwill be sent.There is already onewcd_notifications.signals.notifications_sentsignal reciever. It will recollect stats for all the recipients that new notifications has.Notification state managementYou may change flags in three different ways:add- List of flags to add.remove- List of flags to remove.specify- List of flags to set hard.addandremoveparameters i this case will be ignored.fromwcd_notifications.servicesimportmanager# For example we need to mark some notifications as `read`:manager.change_flags(Notification.objects.all(),add=[Notification.Readability.READ],# Empty specify list will be ignored.specify=[],)Only read/unread flags are not enough. And you may use any numbers you wish as flags. It would be better to usedjango.db.models.IntegerChoicesor at leastenum.Enumto define different state groups.And it will be better if library could know about your flags.For this case there is a simple flags registry:fromenumimportEnumfromwcd_notifications.modelsimportNotificationclassOtherOptions(int,Enum):ONE=1TWO=2# By running this command you register your flags:Notification.flags_registry.add(OtherOptions)Flags change method also has it's own breakpoints to extend:After all flag changes applied, but not saved pipeline fromsettings.CHANGE_FLAG_PIPELINEruns.All notification flags changes are saved to database.Signalwcd_notifications.signals.notifications_flags_changedsent.Signalwcd_notifications.signals.notifications_updatedsent.QueryingNotifications queryset has additional methods, for easier querying:fromwcd_notifications.modelsimportNotificationqs=(Notification.objects# To find only read notifications..read()# To find only new notifications..unread()# To filter by recipients:.recipients([])# Actors:.actors([])# Actions:.actions([])# Targets:.targets([])# To display only already sent messages..sent(now=None))StatsReading information about notifications state is far more frequent action, that notifications change/sending. So library "caches" state data into database and updates it after any change occured.There is only one simple SELECT operation to get state for any recipients:fromwcd_notifications.modelsimportStats# For one recipient there is at most one stats object exists.user_stats=Stats.objects.recipients([user]).first()Collecting stats are automatically happens in management methods. But there could be cases when notifications are updated by hand. In that case you should collect stats manually:fromwcd_notifications.servicesimportmanager# You may pass a recipients list here or manual `None` if stats for all# recipients must be updated.manager.collect_stats([user1])ContribDRFThere are ready for use frontend for django rest framework.INSTALLED_APPS+=[# ...'wcd_notifications.contrib.drf',# Django filters required for api to work.'django_filters','rest_framework',]In`urls.py`:```pythonfromwcd_notifications.contrib.drf.viewsimportmake_urlpatternsurlpatters=[...path('api/v1/notifications/',include(make_urlpatterns(# Here you can replace any view by your own customized one.))),]It will add 5 views there:GET[/api/v1/notifications/flags/list/]Returns list of all available flags.GET[/api/v1/notifications/notifications/list/]Paginatable list of notifications.To be able to display related objects in more detailed manner you may also register serializers that will be used for each object of a particular model:fromwcd_notifications.contrib.drf.serializersimportFIELD_SERIALIZERSfrom.modelsimportSomeModelfrom.serializersimportSomeModelSerializer# So for any object of `SomeModel` `SomeModelSerializer` will be used to# display `props` in notifications list view.FIELD_SERIALIZERS.add(SomeModel,SomeModelSerializer)Also notifications messages can have shortcodes fromwc-shortcodesand will be transformed during render. You only need to register your custom shortodes:fromwcd_notifications.contrib.drf.serializersimportSHORTCODES_REGISTRY@SHORTCODES_REGISTRY.register()defshortcode(entity,context={}):returnentity.contentFilters that can be applied:ids: List of identifiers to filter over:&ids=1,2,3&ids=4,6- There can be mutiple same named fields. Comma ',' could be also a separator for multiple values. Or operator used for all identifiers.actors: List of actors to filter on:&actors=auth.user~1,auth.user~2&actors=auth.user~4- There can be mutiple same named fields. Comma ',' could be also a separator for multiple values. Or operator used for all identifiers.One value to filter on is:{object_type}~{object_id}.actions: Same asactors.targets: Same asactors.actor_types: List of types for filter on:&actor_types=auth.user,muapp.mymodel&actor_types=otherapp.othermodel- There can be mutiple same named fields. Comma ',' could be also a separator for multiple values. Or operator used for all identifiers.action_types: Same asactor_types.target_types: Same asactor_types.sent_at_before: Filter by the time >= notification was sent. ISO datetime.sent_at_after: Filter by the time <= notification was sent. ISO datetime.is_sent: Get only already sent notifications.is_root: Since notifications are hierarchical you may only need a root notifications.parents: List of parent identifiers to filter over:&parents=1,2,3&parents=4,6- There can be mutiple same named fields. Comma ',' could be also a separator for multiple values. Or operator used for all identifiers.flags: Filtering over flags is not that simple OR statement.&flags=1,2,3&flags=4,6- Each comma ',' separated list considered as AND comparison. Or operator used between multipleflagsfields.So this example will lead to(1 or 2 or 3) and (4 or 6)comparison statement.POST[/api/v1/notifications/notifications/set-flags/]To add specific flags to notifications(likereadfor example) you can use this method.Notifications can be filtered the same way as in/notifications/list/view.Under the hood it usesmanager.change_flagsmethod, so data it requires works the same:{"add":[1,2,4],"remove":[6],"specify":[]}POST[/api/v1/notifications/notifications/clear/]This view deletes notifications by your will. What notifications should be deleted can be filtered the same way as in/notifications/list/view.GET[/api/v1/notifications/notifications/stats/]Here user can get a notifications statistics. There will be a list for each recipient that current authorization resolves in.Each recipient stats contains total count of notifications and notification counts for every flag exists in their notifications.So this way you will find all read/unread messages for example.Filters that can be applied:actor_types: List of types for filter on:&actor_types=auth.user,muapp.mymodel&actor_types=otherapp.othermodel- There can be mutiple same named fields. Comma ',' could be also a separator for multiple values. Or operator used for all identifiers.action_types: Same asactor_types.target_types: Same asactor_types.By applying filters you receive statistics data only for affected notifications.ChangelogAll notable changes to this project will be documented in this file.The format is based onKeep a Changelog,
and this project adheres toSemantic Versioning.[Unreleased][0.3.1]FixedAttempts to collect stats forNonerecipients.AddedManagement command to clear notifications and stats.[0.3.0]ChangedInternal stats collect implementation.AddedManagement command to recollect stats.Notification stats filtering.[0.2.0]ChangedPast form of verb "send" changed everywhere from "sended" to "sent".RemovedRemoved explicitpagination_classfrom notifications list view.FixedSmall fixes.[0.1.3]FixedDjango 2.2 compatibility.[0.1.1]Initial version.