mirror of
https://github.com/Hopiu/django-notifications.git
synced 2026-03-16 21:30:24 +00:00
Merge branch 'master' into iss169
This commit is contained in:
commit
3d14fd6b1a
10 changed files with 127 additions and 41 deletions
|
|
@ -8,6 +8,7 @@ env:
|
|||
- DJANGO=1.7
|
||||
- DJANGO=1.8
|
||||
- DJANGO=1.9
|
||||
- DJANGO=1.10
|
||||
install:
|
||||
# command to install dependencies
|
||||
- "pip install coveralls"
|
||||
|
|
@ -23,5 +24,7 @@ matrix:
|
|||
env: DJANGO=1.7
|
||||
- python: "3.3"
|
||||
env: DJANGO=1.9
|
||||
- python: "3.3"
|
||||
env: DJANGO=1.10
|
||||
after_success:
|
||||
- coveralls
|
||||
|
|
|
|||
|
|
@ -1,6 +1,15 @@
|
|||
Changelog
|
||||
=========
|
||||
|
||||
1.2
|
||||
-----
|
||||
|
||||
- Django 1.9 template tag compatibility: due to ``register.simple_tag`` automatically espacing ``unsafe_html`` in Django 1.9, it is now recommended to use format_html (@ikkebr)
|
||||
- Fixed parameter name error in README.rst: there is no to_fetch parameter, the correct name is fetch (@ikkebr)
|
||||
- Add missing migration (@marcgibbons)
|
||||
- Minor documentation correction (@tkwon, @zhang-z)
|
||||
- Return updated count in QuerySet (@zhang-z)
|
||||
|
||||
1.1
|
||||
-----
|
||||
|
||||
|
|
@ -91,4 +100,3 @@ Fix package descriptions and doc links.
|
|||
---
|
||||
|
||||
First version based on `django-activity-stream <https://github.com/justquick/django-activity-stream>`_ v0.4.3
|
||||
|
||||
|
|
|
|||
35
README.rst
35
README.rst
|
|
@ -29,7 +29,7 @@ Requirements
|
|||
============
|
||||
|
||||
- Python 2.7, 3.3, 3.4, 3.5
|
||||
- Django 1.7, 1.8, 1.9
|
||||
- Django 1.7, 1.8, 1.9, 1.10
|
||||
|
||||
Installation
|
||||
============
|
||||
|
|
@ -239,18 +239,25 @@ for updating specific fields within a django template.
|
|||
|
||||
There are two possible API calls that can be made:
|
||||
|
||||
1. ``api/unread_count/`` that returns a javascript object with 1 key: ``unread_count`` eg::
|
||||
1. ``api/unread_count/`` that returns a javascript object with 1 key: ``unread_count`` eg::
|
||||
|
||||
{"unread_count":1}
|
||||
|
||||
#. ``api/unread_list/`` that returns a javascript object with 2 keys: `unread_count` and `unread_list` eg::
|
||||
#. ``api/unread_list/`` that returns a javascript object with 2 keys: `unread_count` and `unread_list` eg::
|
||||
|
||||
{
|
||||
"unread_count":1,
|
||||
"unread_list":[--list of json representations of notifications--]
|
||||
}
|
||||
{
|
||||
"unread_count":1,
|
||||
"unread_list":[--list of json representations of notifications--]
|
||||
}
|
||||
|
||||
Representations of notifications are based on the django method: ``model_to_dict``
|
||||
Representations of notifications are based on the django method: ``model_to_dict``
|
||||
|
||||
Query string arguments:
|
||||
|
||||
- **max** - maximum length of unread list.
|
||||
- **mark_as_read** - mark notification in list as read.
|
||||
|
||||
For example, get ``api/unread_list/?max=3&mark_as_read=true`` returns 3 notifications and mark them read (remove from list on next request).
|
||||
|
||||
|
||||
How to use:
|
||||
|
|
@ -269,7 +276,7 @@ How to use:
|
|||
#. ``refresh_period`` (default ``15``) - How often to fetch unread items from the server (integer in seconds).
|
||||
#. ``fetch`` (default ``5``) - How many notifications to fetch each time.
|
||||
#. ``callbacks`` (default ``<empty string>``) - A comma-separated list of javascript functions to call each period.
|
||||
#. ``api_url_name`` (default ``list``) - The name of the API to call (this can be either ``list`` or ``count``).
|
||||
#. ``api_name`` (default ``list``) - The name of the API to call (this can be either ``list`` or ``count``).
|
||||
|
||||
3. To insert a live-updating unread count, use the following template::
|
||||
|
||||
|
|
@ -280,7 +287,7 @@ How to use:
|
|||
1. ``badge_id`` (default ``live_notify_badge``) - The ``id`` attribute for the ``<span>`` element that will be created to show the unread count.
|
||||
#. ``classes`` (default ``<empty string>``) - A string used to populate the ``class`` attribute of the above element.
|
||||
|
||||
4. To insert a live-updating unread count, use the following template::
|
||||
4. To insert a live-updating unread list, use the following template::
|
||||
|
||||
{% live_notify_list %}
|
||||
|
||||
|
|
@ -336,6 +343,14 @@ Testing the live-updater
|
|||
4. Browse to `yourserverip/test/`
|
||||
5. Click 'Make a notification' and a new notification should appear in the list in 5-10 seconds.
|
||||
|
||||
Notes
|
||||
=====
|
||||
|
||||
Email Notification
|
||||
------------------
|
||||
|
||||
Sending email to users has not been integrated into this library. So for now you need to implement it if needed. There is a reserved field `Notification.emailed` to make it easier.
|
||||
|
||||
|
||||
``django-notifications`` Team
|
||||
==============================
|
||||
|
|
|
|||
|
|
@ -8,6 +8,6 @@
|
|||
"""
|
||||
|
||||
# PEP 386-compliant version number: N.N[.N]+[{a|b|c|rc}N[.N]+][.postN][.devN]
|
||||
__version__ = '1.1'
|
||||
__version__ = '1.2'
|
||||
|
||||
default_app_config = 'notifications.apps.Config'
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ from .models import Notification
|
|||
|
||||
|
||||
class NotificationAdmin(admin.ModelAdmin):
|
||||
raw_id_fields = ('recipient', )
|
||||
list_display = ('recipient', 'actor',
|
||||
'level', 'target', 'unread', 'public')
|
||||
list_filter = ('level', 'unread', 'public', 'timestamp', )
|
||||
|
|
|
|||
|
|
@ -41,6 +41,12 @@ def assert_soft_delete():
|
|||
|
||||
class NotificationQuerySet(models.query.QuerySet):
|
||||
|
||||
def unsent(self):
|
||||
return self.filter(emailed=False)
|
||||
|
||||
def sent(self):
|
||||
return self.filter(emailed=True)
|
||||
|
||||
def unread(self, include_deleted=False):
|
||||
"""Return only unread items in the current queryset"""
|
||||
if is_soft_delete() and not include_deleted:
|
||||
|
|
@ -118,6 +124,12 @@ class NotificationQuerySet(models.query.QuerySet):
|
|||
|
||||
return qs.update(deleted=False)
|
||||
|
||||
def mark_as_unsent(self):
|
||||
return self.update(emailed=False)
|
||||
|
||||
def mark_as_sent(self):
|
||||
return self.update(emailed=True)
|
||||
|
||||
|
||||
class Notification(models.Model):
|
||||
"""
|
||||
|
|
@ -257,6 +269,8 @@ def notify_handler(verb, **kwargs):
|
|||
else:
|
||||
recipients = [recipient]
|
||||
|
||||
new_notifications = []
|
||||
|
||||
for recipient in recipients:
|
||||
newnotify = Notification(
|
||||
recipient=recipient,
|
||||
|
|
@ -280,6 +294,9 @@ def notify_handler(verb, **kwargs):
|
|||
newnotify.data = kwargs
|
||||
|
||||
newnotify.save()
|
||||
new_notifications.append(newnotify)
|
||||
|
||||
return new_notifications
|
||||
|
||||
|
||||
# connect the signal
|
||||
|
|
|
|||
|
|
@ -18,11 +18,9 @@ function fill_notification_badge(data) {
|
|||
function fill_notification_list(data) {
|
||||
var menu = document.getElementById(notify_menu_id);
|
||||
if (menu) {
|
||||
menu.innerHTML = "";
|
||||
for (var i=0; i < data.unread_list.length; i++) {
|
||||
var item = data.unread_list[i];
|
||||
console.log(item)
|
||||
var message = ""
|
||||
var content = [];
|
||||
menu.innerHTML = data.unread_list.map(function (item) {
|
||||
var message = "";
|
||||
if(typeof item.actor !== 'undefined'){
|
||||
message = item.actor;
|
||||
}
|
||||
|
|
@ -35,9 +33,8 @@ function fill_notification_list(data) {
|
|||
if(typeof item.timestamp !== 'undefined'){
|
||||
message = message + " " + item.timestamp;
|
||||
}
|
||||
|
||||
menu.innerHTML = menu.innerHTML + "<li>"+ message + "</li>";
|
||||
}
|
||||
return '<li>' + message + '</li>';
|
||||
}).join('')
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -50,17 +47,13 @@ function fetch_api_data() {
|
|||
//only fetch data if a function is setup
|
||||
var r = new XMLHttpRequest();
|
||||
r.open("GET", notify_api_url+'?max='+notify_fetch_count, true);
|
||||
r.onreadystatechange = function () {
|
||||
if (r.readyState != 4 || r.status != 200) {
|
||||
consecutive_misfires++;
|
||||
}
|
||||
else {
|
||||
consecutive_misfires = 0;
|
||||
for (var i=0; i < registered_functions.length; i++) {
|
||||
var func = registered_functions[i];
|
||||
func(JSON.parse(r.responseText));
|
||||
}
|
||||
}
|
||||
r.onerror = function () {
|
||||
consecutive_misfires++;
|
||||
}
|
||||
r.onready = function () {
|
||||
consecutive_misfires = 0;
|
||||
var data = JSON.parse(r.responseText);
|
||||
registered_functions.forEach(function (func) { func(data); });
|
||||
}
|
||||
r.send();
|
||||
}
|
||||
|
|
@ -75,4 +68,4 @@ function fetch_api_data() {
|
|||
}
|
||||
}
|
||||
|
||||
setTimeout(fetch_api_data,1000);
|
||||
setTimeout(fetch_api_data, 1000);
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import pytz
|
|||
import json
|
||||
|
||||
from notifications import notify
|
||||
from notifications.models import Notification
|
||||
from notifications.models import Notification, notify_handler
|
||||
from notifications.utils import id2slug
|
||||
|
||||
|
||||
|
|
@ -55,19 +55,41 @@ class NotificationManagersTest(TestCase):
|
|||
|
||||
def setUp(self):
|
||||
self.message_count = 10
|
||||
self.other_user = User.objects.create(username="other1", password="pwd", email="example@example.com")
|
||||
|
||||
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_user_list = User.objects.all()
|
||||
self.to_group.user_set.add(self.to_user)
|
||||
self.to_group.user_set.add(self.other_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
|
||||
self.message_count += self.to_group.user_set.count()
|
||||
# Send notification to user list
|
||||
notify.send(self.from_user, recipient=self.to_user_list, verb='commented', action_object=self.from_user)
|
||||
self.message_count += 1
|
||||
self.message_count += len(self.to_user_list)
|
||||
|
||||
|
||||
def test_notify_send_return_val(self):
|
||||
results = notify.send(self.from_user, recipient=self.to_user, verb='commented', action_object=self.from_user)
|
||||
for r in results:
|
||||
if r[0] is notify_handler:
|
||||
self.assertEqual(len(r[1]), 1)
|
||||
# only check types for now
|
||||
self.assertEqual(type(r[1][0]), Notification)
|
||||
|
||||
def test_notify_send_return_val_group(self):
|
||||
results = notify.send(self.from_user, recipient=self.to_group, verb='commented', action_object=self.from_user)
|
||||
for r in results:
|
||||
if r[0] is notify_handler:
|
||||
self.assertEqual(len(r[1]), self.to_group.user_set.count())
|
||||
for n in r[1]:
|
||||
# only check types for now
|
||||
self.assertEqual(type(n), Notification)
|
||||
|
||||
def test_unread_manager(self):
|
||||
self.assertEqual(Notification.objects.unread().count(), self.message_count)
|
||||
|
|
@ -88,7 +110,7 @@ class NotificationManagersTest(TestCase):
|
|||
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(self.to_user.notifications.unread().count(), 0)
|
||||
|
||||
@override_settings(NOTIFICATIONS_SOFT_DELETE=True)
|
||||
def test_mark_all_as_read_manager_with_soft_delete(self):
|
||||
|
|
@ -104,7 +126,7 @@ class NotificationManagersTest(TestCase):
|
|||
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(self.to_user.notifications.unread().count(), 0)
|
||||
Notification.objects.filter(recipient=self.to_user).mark_all_as_unread()
|
||||
self.assertEqual(Notification.objects.unread().count(), self.message_count)
|
||||
|
||||
|
|
@ -181,7 +203,7 @@ class NotificationTestPages(TestCase):
|
|||
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:unread'))
|
||||
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)
|
||||
|
|
@ -299,6 +321,26 @@ class NotificationTestPages(TestCase):
|
|||
self.assertEqual(len(data['unread_list']), 1)
|
||||
self.assertEqual(data['unread_list'][0]['verb'], 'commented')
|
||||
|
||||
def test_unread_list_api_mark_as_read(self):
|
||||
self.login('to', 'pwd')
|
||||
num_requested = 3
|
||||
response = self.client.get(
|
||||
reverse('notifications:live_unread_notification_list'),
|
||||
data={"max": num_requested, "mark_as_read": 1}
|
||||
)
|
||||
data = json.loads(response.content.decode('utf-8'))
|
||||
self.assertEqual(data['unread_count'],
|
||||
self.message_count - num_requested)
|
||||
self.assertEqual(len(data['unread_list']), num_requested)
|
||||
response = self.client.get(
|
||||
reverse('notifications:live_unread_notification_list'),
|
||||
data={"max": num_requested, "mark_as_read": 1}
|
||||
)
|
||||
data = json.loads(response.content.decode('utf-8'))
|
||||
self.assertEqual(data['unread_count'],
|
||||
self.message_count - 2*num_requested)
|
||||
self.assertEqual(len(data['unread_list']), num_requested)
|
||||
|
||||
def test_live_update_tags(self):
|
||||
from django.shortcuts import render
|
||||
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ def mark_all_as_read(request):
|
|||
|
||||
if _next:
|
||||
return redirect(_next)
|
||||
return redirect('notifications:all')
|
||||
return redirect('notifications:unread')
|
||||
|
||||
|
||||
@login_required
|
||||
|
|
@ -79,7 +79,7 @@ def mark_as_read(request, slug=None):
|
|||
if _next:
|
||||
return redirect(_next)
|
||||
|
||||
return redirect('notifications:all')
|
||||
return redirect('notifications:unread')
|
||||
|
||||
|
||||
@login_required
|
||||
|
|
@ -95,7 +95,7 @@ def mark_as_unread(request, slug=None):
|
|||
if _next:
|
||||
return redirect(_next)
|
||||
|
||||
return redirect('notifications:all')
|
||||
return redirect('notifications:unread')
|
||||
|
||||
|
||||
@login_required
|
||||
|
|
@ -156,6 +156,8 @@ def live_unread_notification_list(request):
|
|||
if n.action_object:
|
||||
struct['action_object'] = str(n.action_object)
|
||||
unread_list.append(struct)
|
||||
if request.GET.get('mark_as_read'):
|
||||
n.mark_as_read()
|
||||
data = {
|
||||
'unread_count': request.user.notifications.unread().count(),
|
||||
'unread_list': unread_list
|
||||
|
|
|
|||
5
setup.py
5
setup.py
|
|
@ -48,6 +48,11 @@ setup(
|
|||
'Intended Audience :: Developers',
|
||||
'License :: OSI Approved :: BSD License',
|
||||
'Operating System :: OS Independent',
|
||||
'Framework :: Django',
|
||||
'Framework :: Django :: 1.10',
|
||||
'Framework :: Django :: 1.9',
|
||||
'Framework :: Django :: 1.8',
|
||||
'Framework :: Django :: 1.7',
|
||||
# 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',
|
||||
|
|
|
|||
Loading…
Reference in a new issue