Merge pull request #265 from Adarnof/frontlogging

Website Notification System
This commit is contained in:
Adarnof 2016-02-13 18:43:29 -05:00
commit effc05d6a3
14 changed files with 224 additions and 17 deletions

View File

@ -16,7 +16,7 @@ Join us in-game in the channel allianceauth for help and feature requests.
Special Thanks:
Thanking [Nikdoof](https://github.com/nikdoof), without his old auth
Thanking Nikdoof, without his old auth
implementation this project wouldn't be as far as it is now.
Thanks to Raynaldo for his original work on this system and getting it as far as it is today.
@ -74,6 +74,7 @@ Special Permissions In Admin:
auth | user | sigtracker_view ( Allows for an individual view signitures)
auth | user | optimer_management ( Allows for an individual to create and remove fleet operations)
auth | user | optimer_view ( Allows for an individual view fleet operations)
auth | user | logging_notifications ( Generate notifications from logging)
Active Developers

View File

@ -61,6 +61,7 @@ INSTALLED_APPS = (
'sigtracker',
'optimer',
'corputils',
'notifications',
)
MIDDLEWARE_CLASSES = (
@ -126,6 +127,7 @@ TEMPLATE_CONTEXT_PROCESSORS = (
'util.context_processors.domain_url',
'util.context_processors.member_api_mask',
'util.context_processors.blue_api_mask',
'notifications.context_processors.user_notification_count',
)
TEMPLATE_DIRS = (
@ -408,67 +410,72 @@ LOGGING = {
'level': 'DEBUG', # edit this line to change logging level to console
'class': 'logging.StreamHandler',
'formatter': 'verbose',
}
},
'notifications': { # creates notifications for users with logging_notifications permission
'level': 'ERROR', # edit this line to change logging level to notifications
'class': 'notifications.handlers.NotificationHandler',
'formatter': 'verbose',
},
},
'loggers': {
'authentication': {
'handlers': ['log_file', 'console'],
'handlers': ['log_file', 'console', 'notifications'],
'level': 'DEBUG',
},
'celerytask': {
'handlers': ['log_file', 'console'],
'handlers': ['log_file', 'console', 'notifications'],
'level': 'DEBUG',
},
'eveonline': {
'handlers': ['log_file', 'console'],
'handlers': ['log_file', 'console', 'notifications'],
'level': 'DEBUG',
},
'groupmanagement': {
'handlers': ['log_file', 'console'],
'handlers': ['log_file', 'console', 'notifications'],
'level': 'DEBUG',
},
'hrapplications': {
'handlers': ['log_file', 'console'],
'handlers': ['log_file', 'console', 'notifications'],
'level': 'DEBUG',
},
'portal': {
'handlers': ['log_file', 'console'],
'handlers': ['log_file', 'console', 'notifications'],
'level': 'DEBUG',
},
'registration': {
'handlers': ['log_file', 'console'],
'handlers': ['log_file', 'console', 'notifications'],
'level': 'DEBUG',
},
'services': {
'handlers': ['log_file', 'console'],
'handlers': ['log_file', 'console', 'notifications'],
'level': 'DEBUG',
},
'srp': {
'handlers': ['log_file', 'console'],
'handlers': ['log_file', 'console', 'notifications'],
'level': 'DEBUG',
},
'timerboard': {
'handlers': ['log_file', 'console'],
'handlers': ['log_file', 'console', 'notifications'],
'level': 'DEBUG',
},
'sigtracker': {
'handlers': ['log_file', 'console'],
'handlers': ['log_file', 'console', 'notifications'],
'level': 'DEBUG',
},
'optimer': {
'handlers': ['log_file', 'console'],
'handlers': ['log_file', 'console', 'notifications'],
'level': 'DEBUG',
},
'corputils': {
'handlers': ['log_file', 'console'],
'handlers': ['log_file', 'console', 'notifications'],
'level': 'DEBUG',
},
'util': {
'handlers': ['log_file', 'console'],
'handlers': ['log_file', 'console', 'notifications'],
'level': 'DEBUG',
},
'django': {
'handlers': ['log_file', 'console'],
'handlers': ['log_file', 'console', 'notifications'],
'level': 'ERROR',
},
}

View File

@ -186,4 +186,7 @@ urlpatterns = patterns('',
url(r'^remove_optimer/(\w+)', 'optimer.views.remove_optimer', name='auth_remove_optimer'),
url(r'^edit_optimer/(\w+)$', 'optimer.views.edit_optimer', name='auth_edit_optimer'),
# Notifications
url(r'^notifications/$', 'notifications.views.notification_list', name='auth_notification_list'),
url(r'^notifications/(\w+)/$', 'notifications.views.notification_view', name='auth_notification_view'),
)

View File

4
notifications/admin.py Normal file
View File

@ -0,0 +1,4 @@
from django.contrib import admin
from.models import Notification
admin.site.register(Notification)

View File

@ -0,0 +1,4 @@
from .models import Notification
def user_notification_count(request):
return {'notifications':len(Notification.objects.filter(user__id=request.user.id).filter(viewed=False))}

14
notifications/handlers.py Normal file
View File

@ -0,0 +1,14 @@
import logging
from django.contrib.auth.models import User
from .models import Notification
class NotificationHandler(logging.Handler):
def emit(self, record):
for user in User.objects.all():
if user.has_perm('auth.logging_notifications'):
notif = Notification()
notif.user = user
notif.title = "%s [%s:%s]" % (record.levelname, record.funcName, record.lineno)
notif.level = str([item[0] for item in Notification.LEVEL_CHOICES if item[1] == record.levelname][0])
notif.message = record.getMessage()
notif.save()

34
notifications/models.py Normal file
View File

@ -0,0 +1,34 @@
from django.db import models
from django.contrib.auth.models import User
import logging
logger = logging.getLogger(__name__)
class Notification(models.Model):
LEVEL_CHOICES = (
('danger', 'CRITICAL'),
('danger', 'ERROR'),
('warning', 'WARN'),
('info', 'INFO'),
('success', 'DEBUG'),
)
user = models.ForeignKey(User, on_delete=models.CASCADE)
level = models.CharField(choices=LEVEL_CHOICES, max_length=10)
title = models.CharField(max_length=254)
message = models.TextField()
timestamp = models.DateTimeField(auto_now_add=True)
viewed = models.BooleanField(default=False)
def view(self):
logger.info("Marking notification as viewed: %s" % self)
self.viewed = True
self.save()
def __unicode__(self):
output = "%s: %s" % (self.user, self.title)
return output.encode('utf-8')
def set_level(self, level):
self.level = [item[0] for item in self.LEVEL_CHOICES if item[1] == level][0]

3
notifications/tests.py Normal file
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

31
notifications/views.py Normal file
View File

@ -0,0 +1,31 @@
from django.shortcuts import render, get_object_or_404, redirect
from .models import Notification
from django.contrib.auth.decorators import login_required
import logging
logger = logging.getLogger(__name__)
@login_required
def notification_list(request):
logger.debug("notification_list called by user %s" % request.user)
new_notifs = Notification.objects.filter(user=request.user).filter(viewed=False)
old_notifs = Notification.objects.filter(user=request.user).filter(viewed=True)
logger.debug("User %s has %s unread and %s read notifications" % (request.user, len(new_notifs), len(old_notifs)))
context = {
'read': old_notifs,
'unread': new_notifs,
}
return render(request, 'registered/notification_list.html', context)
@login_required
def notification_view(request, notif_id):
logger.debug("notification_view called by user %s for notif_id %s" % (request.user, notif_id))
notif = get_object_or_404(Notification, pk=notif_id)
if notif.user == request.user:
logger.debug("Providing notification for user %s" % request.user)
context = {'notif': notif}
notif.view()
return render(request, 'registered/notification_view.html', context)
else:
logger.warn("User %s not authorized to view notif_id %s belonging to user %s" % (request.user, notif_id, notif.user))
return redirect('auth_notification_list')

View File

@ -47,6 +47,15 @@
<!-- /.navbar-header -->
<ul class="nav navbar-top-links navbar-right">
{% if notifications %}
<li class="nav-link active"><a href="{% url 'auth_notification_list' %}">
<span class="glyphicon glyphicon-alert"></span></a>
</li>
{% else %}
<li class="nav-link"><a href="{% url 'auth_notification_list' %}">
<span class="glyphicon glyphicon-warning-sign"></span></a>
</li>
{% endif %}
{% if user.is_authenticated %}
<li><a href="{% url 'auth_logout_user' %}">Logout</a></li>
{% else %}

View File

@ -0,0 +1,74 @@
{% extends "public/base.html" %}
{% load staticfiles %}
{% block title %}Notifications{% endblock %}
{% block content %}
<div class="col-lg-12">
<h1 class="page-header text-center">Notifications</h1>
<div class="col-lg-12 container" id="example">
<div class="row">
<div class="col-lg-12">
<ul class="nav nav-tabs">
<li class="active"><a data-toggle="tab" href="#unread">Unread <b>({{unread|length}})</b></a></li>
<li><a data-toggle="tab" href="#read">Read <b>({{read|length}})</b></a></li>
</ul>
<div class="tab-content">
<div id="unread" class="tab-pane fade in active">
<div class="panel-body">
<div class="table-responsive">
<table class="table table-condensed table-hover table-striped">
<tr>
<th class="text-center">Timestamp</th>
<th class="text-center">Title</th>
<th class="text-center">View</th>
</tr>
{% for notif in unread %}
<tr class="{{ notif.level }}">
<td class="text-center">{{ notif.timestamp }}</td>
<td class="text-center">{{ notif.title }}</td>
<td class="text-center">
<a href="{% url 'auth_notification_view' notif.id %}">
<button type="button" class="btn btn-success" title="View">
<span class="glyphicon glyphicon-eye-open"></span>
</button>
</a>
</td>
</tr>
{% endfor %}
</table>
</div>
</div>
</div>
<div id="read" class="tab-pane fade">
<div class="panel-body">
<div class="table-responsive">
<table class="table table-condensed table-hover table-striped">
<tr>
<th class="text-center">Timestamp</th>
<th class="text-center">Title</th>
<th class="text-center">View</th>
</tr>
{% for notif in read %}
<tr class="{{ notif.level }}">
<td class="text-center">{{ notif.timestamp }}</td>
<td class="text-center">{{ notif.title }}</td>
<td class="text-center">
<a href="{% url 'auth_notification_view' notif.id %}">
<button type="button" class="btn btn-success" title="View">
<span class="glyphicon glyphicon-eye-open"></span>
</button>
</a>
</td>
</tr>
{% endfor %}
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,22 @@
{% extends "public/base.html" %}
{% load staticfiles %}
{% block title %}View Notification{% endblock title %}
{% block page_title %}View Notification{% endblock page_title %}
{% block content %}
<div class="col-lg-12">
<h1 class="page-header text-center">View Notification</h1>
<div class="col-lg-12 container" id="example">
<div class="row">
<div class="col-lg-12">
<div class="panel panel-{{ notif.level }}">
<div class="panel-heading">{{ notif.timestamp }} {{ notif.title }}</div>
<div class="panel-body">{{ notif.message }}</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -27,6 +27,7 @@ def bootstrap_permissions():
Permission.objects.get_or_create(codename="signature_view", content_type=ct, name="signature_view")
Permission.objects.get_or_create(codename="optimer_management", content_type=ct, name="optimer_management")
Permission.objects.get_or_create(codename="optimer_view", content_type=ct, name="optimer_view")
Permission.objects.get_or_create(codename="logging_notifications", content_type=ct, name="logging_notifications")
Group.objects.get_or_create(name=settings.DEFAULT_AUTH_GROUP)
Group.objects.get_or_create(name=settings.DEFAULT_BLUE_GROUP)
logger.info("Bootstrapped permissions for auth and created default groups.")