Merge pull request #83 from LegoStormtroopr/master

Fix live updating notifications
This commit is contained in:
Samuel Spencer 2015-12-12 20:43:45 +11:00
commit 75744a713f
12 changed files with 139 additions and 27 deletions

View file

@ -250,11 +250,11 @@ 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,
@ -267,8 +267,8 @@ There are two possible API calls that can be made:
How to use:
-----------
1. Put ``{% load notifications_tags %}`` in the template before you actually use notification tags.
2. In the area where you are loading javascript resources add the following tags in the order below::
1. Put ``{% load notifications_tags %}`` in the template before you actually use notification tags.
2. In the area where you are loading javascript resources add the following tags in the order below::
<script src="{% static 'notifications/notify.js' %}" type="text/javascript"></script>
{% register_notify_callbacks callbacks='fill_notification_list,fill_notification_badge' %}
@ -278,26 +278,27 @@ How to use:
1. ``badge_id`` (default ``live_notify_badge``) - The `id` attribute of the element to show the unread count, that will be periodically updated.
#. ``menu_id`` (default ``live_notify_list``) - The `id` attribute of the element to insert a list of unread items, that will be periodically updated.
#. ``refresh_period`` (default ``15``) - How often to fetch unread items from the server (integer in seconds).
#. ``to_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``).
3. To insert a live-updating unread count, use the following template::
3. To insert a live-updating unread count, use the following template::
{% live_notify_badge %}
``live_notify_badge`` takes the following arguments:
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.
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 count, use the following template::
{% live_notify_list %}
``live_notify_list`` takes the following arguments:
1. ``list_id`` (default ``live_notify_list``) - The ``id`` attribute for the ``<ul>`` element that will be created to insert the list of notifications into.
#. ``classes`` (default ``<empty string>``) - A string used to populate the ``class`` attribute of the above element.
1. ``list_id`` (default ``live_notify_list``) - The ``id`` attribute for the ``<ul>`` element that will be created to insert the list of notifications into.
#. ``classes`` (default ``<empty string>``) - A string used to populate the ``class`` attribute of the above element.
Using the live-updater with bootstrap
-------------------------------------
@ -319,7 +320,7 @@ While the live notifier for unread counts should suit most use cases, users may
unread notifications are shown.
The ``callbacks`` argument of the ``register_notify_callbacks`` dictates which javascript functions are called when
the unread api call is made.
the unread api call is made.
To add a custom javascript callback, simply add this to the list, like so::
@ -337,6 +338,15 @@ For example, the below function would get the recent list of unread messages and
}
}
Testing the live-updater
------------------------
1. Clone the repo
2. Set the 'NOTIFICATION_TEST' environemnt variable. E.g. `export NOTIFICATION_TEST=1`
3. Run `./manage.py runserver`
4. Browse to `yourserverip/test/`
5. Click 'Make a notification' and a new notification should appear in the list in 5-10 seconds.
``django-notifications`` Team
==============================

View file

@ -3,7 +3,7 @@ import os
import sys
if __name__ == "__main__":
if 'test' in sys.argv:
if 'test' in sys.argv or 'NOTIFICATION_TEST' in os.environ.keys():
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "notifications.tests.settings")
from django.core.management import execute_from_command_line

View file

@ -8,3 +8,25 @@ try:
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()

View file

@ -1,6 +1,7 @@
var notify_badge_id;
var notify_menu_id;
var notify_api_url;
var notify_fetch_count;
var notify_unread_url;
var notify_mark_all_unread_url;
var notify_refresh_period = 15000;
@ -20,21 +21,21 @@ function fill_notification_list(data) {
menu.innerHTML = "";
for (var i=0; i < data.unread_list.length; i++) {
var item = data.unread_list[i];
menu.innerHTML = menu.innerHTML + "<li>"+item.object+" "+item.verb+" "+item.subject+"</li>";
console.log(item)
menu.innerHTML = menu.innerHTML + "<li>"+item.actor+" "+item.verb+" "+item.target+" at " +item.timestamp + "</li>";
}
}
}
function register_notifier(func) {
registered_functions.push(func);
console.log(registered_functions)
}
function fetch_api_data() {
if (registered_functions.length > 0) {
//only fetch data if a function is setup
var r = new XMLHttpRequest();
r.open("GET", notify_api_url, true);
r.open("GET", notify_api_url+'?max='+notify_fetch_count, true);
r.onreadystatechange = function () {
if (r.readyState != 4 || r.status != 200) {
consecutive_misfires++;

View file

@ -1,6 +1,6 @@
{% load notifications_tags %}
{% register_notify_callbacks callbacks='fill_aristotle_notification_menu,fill_notification_badge' %}
{% register_notify_callbacks callbacks='fill_notification_menu,fill_notification_badge' %}
{% notifications_unread as unread %}
{{ unread }}

View file

@ -15,7 +15,7 @@ def notifications_unread(context):
# 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'):
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':
@ -29,10 +29,19 @@ def register_notify_callbacks(badge_id='live_notify_badge',menu_id='live_notify_
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'))
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(','):
script += "register_notifier("+callback+");"
@ -54,7 +63,7 @@ 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):
def user_context(context):
if 'user' not in context:
return None

View file

@ -1,12 +1,14 @@
import os
BASE_DIR = os.path.abspath(os.path.dirname(__file__))
SECRET_KEY = 'secret_key'
SOUTH_TESTS_MIGRATE = True
DEBUG = True
TESTING = True
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': ':memory:',
'NAME': 'test.sqlite3',
}
}
@ -20,11 +22,18 @@ INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.staticfiles',
'django.contrib.sessions',
'notifications.tests',
'notifications',
)
ROOT_URLCONF = 'notifications.tests.urls'
STATIC_URL = '/static/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR, "static"),
]
STATIC_ROOT = os.path.join(BASE_DIR, "static-files")
# Need to skip migrations for now as migrations created with python2 break with python3

View file

@ -0,0 +1,7 @@
console.log('running tester')
function make_notification() {
var r = new XMLHttpRequest();
r.open("GET", '/test_make/', true);
r.send();
}

View file

@ -0,0 +1,14 @@
{% load static notifications_tags %}
<script src="{% static 'notifications/notify.js' %}" type="text/javascript"></script>
<script src="{% static 'notifications/live-test.js' %}" type="text/javascript"></script>
{% register_notify_callbacks callbacks='fill_notification_list,fill_notification_badge' fetch=20 refresh_period=5 %}
There are this many notifications pending: <span id='live_notify_badge'></span>
<button onclick='make_notification()'>Make a notification</button>
<ul class="notifications" id="live_notify_list">
{% for notice in notifications %}
{% include 'notifications/notice.html' %}
{% endfor %}
</ul>

View file

@ -8,5 +8,7 @@ from notifications import urls
urlpatterns = patterns('',
url(r'^login/$', 'django.contrib.auth.views.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')),
)

View file

@ -0,0 +1,27 @@
from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.shortcuts import get_object_or_404, 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 = {
'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([
'reticulating splines',
'cleaning the car',
'jumping the shark',
'testing the app',
])
notify.send(sender=request.user, recipient=request.user, verb='you asked for a notification - you are '+the_notification)

View file

@ -131,14 +131,25 @@ 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.
unread_list = []
for n in request.user.notifications.unread()[0:num_to_fetch]:
struct = model_to_dict(n)
if n.actor:
struct['actor'] = str(n.actor)
if n.target:
struct['target'] = str(n.target)
if n.action_object:
struct['action_object'] = str(n.action_object)
unread_list.append(struct)
data = {
'unread_count': request.user.notifications.unread().count(),
'unread_list': [model_to_dict(n) for n in request.user.notifications.unread()[0:num_to_fetch]]
'unread_count':request.user.notifications.unread().count(),
'unread_list':unread_list
}
return JsonResponse(data)