Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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
Showing
20 changed files
with
575 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
build | ||
dist | ||
*.pyc | ||
.DS_Store | ||
MANIFEST |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
Changelog | ||
========== | ||
|
||
0.5 | ||
----- | ||
|
||
First version based on `django-activity-stream <https://github.com/justquick/django-activity-stream>`_ v0.4.3 | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
include setup.py README.md AUTHORS.txt LICENSE.txt CHANGELOG.rst | ||
recursive-include actstream *.py *.html *.txt *.po |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 %} | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
from django.dispatch import Signal | ||
|
||
notify = Signal(providing_args=['recipient', 'actor', 'verb', 'action_object', 'target', | ||
'description', 'timestamp']) |
Oops, something went wrong.