Merge branch 'master' into iss169

This commit is contained in:
Alvaro Leonel 2017-06-01 23:18:58 -04:00 committed by GitHub
commit 3d14fd6b1a
10 changed files with 127 additions and 41 deletions

View file

@ -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

View file

@ -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

View file

@ -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
==============================

View file

@ -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'

View file

@ -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', )

View file

@ -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

View file

@ -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);

View file

@ -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

View file

@ -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

View file

@ -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',