Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
First version based on django-activity-stream v0.4.3
  • Loading branch information
Brant Young committed Jul 22, 2012
1 parent 4c6d21b commit da17a04
Show file tree
Hide file tree
Showing 20 changed files with 575 additions and 4 deletions.
5 changes: 5 additions & 0 deletions .gitignore
@@ -0,0 +1,5 @@
build
dist
*.pyc
.DS_Store
MANIFEST
26 changes: 26 additions & 0 deletions AUTHORS.txt
@@ -0,0 +1,26 @@
Justin Quick <justquick@gmail.com>
Aaron Williamson
Jordan Reiter
Manuel Aristaran
Darrell Hoy
Ken "Elf" Mathieu Sternberg
Josh Ourisman
Trever Shick
Sandip Agrawal
Piet Delport
Steve Ivy
Ben Slavin
Jason Culverhouse
Dave Harrington
Pedro Bur�n
Ryan Quigley
Neelesh Shastry
David Gouldin <david@gould.in>
Donald Stufft
Nolan Brubaker
Herman Schaaf
Walter Scheper
Chris Beaven
Vineet Naik
Walter Scheper
Brant Young
8 changes: 8 additions & 0 deletions CHANGELOG.rst
@@ -0,0 +1,8 @@
Changelog
==========

0.5
-----

First version based on `django-activity-stream <https://github.com/justquick/django-activity-stream>`_ v0.4.3

29 changes: 29 additions & 0 deletions LICENSE.txt
@@ -0,0 +1,29 @@
Copyright (c) 2011, Justin Quick
Copyright (c) 2012, Brant Young
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of the author nor the names of other
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2 changes: 2 additions & 0 deletions MANIFEST.in
@@ -0,0 +1,2 @@
include setup.py README.md AUTHORS.txt LICENSE.txt CHANGELOG.rst
recursive-include actstream *.py *.html *.txt *.po
4 changes: 0 additions & 4 deletions README.md

This file was deleted.

114 changes: 114 additions & 0 deletions README.rst
@@ -0,0 +1,114 @@
Django Notifications Documentation
===================================

`django-notifications <https://github.com/brantyoung/django-notifications>`_ is a GitHub notification alike app for Django, it was derived from `django-activity-stream <https://github.com/justquick/django-activity-stream>`_

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: `justquick <https://github.com/justquick/>`_ ``(actor)`` *closed* ``(verb)`` `issue 2 <https://github.com/justquick/django-activity-stream/issues/2>`_ ``(object)`` on `activity-stream <https://github.com/justquick/django-activity-stream/>`_ ``(target)`` 12 hours ago

Nomenclature of this specification is based on the Activity Streams Spec: `<http://activitystrea.ms/specs/atom/1.0/>`_

Installation
============

Installation is easy using ``pip`` and the only requirement is a recent version of Django.

::

$ pip install django-notifications-hq

or get it from source

::

$ git clone https://github.com/brantyoung/django-notifications
$ cd django-notifications
$ python setup.py install

Then to add the Django Notifications to your project add the app ``notifications`` to your ``INSTALLED_APPS`` and urlconf.

The app should go somewhere after all the apps that are going to be generating notifications like ``django.contrib.auth``::

INSTALLED_APPS = (
'django.contrib.auth',
...
'notifications',
...
)

Add the notifications urls to your urlconf::

urlpatterns = patterns('',
...
('^inbox/notifications/', include('notifications.urls')),
...
)

Generating Notifications
=========================

Generating notifications is probably best done in a separate signal.

::

from django.db.models.signals import post_save
from notifications import notify
from myapp.models import MyModel

def my_handler(sender, instance, created, **kwargs):
notify.send(instance, verb='was saved')

post_save.connect(my_handler, sender=MyModel)

To generate an notification anywhere in your code, simply import the notify signal and send it with your actor, verb, and target.

::

from notifications import notify

notify.send(request.user, verb='reached level 10')
notify.send(request.user, verb='joined', target=group)
notify.send(request.user, verb='created comment', action_object=comment, target=group)

API
====

``Notification.objects.mark_all_as_read(recipient)``
-------------------------------------------------------

Mark all unread notifications received by ``recipient``.


``Notification.objects.get().mark_as_read()``
---------------------------------------------

Mark current notification as read.


``notifications_unread`` templatetags
--------------------------------------

::

{% notifications_unread %}

Give the number of unread notifications for a user, or nothing (an empty string) for an anonymous user.

Storing the count in a variable for further processing is advised, such as::

{% notifications_unread as unread_count %}
...
{% if unread_count %}
You have <strong>{{ unread_count }}</strong> unread notifications.
{% endif %}


25 changes: 25 additions & 0 deletions notifications/__init__.py
@@ -0,0 +1,25 @@
try:
from notifications.signals import notify
except ImportError:
pass

__version_info__ = {
'major': 0,
'minor': 5,
'micro': 1,
'releaselevel': 'final',
'serial': 0
}


def get_version(release_level=True):
"""
Return the formatted version information
"""
vers = ["%(major)i.%(minor)i.%(micro)i" % __version_info__]
if release_level and __version_info__['releaselevel'] != 'final':
vers.append('%(releaselevel)s%(serial)i' % __version_info__)
return ''.join(vers)


__version__ = get_version()
9 changes: 9 additions & 0 deletions notifications/admin.py
@@ -0,0 +1,9 @@
# -*- coding: utf-8 -*-

from django.contrib import admin
from notifications.models import Notification

class NotificationAdmin(admin.ModelAdmin):
pass

admin.site.register(Notification, NotificationAdmin)
9 changes: 9 additions & 0 deletions notifications/managers.py
@@ -0,0 +1,9 @@
from django.db import models


class NotificationManager(models.Manager):
def unread_count(self, user):
return self.filter(recipient=user, readed=False).count()

def mark_all_as_read(self, recipient):
return self.filter(recipient=recipient, readed=False).update(readed=True)
140 changes: 140 additions & 0 deletions notifications/models.py
@@ -0,0 +1,140 @@
import datetime
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic
from django.db import models
from django.utils.timezone import utc
from .utils import id2slug
from notifications.managers import NotificationManager
from notifications.signals import notify

try:
from django.utils import timezone
now = timezone.now
except ImportError:
now = datetime.datetime.now

class Notification(models.Model):
"""
Action model describing the actor acting out a verb (on an optional
target).
Nomenclature based on http://activitystrea.ms/specs/atom/1.0/
Generalized Format::
<actor> <verb> <time>
<actor> <verb> <target> <time>
<actor> <verb> <action_object> <target> <time>
Examples::
<justquick> <reached level 60> <1 minute ago>
<brosner> <commented on> <pinax/pinax> <2 hours ago>
<washingtontimes> <started follow> <justquick> <8 minutes ago>
<mitsuhiko> <closed> <issue 70> on <mitsuhiko/flask> <about 2 hours ago>
Unicode Representation::
justquick reached level 60 1 minute ago
mitsuhiko closed issue 70 on mitsuhiko/flask 3 hours ago
HTML Representation::
<a href="http://oebfare.com/">brosner</a> commented on <a href="http://github.com/pinax/pinax">pinax/pinax</a> 2 hours ago
"""
recipient = models.ForeignKey(User, blank=False, related_name='notifications')
readed = models.BooleanField(default=False, blank=False)

actor_content_type = models.ForeignKey(ContentType, related_name='notify_actor')
actor_object_id = models.CharField(max_length=255)
actor = generic.GenericForeignKey('actor_content_type', 'actor_object_id')

verb = models.CharField(max_length=255)
description = models.TextField(blank=True, null=True)

target_content_type = models.ForeignKey(ContentType, related_name='notify_target',
blank=True, null=True)
target_object_id = models.CharField(max_length=255, blank=True, null=True)
target = generic.GenericForeignKey('target_content_type',
'target_object_id')

action_object_content_type = models.ForeignKey(ContentType,
related_name='notify_action_object', blank=True, null=True)
action_object_object_id = models.CharField(max_length=255, blank=True,
null=True)
action_object = generic.GenericForeignKey('action_object_content_type',
'action_object_object_id')

timestamp = models.DateTimeField(default=now)

public = models.BooleanField(default=True)

objects = NotificationManager()

class Meta:
ordering = ('-timestamp', )

def __unicode__(self):
ctx = {
'actor': self.actor,
'verb': self.verb,
'action_object': self.action_object,
'target': self.target,
'timesince': self.timesince()
}
if self.target:
if self.action_object:
return '%(actor)s %(verb)s %(action_object)s on %(target)s %(timesince)s ago' % ctx
return '%(actor)s %(verb)s %(target)s %(timesince)s ago' % ctx
if self.action_object:
return '%(actor)s %(verb)s %(action_object)s %(timesince)s ago' % ctx
return '%(actor)s %(verb)s %(timesince)s ago' % ctx

def timesince(self, now=None):
"""
Shortcut for the ``django.utils.timesince.timesince`` function of the
current timestamp.
"""
from django.utils.timesince import timesince as timesince_
return timesince_(self.timestamp, now)

@property
def slug(self):
return id2slug(self.id)

def mark_as_read(self):
if not self.readed:
self.readed = True
self.save()

def notify_handler(verb, **kwargs):
"""
Handler function to create Notification instance upon action signal call.
"""

kwargs.pop('signal', None)
recipient = kwargs.pop('recipient')
actor = kwargs.pop('sender')
newnotify = Notification(
recipient = recipient,
actor_content_type=ContentType.objects.get_for_model(actor),
actor_object_id=actor.pk,
verb=unicode(verb),
public=bool(kwargs.pop('public', True)),
description=kwargs.pop('description', None),
timestamp=kwargs.pop('timestamp', datetime.datetime.utcnow().replace(tzinfo=utc))
)

for opt in ('target', 'action_object'):
obj = kwargs.pop(opt, None)
if not obj is None:
setattr(newnotify, '%s_object_id' % opt, obj.pk)
setattr(newnotify, '%s_content_type' % opt,
ContentType.objects.get_for_model(obj))

newnotify.save()


# connect the signal
notify.connect(notify_handler, dispatch_uid='notifications.models.notification')
4 changes: 4 additions & 0 deletions notifications/signals.py
@@ -0,0 +1,4 @@
from django.dispatch import Signal

notify = Signal(providing_args=['recipient', 'actor', 'verb', 'action_object', 'target',
'description', 'timestamp'])

0 comments on commit da17a04

Please sign in to comment.