This commit is contained in:
Samuel Spencer 2016-02-08 03:57:02 +00:00
commit 1b85a996df
30 changed files with 350 additions and 983 deletions

1
.gitignore vendored
View file

@ -6,3 +6,4 @@ MANIFEST
.coverage
htmlcov
/.idea/
*.egg-info

View file

@ -1,27 +1,27 @@
language: python
python:
- "2.6"
- "2.7"
- "3.3"
- "3.4"
- "3.5"
env:
- DJANGO=1.6
- DJANGO=1.7
- DJANGO=1.8
- DJANGO=1.9
install:
# command to install dependencies
- "pip install coveralls"
- "pip install pytz" # Needed for tests
- pip install -q Django==$DJANGO --use-mirrors
- pip install -q Django==$DJANGO
- "pip install ."
# command to run tests
script:
- coverage run --branch --source=notifications manage.py test
matrix:
exclude:
- python: "2.6"
- python: "3.5"
env: DJANGO=1.7
- python: "2.6"
env: DJANGO=1.8
- python: "3.3"
env: DJANGO=1.9
after_success:
- coveralls

View file

@ -1,8 +1,38 @@
Changelog
==========
=========
1.0
-----
The first major version that requires Django 1.7+.
- Drop support for Django 1.6 and below (@zhang-z)
- Django 1.9 compability (@illing2005)
- Now depends on Django built-in migration facility, "south_migrations" dependence was removed (@zhang-z)
- Make django-notification compatible with django-model-utils >= 2.4 ( #87, #88, #90 ) (@zhang-z)
- Fix a RemovedInDjango110Warning in unittest (@zhang-z)
- Fix pep8 & use setuptools (@areski)
- Fix typo- in doc (@areski, @zhang-z)
- Add app_name in urls.py (@zhang-z)
- Use Django's vendored copy of six (@funkybob)
- Tidy with flake8 (@funkybob)
- Remove custom now() function (@funkybob, @yangyubo)
- notify.send() accepts User or Group (@Evidlo)
0.8.0
-----
0.8 is the last major version supports Django 1.4~1.6, version 0.8.0 will go into bugfix mode, no new features will be accepted.
- Bugfixes for live-updater, and added a live tester page (@LegoStormtroopr)
- Class-based classes (@alazaro)
- Fixed urls in tests (@alazaro)
- Added app_label to Notification model in order to fix a Django 1.9 deprecation warning (@Heldroe)
- django-model-utils compatible issue (must >=2.0.3 and <2.4) (@zhang-z)
- Reliable setup.py versioning (@yangyubo)
0.7.1
------
-----
- Able to pass level when adding notification (@Arthur)
- Fix deprecation notice in Django 1.8 (@ashokfernandez)
@ -12,7 +42,7 @@ Changelog
- Add missing migration for Notification model (@shezadkhan137)
0.7.0
------
-----
- Add filters and displays to Django model Admin
- Support Django 1.8, compatible with both django-south (django < 1.7) and built-in schema migration (django >= 1.7)
@ -20,14 +50,14 @@ Changelog
- Test fixtures, and integrated with travis-ci
0.6.2
------
-----
- Fix README.rst reStructuredText syntax format
- Use relative imports
- Add contributors to AUTHORS.txt
0.6.1
------
-----
- Add support for custom user model
- mark_as_unread
@ -35,13 +65,13 @@ Changelog
- Use different `now` function according to the `USE_TZ` setting
0.6.0
------
-----
- Improve documentation
- Add unicode support at admin panel or shell
0.5.5
------
-----
Support for arbitrary data attribute.
@ -51,7 +81,7 @@ Support for arbitrary data attribute.
Fix package descriptions and doc links.
0.5
-----
---
First version based on `django-activity-stream <https://github.com/justquick/django-activity-stream>`_ v0.4.3

View file

@ -1,6 +1,7 @@
``django-notifications`` Documentation
=======================================
|build-status| |coveralls|
`django-notifications <https://github.com/django-notifications/django-notifications>`_ is a GitHub notification alike app for Django, it was derived from `django-activity-stream <https://github.com/justquick/django-activity-stream>`_
@ -24,6 +25,12 @@ For example: `justquick <https://github.com/justquick/>`_ ``(actor)`` *closed* `
Nomenclature of this specification is based on the Activity Streams Spec: `<http://activitystrea.ms/specs/atom/1.0/>`_
Requirements
============
- Python 2.7, 3.3, 3.4, 3.5
- Django 1.7, 1.8, 1.9
Installation
============
@ -58,34 +65,15 @@ Add the notifications urls to your urlconf::
import notifications
urlpatterns = patterns('',
urlpatterns = [
...
url('^inbox/notifications/', include(notifications.urls)),
url('^inbox/notifications/', include(notifications.urls, namespace='notifications')),
...
)
]
The method of installing these urls, importing rather than using ``'notifications.urls'``, is required to ensure that the urls are installed in the ``notifications`` namespace.
How to migrate schema
=========================================
For Django 1.4~1.6
------------------
`django-south` support is shipped with `django-notifications`.
#. Install latest version (>=1.0) `django-south <http://pypi.python.org/pypi/South/>`_
#. Execute ``manage.py migrate notifications`` to migrate `django-notifications` schema
Note: If you use a `custom user model <https://docs.djangoproject.com/en/1.6/topics/auth/customizing/#auth-custom-user>`_ in your application, you may want to check `reverse dependencies <http://south.aeracode.org/wiki/Dependencies>`_ of South to run your migrations in the correct order.
For Django 1.7
--------------
Django 1.7 has built-in support for migrations. No need to install `django-south`.
#. Execute ``manage.py migrate notifications``
To run schema migration, execute ``python manage.py migrate notifications``.
Generating Notifications
=========================
@ -95,7 +83,7 @@ Generating notifications is probably best done in a separate signal.
::
from django.db.models.signals import post_save
from notifications import notify
from notifications.signals import notify
from myapp.models import MyModel
def my_handler(sender, instance, created, **kwargs):
@ -107,11 +95,12 @@ To generate an notification anywhere in your code, simply import the notify sign
::
from notifications import notify
from notifications.signals import notify
notify.send(user, recipient=user, verb='you reached level 10')
notify.send(comment.user, recipient=user, verb=u'replied', action_object=comment,
// "recipient" can also be a Group, the notification will be sent to all the Users in the Group
notify.send(comment.user, recipient=group, verb=u'replied', action_object=comment,
description=comment.comment, target=comment.content_object)
notify.send(follow_instance.user, recipient=follow_instance.follow_object, verb=u'has followed you',
@ -245,7 +234,7 @@ Storing the count in a variable for further processing is advised, such as::
Live-updater API
================
To ensure users always have the most up-to-date notfications, `django-notifications` includes a simple javascript API
To ensure users always have the most up-to-date notifications, `django-notifications` includes a simple javascript API
for updating specific fields within a django template.
There are two possible API calls that can be made:

View file

@ -7,4 +7,4 @@ if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "notifications.tests.settings")
from django.core.management import execute_from_command_line
execute_from_command_line(sys.argv)
execute_from_command_line(sys.argv)

View file

@ -1,32 +1,13 @@
try:
from notifications.signals import notify
except ImportError:
pass
# -*- coding: utf-8 -*-
"""
django-notifications
~~~~~
A GitHub notification alike app for Django.
:copyright: (c) 2015 by django-notifications team.
:license: BSD, see LICENSE.txt for more details.
"""
try:
from notifications.urls import urlpatterns
urls = (urlpatterns, 'notifications', 'notifications')
except ImportError:
pass
__version_info__ = {
'major': 0,
'minor': 8,
'micro': 0,
'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()
# PEP 386-compliant version number: N.N[.N]+[{a|b|c|rc}N[.N]+][.postN][.devN]
__version__ = '1.0'
default_app_config = 'notifications.apps.Config'

View file

@ -3,6 +3,7 @@
from django.contrib import admin
from .models import Notification
class NotificationAdmin(admin.ModelAdmin):
list_display = ('recipient', 'actor',
'level', 'target', 'unread', 'public')

11
notifications/apps.py Normal file
View file

@ -0,0 +1,11 @@
from django.apps import AppConfig
class Config(AppConfig):
name = "notifications"
def ready(self):
super(Config, self).ready()
# this is for backwards compability
import notifications.signals
notifications.notify = notifications.signals.notify

View file

@ -2,7 +2,7 @@
from __future__ import unicode_literals
from django.db import models, migrations
import notifications.models
from django.utils import timezone
class Migration(migrations.Migration):
@ -15,6 +15,6 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='notification',
name='timestamp',
field=models.DateTimeField(default=notifications.models.now),
field=models.DateTimeField(default=timezone.now),
),
]

View file

@ -1,8 +1,8 @@
import datetime
from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django import get_version
from django.utils import timezone
from distutils.version import StrictVersion
if StrictVersion(get_version()) >= StrictVersion('1.8.0'):
@ -12,30 +12,20 @@ else:
from django.db import models
from django.core.exceptions import ImproperlyConfigured
from six import text_type
from django.utils.six import text_type
from .utils import id2slug
from .signals import notify
from model_utils import managers, Choices
from model_utils import Choices
from jsonfield.fields import JSONField
def now():
# Needs to be be a function as USE_TZ can change based on if we are testing or not.
_now = datetime.datetime.now
if getattr(settings, 'USE_TZ'):
try:
from django.utils import timezone
_now = timezone.now
except ImportError:
pass
return _now()
from django.contrib.auth.models import Group
#SOFT_DELETE = getattr(settings, 'NOTIFICATIONS_SOFT_DELETE', False)
# SOFT_DELETE = getattr(settings, 'NOTIFICATIONS_SOFT_DELETE', False)
def is_soft_delete():
#TODO: SOFT_DELETE = getattr(settings, ...) doesn't work with "override_settings" decorator in unittest
# TODO: SOFT_DELETE = getattr(settings, ...) doesn't work with "override_settings" decorator in unittest
# But is_soft_delete is neither a very elegant way. Should try to find better approach
return getattr(settings, 'NOTIFICATIONS_SOFT_DELETE', False)
@ -170,27 +160,23 @@ class Notification(models.Model):
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_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 = GenericForeignKey('target_content_type',
'target_object_id')
target = 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 = GenericForeignKey('action_object_content_type',
'action_object_object_id')
action_object_content_type = models.ForeignKey(ContentType, blank=True, null=True,
related_name='notify_action_object')
action_object_object_id = models.CharField(max_length=255, blank=True, null=True)
action_object = GenericForeignKey('action_object_content_type', 'action_object_object_id')
timestamp = models.DateTimeField(default=now)
timestamp = models.DateTimeField(default=timezone.now)
public = models.BooleanField(default=True)
deleted = models.BooleanField(default=False)
emailed = models.BooleanField(default=False)
data = JSONField(blank=True, null=True)
objects = managers.PassThroughManager.for_queryset_class(NotificationQuerySet)()
objects = NotificationQuerySet.as_manager()
class Meta:
ordering = ('-timestamp', )
@ -212,7 +198,7 @@ class Notification(models.Model):
return u'%(actor)s %(verb)s %(action_object)s %(timesince)s ago' % ctx
return u'%(actor)s %(verb)s %(timesince)s ago' % ctx
def __str__(self):#Adds support for Python 3
def __str__(self): # Adds support for Python 3
return self.__unicode__()
def timesince(self, now=None):
@ -249,31 +235,48 @@ def notify_handler(verb, **kwargs):
Handler function to create Notification instance upon action signal call.
"""
# Pull the options out of kwargs
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=text_type(verb),
public=bool(kwargs.pop('public', True)),
description=kwargs.pop('description', None),
timestamp=kwargs.pop('timestamp', now()),
level=kwargs.pop('level', Notification.LEVELS.info),
)
optional_objs = [
(kwargs.pop(opt, None), opt)
for opt in ('target', 'action_object')
]
public = bool(kwargs.pop('public', True))
description = kwargs.pop('description', None)
timestamp = kwargs.pop('timestamp', timezone.now())
level = kwargs.pop('level', Notification.LEVELS.info)
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))
# Check if User or Group
if isinstance(recipient, Group):
recipients = recipient.user_set.all()
else:
recipients = [recipient]
if len(kwargs) and EXTRA_DATA:
newnotify.data = kwargs
for recipient in recipients:
newnotify = Notification(
recipient=recipient,
actor_content_type=ContentType.objects.get_for_model(actor),
actor_object_id=actor.pk,
verb=text_type(verb),
public=public,
description=description,
timestamp=timestamp,
level=level,
)
newnotify.save()
# Set optional objects
for obj, opt in optional_objs:
if obj is not None:
setattr(newnotify, '%s_object_id' % opt, obj.pk)
setattr(newnotify, '%s_content_type' % opt,
ContentType.objects.get_for_model(obj))
if len(kwargs) and EXTRA_DATA:
newnotify.data = kwargs
newnotify.save()
# connect the signal

View file

@ -1,96 +0,0 @@
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
try:
from django.contrib.auth import get_user_model
except ImportError: # django < 1.5
from django.contrib.auth.models import User
else:
User = get_user_model()
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding model 'Notification'
db.create_table('notifications_notification', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('recipient', self.gf('django.db.models.fields.related.ForeignKey')(related_name='notifications', to=User)),
('readed', self.gf('django.db.models.fields.BooleanField')(default=False)),
('actor_content_type', self.gf('django.db.models.fields.related.ForeignKey')(related_name='notify_actor', to=orm['contenttypes.ContentType'])),
('actor_object_id', self.gf('django.db.models.fields.CharField')(max_length=255)),
('verb', self.gf('django.db.models.fields.CharField')(max_length=255)),
('description', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
('target_content_type', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='notify_target', null=True, to=orm['contenttypes.ContentType'])),
('target_object_id', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
('action_object_content_type', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='notify_action_object', null=True, to=orm['contenttypes.ContentType'])),
('action_object_object_id', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
('timestamp', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now)),
('public', self.gf('django.db.models.fields.BooleanField')(default=True)),
))
db.send_create_signal('notifications', ['Notification'])
def backwards(self, orm):
# Deleting model 'Notification'
db.delete_table('notifications_notification')
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'notifications.notification': {
'Meta': {'ordering': "('-timestamp',)", 'object_name': 'Notification'},
'action_object_content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'notify_action_object'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}),
'action_object_object_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'actor_content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'notify_actor'", 'to': "orm['contenttypes.ContentType']"}),
'actor_object_id': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'readed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'recipient': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'notifications'", 'to': "orm['auth.User']"}),
'target_content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'notify_target'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}),
'target_object_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'timestamp': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'verb': ('django.db.models.fields.CharField', [], {'max_length': '255'})
}
}
complete_apps = ['notifications']

View file

@ -1,78 +0,0 @@
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding field 'Notification.data'
db.add_column('notifications_notification', 'data',
self.gf('django.db.models.fields.TextField')(null=True, blank=True),
keep_default=False)
def backwards(self, orm):
# Deleting field 'Notification.data'
db.delete_column('notifications_notification', 'data')
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'notifications.notification': {
'Meta': {'ordering': "('-timestamp',)", 'object_name': 'Notification'},
'action_object_content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'notify_action_object'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}),
'action_object_object_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'actor_content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'notify_actor'", 'to': "orm['contenttypes.ContentType']"}),
'actor_object_id': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'data': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'readed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'recipient': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'notifications'", 'to': "orm['auth.User']"}),
'target_content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'notify_target'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}),
'target_object_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'timestamp': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'verb': ('django.db.models.fields.CharField', [], {'max_length': '255'})
}
}
complete_apps = ['notifications']

View file

@ -1,79 +0,0 @@
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding field 'Notification.unread'
db.add_column('notifications_notification', 'unread',
self.gf('django.db.models.fields.BooleanField')(default=True),
keep_default=True)
def backwards(self, orm):
# Deleting field 'Notification.unread'
db.delete_column('notifications_notification', 'unread')
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'notifications.notification': {
'Meta': {'ordering': "('-timestamp',)", 'object_name': 'Notification'},
'action_object_content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'notify_action_object'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}),
'action_object_object_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'actor_content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'notify_actor'", 'to': "orm['contenttypes.ContentType']"}),
'actor_object_id': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'data': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'readed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'recipient': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'notifications'", 'to': "orm['auth.User']"}),
'target_content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'notify_target'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}),
'target_object_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'timestamp': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'unread': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'verb': ('django.db.models.fields.CharField', [], {'max_length': '255'})
}
}
complete_apps = ['notifications']

View file

@ -1,77 +0,0 @@
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import DataMigration
from django.db import models
class Migration(DataMigration):
def forwards(self, orm):
""
orm['notifications.Notification'].objects.filter(readed=True).update(unread=False)
orm['notifications.Notification'].objects.filter(readed=False).update(unread=True)
def backwards(self, orm):
"Write your backwards methods here."
orm['notifications.Notification'].objects.filter(unread=True).update(readed=False)
orm['notifications.Notification'].objects.filter(unread=False).update(readed=True)
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'notifications.notification': {
'Meta': {'ordering': "('-timestamp',)", 'object_name': 'Notification'},
'action_object_content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'notify_action_object'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}),
'action_object_object_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'actor_content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'notify_actor'", 'to': "orm['contenttypes.ContentType']"}),
'actor_object_id': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'data': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'readed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'recipient': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'notifications'", 'to': "orm['auth.User']"}),
'target_content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'notify_target'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}),
'target_object_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'timestamp': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'unread': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'verb': ('django.db.models.fields.CharField', [], {'max_length': '255'})
}
}
complete_apps = ['notifications']
symmetrical = True

View file

@ -1,78 +0,0 @@
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Deleting field 'Notification.readed'
db.delete_column('notifications_notification', 'readed')
def backwards(self, orm):
# Adding field 'Notification.readed'
db.add_column('notifications_notification', 'readed',
self.gf('django.db.models.fields.BooleanField')(default=False),
keep_default=True)
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'notifications.notification': {
'Meta': {'ordering': "('-timestamp',)", 'object_name': 'Notification'},
'action_object_content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'notify_action_object'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}),
'action_object_object_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'actor_content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'notify_actor'", 'to': "orm['contenttypes.ContentType']"}),
'actor_object_id': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'data': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'recipient': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'notifications'", 'to': "orm['auth.User']"}),
'target_content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'notify_target'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}),
'target_object_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'timestamp': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'unread': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'verb': ('django.db.models.fields.CharField', [], {'max_length': '255'})
}
}
complete_apps = ['notifications']

View file

@ -1,79 +0,0 @@
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding field 'Notification.level'
db.add_column('notifications_notification', 'level',
self.gf('django.db.models.fields.CharField')(default='info', max_length=20),
keep_default=False)
def backwards(self, orm):
# Deleting field 'Notification.level'
db.delete_column('notifications_notification', 'level')
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'notifications.notification': {
'Meta': {'ordering': "('-timestamp',)", 'object_name': 'Notification'},
'action_object_content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'notify_action_object'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}),
'action_object_object_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'actor_content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'notify_actor'", 'to': "orm['contenttypes.ContentType']"}),
'actor_object_id': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'data': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'level': ('django.db.models.fields.CharField', [], {'default': "'info'", 'max_length': '20'}),
'public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'recipient': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'notifications'", 'to': "orm['auth.User']"}),
'target_content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'notify_target'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}),
'target_object_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'timestamp': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'unread': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'verb': ('django.db.models.fields.CharField', [], {'max_length': '255'})
}
}
complete_apps = ['notifications']

View file

@ -1,96 +0,0 @@
# -*- coding: utf-8 -*-
from south.utils import datetime_utils as datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Deleting field 'Notification.data'
db.delete_column(u'notifications_notification', 'data')
# Adding field 'Notification.deleted'
db.add_column(u'notifications_notification', 'deleted',
self.gf('django.db.models.fields.BooleanField')(default=False),
keep_default=False)
# Adding field 'Notification.emailed'
db.add_column(u'notifications_notification', 'emailed',
self.gf('django.db.models.fields.BooleanField')(default=False),
keep_default=False)
def backwards(self, orm):
# Adding field 'Notification.data'
db.add_column(u'notifications_notification', 'data',
self.gf('django.db.models.fields.TextField')(null=True, blank=True),
keep_default=False)
# Deleting field 'Notification.deleted'
db.delete_column(u'notifications_notification', 'deleted')
# Deleting field 'Notification.emailed'
db.delete_column(u'notifications_notification', 'emailed')
models = {
u'auth.group': {
'Meta': {'object_name': 'Group'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
u'auth.permission': {
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
u'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
u'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
u'notifications.notification': {
'Meta': {'ordering': "('-timestamp',)", 'object_name': 'Notification'},
'action_object_content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'notify_action_object'", 'null': 'True', 'to': u"orm['contenttypes.ContentType']"}),
'action_object_object_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'actor_content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'notify_actor'", 'to': u"orm['contenttypes.ContentType']"}),
'actor_object_id': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'emailed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'level': ('django.db.models.fields.CharField', [], {'default': "'info'", 'max_length': '20'}),
'public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'recipient': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'notifications'", 'to': u"orm['auth.User']"}),
'target_content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'notify_target'", 'null': 'True', 'to': u"orm['contenttypes.ContentType']"}),
'target_object_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'timestamp': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'unread': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'verb': ('django.db.models.fields.CharField', [], {'max_length': '255'})
}
}
complete_apps = ['notifications']

View file

@ -1,81 +0,0 @@
# -*- coding: utf-8 -*-
from south.utils import datetime_utils as datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding field 'Notification.data'
db.add_column(u'notifications_notification', 'data',
self.gf('jsonfield.fields.JSONField')(null=True, blank=True),
keep_default=False)
def backwards(self, orm):
# Deleting field 'Notification.data'
db.delete_column(u'notifications_notification', 'data')
models = {
u'auth.group': {
'Meta': {'object_name': 'Group'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
u'auth.permission': {
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
u'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
u'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
u'notifications.notification': {
'Meta': {'ordering': "('-timestamp',)", 'object_name': 'Notification'},
'action_object_content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'notify_action_object'", 'null': 'True', 'to': u"orm['contenttypes.ContentType']"}),
'action_object_object_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'actor_content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'notify_actor'", 'to': u"orm['contenttypes.ContentType']"}),
'actor_object_id': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'data': ('jsonfield.fields.JSONField', [], {'null': 'True', 'blank': 'True'}),
'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'emailed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'level': ('django.db.models.fields.CharField', [], {'default': "'info'", 'max_length': '20'}),
'public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'recipient': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'notifications'", 'to': u"orm['auth.User']"}),
'target_content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'notify_target'", 'null': 'True', 'to': u"orm['contenttypes.ContentType']"}),
'target_object_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'timestamp': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'unread': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'verb': ('django.db.models.fields.CharField', [], {'max_length': '255'})
}
}
complete_apps = ['notifications']

View file

@ -1,11 +1,10 @@
# -*- coding: utf-8 -*-
from django.core.urlresolvers import reverse
from django.template import Library
from django.template.base import TemplateSyntaxError
from django.template import Node
register = Library()
@register.assignment_tag(takes_context=True)
def notifications_unread(context):
user = user_context(context)
@ -13,34 +12,40 @@ def notifications_unread(context):
return ''
return user.notifications.unread().count()
# Requires vanilla-js framework - http://vanilla-js.com/
@register.simple_tag
def register_notify_callbacks(badge_id='live_notify_badge',menu_id='live_notify_list',refresh_period=15,callbacks="",api_name='list',fetch=5):
refresh_period=int(refresh_period)*1000
def register_notify_callbacks(badge_id='live_notify_badge',
menu_id='live_notify_list',
refresh_period=15,
callbacks='',
api_name='list',
fetch=5):
refresh_period = int(refresh_period)*1000
if api_name=='list':
if api_name == 'list':
api_url = reverse('notifications:live_unread_notification_list')
elif api_name=='count':
elif api_name == 'count':
api_url = reverse('notifications:live_unread_notification_count')
else:
return ""
definitions="""
definitions = """
notify_badge_id='{badge_id}';
notify_menu_id='{menu_id}';
notify_api_url='{api_url}';
notify_fetch_count='{fetch_count}';
notify_unread_url='{unread_url}';
notify_mark_all_unread_url='{mark_all_unread_url}';
notify_refresh_period={refresh};""".format(
badge_id=badge_id,
menu_id=menu_id,
refresh=refresh_period,
api_url=api_url,
unread_url=reverse('notifications:unread'),
mark_all_unread_url=reverse('notifications:mark_all_as_read'),
fetch_count=fetch
)
notify_refresh_period={refresh};
""".format(
badge_id=badge_id,
menu_id=menu_id,
refresh=refresh_period,
api_url=api_url,
unread_url=reverse('notifications:unread'),
mark_all_unread_url=reverse('notifications:mark_all_as_read'),
fetch_count=fetch
)
script = "<script>"+definitions
for callback in callbacks.split(','):
@ -50,19 +55,23 @@ def register_notify_callbacks(badge_id='live_notify_badge',menu_id='live_notify_
@register.simple_tag(takes_context=True)
def live_notify_badge(context,badge_id='live_notify_badge',classes=""):
def live_notify_badge(context, badge_id='live_notify_badge', classes=""):
user = user_context(context)
if not user:
return ''
html="<span id='{badge_id}' class='{classes}'>{unread}</span>".format(badge_id=badge_id,classes=classes,unread=user.notifications.unread().count())
html = "<span id='{badge_id}' class='{classes}'>{unread}</span>".format(
badge_id=badge_id, classes=classes, unread=user.notifications.unread().count()
)
return html
@register.simple_tag
def live_notify_list(list_id='live_notify_list',classes=""):
html="<ul id='{list_id}' class='{classes}'></ul>".format(list_id=list_id,classes=classes)
def live_notify_list(list_id='live_notify_list', classes=""):
html = "<ul id='{list_id}' class='{classes}'></ul>".format(list_id=list_id, classes=classes)
return html
def user_context(context):
if 'user' not in context:
return None
@ -71,4 +80,4 @@ def user_context(context):
user = request.user
if user.is_anonymous():
return None
return user
return user

View file

@ -1 +0,0 @@
from .tests import *

View file

@ -1,7 +1,7 @@
import os
BASE_DIR = os.path.abspath(os.path.dirname(__file__))
SECRET_KEY = 'secret_key'
SOUTH_TESTS_MIGRATE = True
DEBUG = True
TESTING = True
@ -9,7 +9,7 @@ DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': 'test.sqlite3',
}
}
}
@ -35,14 +35,30 @@ STATICFILES_DIRS = [
]
STATIC_ROOT = os.path.join(BASE_DIR, "static-files")
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
# Need to skip migrations for now as migrations created with python2 break with python3
# See https://code.djangoproject.com/ticket/23455
class DisableMigrations(object):
def __contains__(self, item):
return True
def __getitem__(self, item):
return "notmigrations"
MIGRATION_MODULES = DisableMigrations()

View file

@ -12,7 +12,7 @@ except ImportError:
from django.test.utils import override_settings
from django.conf import settings
from django.contrib.auth.models import User
from django.contrib.auth.models import User, Group
from django.core.exceptions import ImproperlyConfigured
from django.core.urlresolvers import reverse
from django.utils.timezone import utc, localtime
@ -34,10 +34,11 @@ class NotificationTest(TestCase):
to_user = User.objects.create(username="to", password="pwd", email="example@example.com")
notify.send(from_user, recipient=to_user, verb='commented', action_object=from_user)
notification = Notification.objects.get(recipient=to_user)
delta = timezone.now().replace(tzinfo=utc) - localtime(notification.timestamp,pytz.timezone(settings.TIME_ZONE))
delta = timezone.now().replace(tzinfo=utc) - localtime(notification.timestamp, pytz.timezone(settings.TIME_ZONE))
self.assertTrue(delta.seconds < 60)
# The delta between the two events will still be less than a second despite the different timezones
# The call to now and the immediate call afterwards will be within a short period of time, not 8 hours as the test above was originally.
# The call to now and the immediate call afterwards will be within a short period of time, not 8 hours as the
# test above was originally.
@override_settings(USE_TZ=False)
@override_settings(TIME_ZONE='Asia/Shanghai')
@ -56,8 +57,13 @@ class NotificationManagersTest(TestCase):
self.message_count = 10
self.from_user = User.objects.create(username="from2", password="pwd", email="example@example.com")
self.to_user = User.objects.create(username="to2", password="pwd", email="example@example.com")
self.to_group = Group.objects.create(name="to2_g")
self.to_group.user_set.add(self.to_user)
for i in range(self.message_count):
notify.send(self.from_user, recipient=self.to_user, verb='commented', action_object=self.from_user)
# Send notification to group
notify.send(self.from_user, recipient=self.to_group, verb='commented', action_object=self.from_user)
self.message_count += 1
def test_unread_manager(self):
self.assertEqual(Notification.objects.unread().count(), self.message_count)
@ -71,19 +77,19 @@ class NotificationManagersTest(TestCase):
self.assertEqual(Notification.objects.unread().count(), self.message_count)
n = Notification.objects.filter(recipient=self.to_user).first()
n.mark_as_read()
self.assertEqual(Notification.objects.read().count(),1)
self.assertEqual(Notification.objects.read().count(), 1)
for n in Notification.objects.read():
self.assertFalse(n.unread)
def test_mark_all_as_read_manager(self):
self.assertEqual(Notification.objects.unread().count(), self.message_count)
Notification.objects.filter(recipient=self.to_user).mark_all_as_read()
self.assertEqual(Notification.objects.unread().count(),0)
self.assertEqual(Notification.objects.unread().count(), 0)
def test_mark_all_as_unread_manager(self):
self.assertEqual(Notification.objects.unread().count(), self.message_count)
Notification.objects.filter(recipient=self.to_user).mark_all_as_read()
self.assertEqual(Notification.objects.unread().count(),0)
self.assertEqual(Notification.objects.unread().count(), 0)
Notification.objects.filter(recipient=self.to_user).mark_all_as_unread()
self.assertEqual(Notification.objects.unread().count(), self.message_count)
@ -129,54 +135,60 @@ class NotificationTestPages(TestCase):
def logout(self):
self.client.post(reverse('admin:logout')+'?next=/', {})
def login(self,username,password):
def login(self, username, password):
self.logout()
response = self.client.post(reverse('login'), {'username': username, 'password': password})
self.assertEqual(response.status_code,302)
self.assertEqual(response.status_code, 302)
return response
def test_all_messages_page(self):
self.login('to','pwd')
self.login('to', 'pwd')
response = self.client.get(reverse('notifications:all'))
self.assertEqual(response.status_code,200)
self.assertEqual(len(response.context['notifications']),len(self.to_user.notifications.all()))
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.context['notifications']), len(self.to_user.notifications.all()))
def test_unread_messages_pages(self):
self.login('to','pwd')
self.login('to', 'pwd')
response = self.client.get(reverse('notifications:unread'))
self.assertEqual(response.status_code,200)
self.assertEqual(len(response.context['notifications']),len(self.to_user.notifications.unread()))
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.context['notifications']), len(self.to_user.notifications.unread()))
self.assertEqual(len(response.context['notifications']), self.message_count)
for i,n in enumerate(self.to_user.notifications.all()):
if i%3 == 0:
response = self.client.get(reverse('notifications:mark_as_read',args=[id2slug(n.id)]))
self.assertEqual(response.status_code,302)
for i, n in enumerate(self.to_user.notifications.all()):
if i % 3 == 0:
response = self.client.get(reverse('notifications:mark_as_read', args=[id2slug(n.id)]))
self.assertEqual(response.status_code, 302)
response = self.client.get(reverse('notifications:unread'))
self.assertEqual(response.status_code,200)
self.assertEqual(len(response.context['notifications']),len(self.to_user.notifications.unread()))
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.context['notifications']), len(self.to_user.notifications.unread()))
self.assertTrue(len(response.context['notifications']) < self.message_count)
response = self.client.get(reverse('notifications:mark_all_as_read'))
self.assertRedirects(response,reverse('notifications:all'))
self.assertRedirects(response, reverse('notifications:all'))
response = self.client.get(reverse('notifications:unread'))
self.assertEqual(len(response.context['notifications']),len(self.to_user.notifications.unread()))
self.assertEqual(len(response.context['notifications']),0)
self.assertEqual(len(response.context['notifications']), len(self.to_user.notifications.unread()))
self.assertEqual(len(response.context['notifications']), 0)
def test_next_pages(self):
self.login('to','pwd')
response = self.client.get(reverse('notifications:mark_all_as_read')+"?next="+reverse('notifications:unread'))
self.assertRedirects(response,reverse('notifications:unread'))
self.login('to', 'pwd')
response = self.client.get(reverse('notifications:mark_all_as_read'), data={
"next": reverse('notifications:unread'),
})
self.assertRedirects(response, reverse('notifications:unread'))
slug = id2slug(self.to_user.notifications.first().id)
response = self.client.get(reverse('notifications:mark_as_read',args=[slug])+"?next="+reverse('notifications:unread'))
self.assertRedirects(response,reverse('notifications:unread'))
response = self.client.get(reverse('notifications:mark_as_read', args=[slug]), data={
"next": reverse('notifications:unread'),
})
self.assertRedirects(response, reverse('notifications:unread'))
slug = id2slug(self.to_user.notifications.first().id)
response = self.client.get(reverse('notifications:mark_as_unread',args=[slug])+"?next="+reverse('notifications:unread'))
self.assertRedirects(response,reverse('notifications:unread'))
response = self.client.get(reverse('notifications:mark_as_unread', args=[slug]), {
"next": reverse('notifications:unread'),
})
self.assertRedirects(response, reverse('notifications:unread'))
def test_delete_messages_pages(self):
self.login('to', 'pwd')
@ -213,64 +225,64 @@ class NotificationTestPages(TestCase):
self.assertEqual(len(response.context['notifications']), len(self.to_user.notifications.unread()))
self.assertEqual(len(response.context['notifications']), self.message_count-1)
def test_unread_count_api(self):
self.login('to', 'pwd')
response = self.client.get(reverse('notifications:live_unread_notification_count'))
data = json.loads(response.content.decode('utf-8'))
self.assertEqual(list(data.keys()),['unread_count'])
self.assertEqual(data['unread_count'],10)
self.assertEqual(list(data.keys()), ['unread_count'])
self.assertEqual(data['unread_count'], 10)
Notification.objects.filter(recipient=self.to_user).mark_all_as_read()
response = self.client.get(reverse('notifications:live_unread_notification_count'))
data = json.loads(response.content.decode('utf-8'))
self.assertEqual(list(data.keys()),['unread_count'])
self.assertEqual(data['unread_count'],0)
self.assertEqual(list(data.keys()), ['unread_count'])
self.assertEqual(data['unread_count'], 0)
notify.send(self.from_user, recipient=self.to_user, verb='commented', action_object=self.from_user)
response = self.client.get(reverse('notifications:live_unread_notification_count'))
data = json.loads(response.content.decode('utf-8'))
self.assertEqual(list(data.keys()),['unread_count'])
self.assertEqual(data['unread_count'],1)
self.assertEqual(list(data.keys()), ['unread_count'])
self.assertEqual(data['unread_count'], 1)
def test_unread_list_api(self):
self.login('to', 'pwd')
response = self.client.get(reverse('notifications:live_unread_notification_list'))
data = json.loads(response.content.decode('utf-8'))
self.assertEqual(sorted(list(data.keys())),['unread_count','unread_list'])
self.assertEqual(data['unread_count'],10)
self.assertEqual(len(data['unread_list']),5)
self.assertEqual(sorted(list(data.keys())), ['unread_count', 'unread_list'])
self.assertEqual(data['unread_count'], 10)
self.assertEqual(len(data['unread_list']), 5)
response = self.client.get(reverse('notifications:live_unread_notification_list')+"?max=12")
response = self.client.get(reverse('notifications:live_unread_notification_list'), data={"max": "12"})
data = json.loads(response.content.decode('utf-8'))
self.assertEqual(sorted(list(data.keys())),['unread_count','unread_list'])
self.assertEqual(data['unread_count'],10)
self.assertEqual(len(data['unread_list']),10)
self.assertEqual(sorted(list(data.keys())), ['unread_count', 'unread_list'])
self.assertEqual(data['unread_count'], 10)
self.assertEqual(len(data['unread_list']), 10)
# Test with a bad 'max' value
response = self.client.get(reverse('notifications:live_unread_notification_list')+"?max=this_is_wrong")
response = self.client.get(reverse('notifications:live_unread_notification_list'), data={
"max": "this_is_wrong",
})
data = json.loads(response.content.decode('utf-8'))
self.assertEqual(sorted(list(data.keys())),['unread_count','unread_list'])
self.assertEqual(data['unread_count'],10)
self.assertEqual(len(data['unread_list']),5)
self.assertEqual(sorted(list(data.keys())), ['unread_count', 'unread_list'])
self.assertEqual(data['unread_count'], 10)
self.assertEqual(len(data['unread_list']), 5)
Notification.objects.filter(recipient=self.to_user).mark_all_as_read()
response = self.client.get(reverse('notifications:live_unread_notification_list'))
data = json.loads(response.content.decode('utf-8'))
self.assertEqual(sorted(list(data.keys())),['unread_count','unread_list'])
self.assertEqual(data['unread_count'],0)
self.assertEqual(len(data['unread_list']),0)
self.assertEqual(sorted(list(data.keys())), ['unread_count', 'unread_list'])
self.assertEqual(data['unread_count'], 0)
self.assertEqual(len(data['unread_list']), 0)
notify.send(self.from_user, recipient=self.to_user, verb='commented', action_object=self.from_user)
response = self.client.get(reverse('notifications:live_unread_notification_list'))
data = json.loads(response.content.decode('utf-8'))
self.assertEqual(sorted(list(data.keys())),['unread_count','unread_list'])
self.assertEqual(data['unread_count'],1)
self.assertEqual(len(data['unread_list']),1)
self.assertEqual(data['unread_list'][0]['verb'],'commented')
self.assertEqual(sorted(list(data.keys())), ['unread_count', 'unread_list'])
self.assertEqual(data['unread_count'], 1)
self.assertEqual(len(data['unread_list']), 1)
self.assertEqual(data['unread_list'][0]['verb'], 'commented')
def test_live_update_tags(self):
from django.shortcuts import render
@ -281,9 +293,9 @@ class NotificationTestPages(TestCase):
request = self.factory.get('/notification/live_updater')
request.user = self.to_user
page = render(request, 'notifications/test_tags.html', {'request':request})
#TODO: Add more tests to check what is being output.
render(request, 'notifications/test_tags.html', {'request': request})
# TODO: Add more tests to check what is being output.
def test_anon_user_gets_nothing(self):
response = self.client.post(reverse('notifications:live_unread_notification_count'))
@ -296,4 +308,3 @@ class NotificationTestPages(TestCase):
data = json.loads(response.content.decode('utf-8'))
self.assertEqual(data['unread_count'],0)
self.assertEqual(data['unread_list'],[])

View file

@ -1,14 +1,16 @@
# -*- coding: utf-8 -*-
from django.conf.urls import include, patterns, url
from django.conf.urls import include, url
from django.contrib import admin
from django.contrib.auth.views import login
from notifications import urls
import notifications.urls
import notifications.tests.views
urlpatterns = patterns('',
url(r'^login/$', 'django.contrib.auth.views.login', name='login'), # needed for Django 1.6 tests
urlpatterns = [
url(r'^login/$', login, name='login'), # needed for Django 1.6 tests
url(r'^admin/', include(admin.site.urls)),
url(r'^test_make/', 'notifications.tests.views.make_notification'),
url(r'^test/', 'notifications.tests.views.live_tester'),
url(r'^', include(urls, 'notifications')),
)
url(r'^test_make/', notifications.tests.views.make_notification),
url(r'^test/', notifications.tests.views.live_tester),
url(r'^', include(notifications.urls, namespace='notifications')),
]

View file

@ -1,19 +1,18 @@
from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.shortcuts import get_object_or_404, render
from django.shortcuts import render
from notifications import notify
import random
def live_tester(request):
notify.send(sender=request.user, recipient=request.user, verb='you loaded the page')
data = {
return render(request, 'test_live.html', {
'unread_count': request.user.notifications.unread().count(),
'notifications': request.user.notifications.all()
}
return render(request,'test_live.html',data)
})
def make_notification(request):
the_notification = random.choice([
@ -22,7 +21,7 @@ def make_notification(request):
'jumping the shark',
'testing the app',
'attaching the plumbus',
])
notify.send(sender=request.user, recipient=request.user, verb='you asked for a notification - you are '+the_notification)
])
notify.send(sender=request.user, recipient=request.user,
verb='you asked for a notification - you are ' + the_notification)

View file

@ -1,17 +1,18 @@
# -*- coding: utf-8 -*-
from django.conf.urls import patterns, url
from django.conf.urls import url
from . import views
urlpatterns = patterns('notifications.views',
app_name = 'notifications'
urlpatterns = [
url(r'^$', views.AllNotificationsList.as_view(), name='all'),
url(r'^unread/$', views.UnreadNotificationsList.as_view(), name='unread'),
url(r'^mark-all-as-read/$', 'mark_all_as_read', name='mark_all_as_read'),
url(r'^mark-as-read/(?P<slug>\d+)/$', 'mark_as_read', name='mark_as_read'),
url(r'^mark-as-unread/(?P<slug>\d+)/$', 'mark_as_unread', name='mark_as_unread'),
url(r'^delete/(?P<slug>\d+)/$', 'delete', name='delete'),
url(r'^api/unread_count/$', 'live_unread_notification_count', name='live_unread_notification_count'),
url(r'^api/unread_list/$', 'live_unread_notification_list', name='live_unread_notification_list'),
)
url(r'^mark-all-as-read/$', views.mark_all_as_read, name='mark_all_as_read'),
url(r'^mark-as-read/(?P<slug>\d+)/$', views.mark_as_read, name='mark_as_read'),
url(r'^mark-as-unread/(?P<slug>\d+)/$', views.mark_as_unread, name='mark_as_unread'),
url(r'^delete/(?P<slug>\d+)/$', views.delete, name='delete'),
url(r'^api/unread_count/$', views.live_unread_notification_count, name='live_unread_notification_count'),
url(r'^api/unread_list/$', views.live_unread_notification_list, name='live_unread_notification_list'),
]

View file

@ -4,8 +4,10 @@ import sys
if sys.version > '3':
long = int
def slug2id(slug):
return long(slug) - 110909
def id2slug(id):
return id + 110909

View file

@ -140,8 +140,8 @@ def live_unread_notification_list(request):
try:
num_to_fetch = request.GET.get('max', 5) # If they don't specify, make it 5.
num_to_fetch = int(num_to_fetch)
num_to_fetch = max(1,num_to_fetch) # if num_to_fetch is negative, force at least one fetched notifications
num_to_fetch = min(num_to_fetch,100) # put a sane ceiling on the number retrievable
num_to_fetch = max(1, num_to_fetch) # if num_to_fetch is negative, force at least one fetched notifications
num_to_fetch = min(num_to_fetch, 100) # put a sane ceiling on the number retrievable
except ValueError:
num_to_fetch = 5 # If casting to an int fails, just make it 5.
@ -157,8 +157,7 @@ def live_unread_notification_list(request):
struct['action_object'] = str(n.action_object)
unread_list.append(struct)
data = {
'unread_count':request.user.notifications.unread().count(),
'unread_list':unread_list
'unread_count': request.user.notifications.unread().count(),
'unread_list': unread_list
}
return JsonResponse(data)

113
setup.py
View file

@ -1,66 +1,63 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from distutils.core import setup
from version import __version__
import re
import ast
from setuptools import setup
setup(name='django-notifications-hq',
version=__version__,
description='GitHub notifications alike app for Django.',
_version_re = re.compile(r'__version__\s+=\s+(.*)')
long_description=open('README.rst').read(),
with open('notifications/__init__.py', 'rb') as f:
version = str(ast.literal_eval(_version_re.search(
f.read().decode('utf-8')).group(1)))
author='django-notifications team',
author_email='yang@yangyubo.com',
url='http://github.com/django-notifications/django-notifications',
install_requires=[
'django>=1.4',
'django-model-utils>=2.0.3,<2.4',
'six>=1.9.0',
'jsonfield>=1.0.3',
],
test_requires=[
'django>=1.4',
'django-model-utils>=2.0.3,<2.4',
'six>=1.9.0',
'jsonfield>=1.0.3',
'pytz'
],
packages=['notifications',
'notifications.templatetags',
'notifications.migrations',
'notifications.south_migrations'
],
package_data={'notifications': [
'templates/notifications/*.html', 'static/notifications/*.js']},
classifiers=['Development Status :: 5 - Production/Stable',
'Environment :: Web Environment',
'Framework :: Django',
'Intended Audience :: Developers',
'License :: OSI Approved :: BSD License',
'Operating System :: OS Independent',
# Specify the Python versions you support here. In particular, ensure
# that you indicate whether you support Python 2, Python 3 or both.
'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.2',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
'Topic :: Utilities'
],
keywords='django notifications github action event stream',
license='MIT',
)
setup(
name='django-notifications-hq',
version=version,
description='GitHub notifications alike app for Django.',
long_description=open('README.rst').read(),
author='django-notifications team',
author_email='yang@yangyubo.com',
url='http://github.com/django-notifications/django-notifications',
install_requires=[
'django>=1.7',
'django-model-utils>=2.0.3',
'jsonfield>=1.0.3',
'pytz'
],
test_requires=[
'django>=1.7',
'django-model-utils>=2.0.3',
'jsonfield>=1.0.3',
'pytz'
],
packages=[
'notifications',
'notifications.templatetags',
'notifications.migrations',
],
package_data={
'notifications': ['templates/notifications/*.html', 'static/notifications/*.js']
},
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Web Environment',
'Framework :: Django',
'Intended Audience :: Developers',
'License :: OSI Approved :: BSD License',
'Operating System :: OS Independent',
# Specify the Python versions you support here. In particular, ensure
# that you indicate whether you support Python 2, Python 3 or both.
'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
'Topic :: Utilities'
],
keywords='django notifications github action event stream',
license='MIT',
)

View file

@ -1,20 +0,0 @@
__version_info__ = {
'major': 0,
'minor': 7,
'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()