Commit 07193e64de2ee9ab1e2642c6ff8b01ebbc0c5d60
1 parent
85fe3d3f
Exists in
master
and in
3 other branches
Adding subject notifications view page
Showing
15 changed files
with
307 additions
and
7 deletions
Show diff stats
amadeus/static/css/base/amadeus.css
| @@ -142,6 +142,10 @@ a:focus { | @@ -142,6 +142,10 @@ a:focus { | ||
| 142 | font-size: 15px; | 142 | font-size: 15px; |
| 143 | } | 143 | } |
| 144 | 144 | ||
| 145 | +.pendencies-content { | ||
| 146 | + padding: 10px; | ||
| 147 | +} | ||
| 148 | + | ||
| 145 | .category-panel-content { | 149 | .category-panel-content { |
| 146 | padding:10px; | 150 | padding:10px; |
| 147 | } | 151 | } |
| @@ -834,4 +838,54 @@ li.item .notify_badge { | @@ -834,4 +838,54 @@ li.item .notify_badge { | ||
| 834 | font-size: 0.9rem; | 838 | font-size: 0.9rem; |
| 835 | border-radius: 50%; | 839 | border-radius: 50%; |
| 836 | padding: 0px; | 840 | padding: 0px; |
| 841 | +} | ||
| 842 | + | ||
| 843 | +.pendency { | ||
| 844 | + border: 1px solid; | ||
| 845 | + padding-left: 10px; | ||
| 846 | + padding-top: 7px; | ||
| 847 | + margin-bottom: 15px; | ||
| 848 | +} | ||
| 849 | + | ||
| 850 | +.pendency .breadcrumb { | ||
| 851 | + padding-left: 0px; | ||
| 852 | + margin-bottom: 0px; | ||
| 853 | +} | ||
| 854 | + | ||
| 855 | +.pendency h4 { | ||
| 856 | + margin: 0px; | ||
| 857 | + font-size: 22px; | ||
| 858 | + font-weight: 700; | ||
| 859 | +} | ||
| 860 | + | ||
| 861 | +.pendency p { | ||
| 862 | + margin-bottom: 3px; | ||
| 863 | +} | ||
| 864 | + | ||
| 865 | +.pendency .alert { | ||
| 866 | + padding-top: 5px; | ||
| 867 | + padding-bottom: 5px; | ||
| 868 | + font-weight: 700; | ||
| 869 | +} | ||
| 870 | + | ||
| 871 | +.pendency .alert span { | ||
| 872 | + position: relative; | ||
| 873 | + top: -7px; | ||
| 874 | + padding-left: 10px; | ||
| 875 | +} | ||
| 876 | + | ||
| 877 | +.pendency .alert i { | ||
| 878 | + font-size: 35px; | ||
| 879 | +} | ||
| 880 | + | ||
| 881 | +.alert-low { | ||
| 882 | + opacity: 0.5; | ||
| 883 | +} | ||
| 884 | + | ||
| 885 | +.no_button { | ||
| 886 | + cursor: initial; | ||
| 887 | +} | ||
| 888 | + | ||
| 889 | +.no_button:focus, .no_button:active:focus, .no_button.active:focus, .no_button.focus, .no_button:active.focus, .no_button.active.focus { | ||
| 890 | + outline: none; | ||
| 837 | } | 891 | } |
| 838 | \ No newline at end of file | 892 | \ No newline at end of file |
amadeus/static/css/themes/green.css
| @@ -85,6 +85,14 @@ a, a:focus, a:hover { | @@ -85,6 +85,14 @@ a, a:focus, a:hover { | ||
| 85 | color: #F5F5F5; | 85 | color: #F5F5F5; |
| 86 | } | 86 | } |
| 87 | 87 | ||
| 88 | +.pendencies-content { | ||
| 89 | + background: #FFFFFF; | ||
| 90 | +} | ||
| 91 | + | ||
| 92 | +.pendencies-content .core-subjects-options li { | ||
| 93 | + background: #F5F5F5; | ||
| 94 | +} | ||
| 95 | + | ||
| 88 | .category-header i { | 96 | .category-header i { |
| 89 | color: white; | 97 | color: white; |
| 90 | } | 98 | } |
| @@ -426,6 +434,27 @@ a.add-row { | @@ -426,6 +434,27 @@ a.add-row { | ||
| 426 | color: #F5F5F5 !important; | 434 | color: #F5F5F5 !important; |
| 427 | } | 435 | } |
| 428 | 436 | ||
| 437 | +.pendency { | ||
| 438 | + border-color: #CCCCCC; | ||
| 439 | +} | ||
| 440 | + | ||
| 441 | +.pendency .breadcrumb { | ||
| 442 | + background-color: #FFFFFF; | ||
| 443 | +} | ||
| 444 | + | ||
| 445 | +.pendency .meta { | ||
| 446 | + color: #FF0000; | ||
| 447 | +} | ||
| 448 | + | ||
| 449 | +.alert-low, .alert-medium { | ||
| 450 | + background-color: #FFA500; | ||
| 451 | + color: #FFFFFF; | ||
| 452 | +} | ||
| 453 | + | ||
| 454 | +.no_button:hover, .no_button:focus, .no_button:active { | ||
| 455 | + background-color: initial !important; | ||
| 456 | +} | ||
| 457 | + | ||
| 429 | @media(max-width: 768px) { | 458 | @media(max-width: 768px) { |
| 430 | .navbar .navbar-nav .dropdown .dropdown-menu li > a { | 459 | .navbar .navbar-nav .dropdown .dropdown-menu li > a { |
| 431 | color: #333333 !important; | 460 | color: #333333 !important; |
amadeus/templates/pagination.html
| 1 | {% if paginator.count > 0 %} | 1 | {% if paginator.count > 0 %} |
| 2 | - <div class="row-fluid"> | 2 | + <div class="row"> |
| 3 | <div class="col-md-12 col-lg-12 col-sm-12 col-xs-12 text-center"> | 3 | <div class="col-md-12 col-lg-12 col-sm-12 col-xs-12 text-center"> |
| 4 | <ul class="pagination"> | 4 | <ul class="pagination"> |
| 5 | {% if page_obj.has_previous %} | 5 | {% if page_obj.has_previous %} |
amadeus/urls.py
| @@ -34,6 +34,7 @@ urlpatterns = [ | @@ -34,6 +34,7 @@ urlpatterns = [ | ||
| 34 | url(r'^mailsender/', include('mailsender.urls', namespace = 'mailsender')), | 34 | url(r'^mailsender/', include('mailsender.urls', namespace = 'mailsender')), |
| 35 | url(r'^security/', include('security.urls', namespace = 'security')), | 35 | url(r'^security/', include('security.urls', namespace = 'security')), |
| 36 | url(r'^themes/', include('themes.urls', namespace = 'themes')), | 36 | url(r'^themes/', include('themes.urls', namespace = 'themes')), |
| 37 | + url(r'^notifications/', include('notifications.urls', namespace = 'notifications')), | ||
| 37 | #API | 38 | #API |
| 38 | url(r'^o/', include('oauth2_provider.urls', namespace='oauth2_provider')), | 39 | url(r'^o/', include('oauth2_provider.urls', namespace='oauth2_provider')), |
| 39 | #S3Direct | 40 | #S3Direct |
notifications/models.py
| @@ -11,3 +11,6 @@ class Notification(models.Model): | @@ -11,3 +11,6 @@ class Notification(models.Model): | ||
| 11 | level = models.IntegerField(_('Type'), choices = ((1, _('Type 1-A')), (2, _('Type 1-B')), (3, _('Type 2')), (4, _('Type 3')))) | 11 | level = models.IntegerField(_('Type'), choices = ((1, _('Type 1-A')), (2, _('Type 1-B')), (3, _('Type 2')), (4, _('Type 3')))) |
| 12 | viewed = models.BooleanField(_('Visualized'), default = False) | 12 | viewed = models.BooleanField(_('Visualized'), default = False) |
| 13 | creation_date = models.DateField(_('Creation Date'), auto_now_add = True) | 13 | creation_date = models.DateField(_('Creation Date'), auto_now_add = True) |
| 14 | + | ||
| 15 | + def __str__(self): | ||
| 16 | + return self.task.get_action_display() + " " + str(self.task.resource) |
| @@ -0,0 +1,65 @@ | @@ -0,0 +1,65 @@ | ||
| 1 | +{% load i18n notification_filters %} | ||
| 2 | + | ||
| 3 | +<div class="row"> | ||
| 4 | + <div class="col-md-12"> | ||
| 5 | + <div class="pendency"> | ||
| 6 | + <ul class="breadcrumb"> | ||
| 7 | + <li> | ||
| 8 | + <a href="">{% trans 'Home' %}</a> | ||
| 9 | + </li> | ||
| 10 | + <li> | ||
| 11 | + <a href="">{{ notification.task.resource.topic.subject.category }}</a> | ||
| 12 | + </li> | ||
| 13 | + <li> | ||
| 14 | + <a href="">{{ notification.task.resource.topic.subject }}</a> | ||
| 15 | + </li> | ||
| 16 | + <li> | ||
| 17 | + <a href="">{{ notification.task.resource.topic }}</a> | ||
| 18 | + </li> | ||
| 19 | + <li> | ||
| 20 | + <a href="">{{ notification.task.resource }}</a> | ||
| 21 | + </li> | ||
| 22 | + </ul> | ||
| 23 | + <div class="row"> | ||
| 24 | + <div class="col-md-6"> | ||
| 25 | + <h4>{{ notification }}</h4> | ||
| 26 | + <p>{% trans 'Final Date/Time' %}: {{ notification.task.end_date|default:_('Not Informed') }}</p> | ||
| 27 | + | ||
| 28 | + {% if notification.level == 2 %} | ||
| 29 | + <p class="meta">{% trans 'Your goal was to realize this in' %}: {{ notification.meta }}</p> | ||
| 30 | + {% elif notification.level == 4 %} | ||
| 31 | + <p class="meta">{% trans 'Task finished in' %}: {{ notification.task.limit_date }}</p> | ||
| 32 | + {% endif %} | ||
| 33 | + | ||
| 34 | + <b>{{ notification|done_percent }}%</b> {% trans 'of the participants already realized this task.' %} | ||
| 35 | + </div> | ||
| 36 | + <div class="col-md-6"> | ||
| 37 | + <div class="alert {{ notification.level|warning_class }}"> | ||
| 38 | + <i class="fa fa-exclamation-triangle"></i> | ||
| 39 | + <span>{{ notification.level|warning_msg }}</span> | ||
| 40 | + </div> | ||
| 41 | + </div> | ||
| 42 | + </div> | ||
| 43 | + <div class="row text-center"> | ||
| 44 | + <a href="" class="btn btn-success btn-raised"> | ||
| 45 | + {% if notification.level == 4 %} | ||
| 46 | + {% trans 'Access the task' %} | ||
| 47 | + {% else %} | ||
| 48 | + {% trans 'Realize the task' %} | ||
| 49 | + {% endif %} | ||
| 50 | + </a> | ||
| 51 | + | ||
| 52 | + {% if notification.level < 3 %} | ||
| 53 | + <button class="btn btn-default no_button">{% trans 'or' %}</button> | ||
| 54 | + <a href="" class="btn btn-default btn-raised"> | ||
| 55 | + {% if notification.level == 1 %} | ||
| 56 | + {% trans 'Define goal to realization' %} | ||
| 57 | + {% else %} | ||
| 58 | + {% trans 'Define new goal' %} | ||
| 59 | + {% endif %} | ||
| 60 | + </a> | ||
| 61 | + {% endif %} | ||
| 62 | + </div> | ||
| 63 | + </div> | ||
| 64 | + </div> | ||
| 65 | +</div> | ||
| 0 | \ No newline at end of file | 66 | \ No newline at end of file |
| @@ -0,0 +1,50 @@ | @@ -0,0 +1,50 @@ | ||
| 1 | +{% extends 'subjects/view.html' %} | ||
| 2 | + | ||
| 3 | +{% load static i18n pagination permissions_tags subject_counter %} | ||
| 4 | +{% load django_bootstrap_breadcrumbs %} | ||
| 5 | + | ||
| 6 | +{% block breadcrumbs %} | ||
| 7 | + {{ block.super }} | ||
| 8 | + | ||
| 9 | + {% breadcrumb 'Pendencies' 'notifications:view' subject.slug %} | ||
| 10 | +{% endblock %} | ||
| 11 | + | ||
| 12 | +{% block content %} | ||
| 13 | + {% if subject.visible %} | ||
| 14 | + <div class="panel panel-info subject-panel"> | ||
| 15 | + <div class="panel-heading"> | ||
| 16 | + {% else %} | ||
| 17 | + <div class="panel panel-info subject-panel-invisible"> | ||
| 18 | + <div class="panel-heading panel-invisible"> | ||
| 19 | + {% endif %} | ||
| 20 | + <div class="row"> | ||
| 21 | + <div class="col-md-12 category-header"> | ||
| 22 | + <h4 class="panel-title" style="margin-top: 10px; margin-bottom: 8px"> | ||
| 23 | + <span>{% trans 'Pendencies' %}</span> | ||
| 24 | + </h4> | ||
| 25 | + </div> | ||
| 26 | + </div> | ||
| 27 | + </div> | ||
| 28 | + <div id="{{subject.slug}}" class="panel-collapse in collapse pendencies-content"> | ||
| 29 | + <div id="core-subjects-options-div"> | ||
| 30 | + <ul class="core-subjects-options"> | ||
| 31 | + <a href="{% url 'notifications:view' subject.slug %}"><li class="active">{% trans "Actual Pendencies" %} ({{ notifications.count }})</li></a> | ||
| 32 | + <a href="" ><li>{% trans "Notifications History" %}</li></a> | ||
| 33 | + </ul> | ||
| 34 | + </div> | ||
| 35 | + | ||
| 36 | + {% if notifications.count > 0 %} | ||
| 37 | + {% for notification in notifications %} | ||
| 38 | + {% include 'notifications/_view.html' %} | ||
| 39 | + {% endfor %} | ||
| 40 | + | ||
| 41 | + {% pagination request paginator page_obj %} | ||
| 42 | + {% else %} | ||
| 43 | + <div class="text-center no-subjects"> | ||
| 44 | + <i class="fa fa-exclamation-triangle"></i> | ||
| 45 | + <h4>{% trans 'You do not posses any pendency in this subject' %}</h4> | ||
| 46 | + </div> | ||
| 47 | + {% endif %} | ||
| 48 | + </div> | ||
| 49 | + </div> | ||
| 50 | +{% endblock %} | ||
| 0 | \ No newline at end of file | 51 | \ No newline at end of file |
| @@ -0,0 +1,45 @@ | @@ -0,0 +1,45 @@ | ||
| 1 | +from django import template | ||
| 2 | +from datetime import datetime | ||
| 3 | +from django.utils.translation import ugettext_lazy as _ | ||
| 4 | + | ||
| 5 | +from notifications.utils import get_resource_users | ||
| 6 | +from notifications.models import Notification | ||
| 7 | + | ||
| 8 | +register = template.Library() | ||
| 9 | + | ||
| 10 | +@register.filter(name = 'warning_class') | ||
| 11 | +def warning_class(level): | ||
| 12 | + if level == 1: | ||
| 13 | + class_name = "alert-low" | ||
| 14 | + elif level == 2: | ||
| 15 | + class_name = "alert-low" | ||
| 16 | + elif level == 3: | ||
| 17 | + class_name = "alert-medium" | ||
| 18 | + else: | ||
| 19 | + class_name = "alert-danger" | ||
| 20 | + | ||
| 21 | + return class_name | ||
| 22 | + | ||
| 23 | +@register.filter(name = 'warning_msg') | ||
| 24 | +def warning_msg(level): | ||
| 25 | + if level == 1: | ||
| 26 | + msg = _('You still did not realize this task') | ||
| 27 | + elif level == 2: | ||
| 28 | + msg = _('You still did not realize this task') | ||
| 29 | + elif level == 3: | ||
| 30 | + msg = _('This task is late') | ||
| 31 | + else: | ||
| 32 | + msg = _('You miss this task') | ||
| 33 | + | ||
| 34 | + return msg | ||
| 35 | + | ||
| 36 | +@register.filter(name = 'done_percent') | ||
| 37 | +def done_percent(notification): | ||
| 38 | + users = get_resource_users(notification.task.resource) | ||
| 39 | + notified = Notification.objects.filter(user__in = users.values_list('id', flat = True), creation_date = datetime.now(), task = notification.task).count() | ||
| 40 | + | ||
| 41 | + number_users = users.count() | ||
| 42 | + | ||
| 43 | + not_done = (notified * 100) / number_users | ||
| 44 | + | ||
| 45 | + return 100 - not_done | ||
| 0 | \ No newline at end of file | 46 | \ No newline at end of file |
notifications/views.py
| 1 | -from django.shortcuts import render | 1 | +from django.shortcuts import get_object_or_404, redirect, render |
| 2 | +from django.views import generic | ||
| 3 | +from django.contrib import messages | ||
| 4 | +from django.core.urlresolvers import reverse, reverse_lazy | ||
| 5 | +from django.utils.translation import ugettext_lazy as _ | ||
| 6 | +from django.contrib.auth.mixins import LoginRequiredMixin | ||
| 2 | 7 | ||
| 3 | -# Create your views here. | 8 | +from datetime import datetime |
| 9 | + | ||
| 10 | +from amadeus.permissions import has_subject_view_permissions | ||
| 11 | + | ||
| 12 | +from subjects.models import Subject | ||
| 13 | + | ||
| 14 | +from .models import Notification | ||
| 15 | + | ||
| 16 | +class SubjectNotifications(LoginRequiredMixin, generic.ListView): | ||
| 17 | + login_url = reverse_lazy("users:login") | ||
| 18 | + redirect_field_name = 'next' | ||
| 19 | + | ||
| 20 | + context_object_name = 'notifications' | ||
| 21 | + template_name = 'notifications/subject.html' | ||
| 22 | + paginate_by = 10 | ||
| 23 | + | ||
| 24 | + def dispatch(self, request, *args, **kwargs): | ||
| 25 | + slug = self.kwargs.get('slug', '') | ||
| 26 | + subject = get_object_or_404(Subject, slug = slug) | ||
| 27 | + | ||
| 28 | + if not has_subject_view_permissions(request.user, subject): | ||
| 29 | + return redirect(reverse_lazy('subjects:home')) | ||
| 30 | + | ||
| 31 | + return super(SubjectNotifications, self).dispatch(request, *args, **kwargs) | ||
| 32 | + | ||
| 33 | + def get_queryset(self): | ||
| 34 | + slug = self.kwargs.get('slug', '') | ||
| 35 | + subject = get_object_or_404(Subject, slug = slug) | ||
| 36 | + | ||
| 37 | + notifications = Notification.objects.filter(user = self.request.user, task__resource__topic__subject = subject, creation_date = datetime.now()) | ||
| 38 | + | ||
| 39 | + return notifications | ||
| 40 | + | ||
| 41 | + def get_context_data(self, **kwargs): | ||
| 42 | + context = super(SubjectNotifications, self).get_context_data(**kwargs) | ||
| 43 | + | ||
| 44 | + slug = self.kwargs.get('slug', '') | ||
| 45 | + subject = get_object_or_404(Subject, slug = slug) | ||
| 46 | + | ||
| 47 | + context['title'] = _('%s - Pendencies')%(subject.name) | ||
| 48 | + context['subject'] = subject | ||
| 49 | + | ||
| 50 | + return context |
subjects/templates/subjects/subject_card.html
| @@ -37,7 +37,7 @@ | @@ -37,7 +37,7 @@ | ||
| 37 | 37 | ||
| 38 | 38 | ||
| 39 | <a href="" class="pull-right action_icon"><i class="fa fa-bar-chart" aria-hidden="true"></i></a> | 39 | <a href="" class="pull-right action_icon"><i class="fa fa-bar-chart" aria-hidden="true"></i></a> |
| 40 | - <a href="" class="pull-right action_icon"> | 40 | + <a href="{% url 'notifications:view' subject.slug %}" class="pull-right action_icon"> |
| 41 | <i class="fa fa-exclamation-triangle" aria-hidden="true"></i> | 41 | <i class="fa fa-exclamation-triangle" aria-hidden="true"></i> |
| 42 | {% notifies_number subject request.user %} | 42 | {% notifies_number subject request.user %} |
| 43 | </a> | 43 | </a> |
subjects/templates/subjects/view.html
| @@ -56,7 +56,7 @@ | @@ -56,7 +56,7 @@ | ||
| 56 | </ul> | 56 | </ul> |
| 57 | {% endif %} | 57 | {% endif %} |
| 58 | <a href="" class="pull-right action_icon"><i class="fa fa-bar-chart" aria-hidden="true"></i></a> | 58 | <a href="" class="pull-right action_icon"><i class="fa fa-bar-chart" aria-hidden="true"></i></a> |
| 59 | - <a href="" class="pull-right action_icon"> | 59 | + <a href="{% url 'notifications:view' subject.slug %}" class="pull-right action_icon"> |
| 60 | <i class="fa fa-exclamation-triangle" aria-hidden="true"></i> | 60 | <i class="fa fa-exclamation-triangle" aria-hidden="true"></i> |
| 61 | {% notifies_number subject request.user %} | 61 | {% notifies_number subject request.user %} |
| 62 | </a> | 62 | </a> |
subjects/views.py
| @@ -46,7 +46,7 @@ class HomeView(LoginRequiredMixin, ListView): | @@ -46,7 +46,7 @@ class HomeView(LoginRequiredMixin, ListView): | ||
| 46 | 46 | ||
| 47 | subjects = Subject.objects.filter(Q(students__pk=pk) | Q(professor__pk=pk) | Q(category__coordinators__pk=pk)).distinct() | 47 | subjects = Subject.objects.filter(Q(students__pk=pk) | Q(professor__pk=pk) | Q(category__coordinators__pk=pk)).distinct() |
| 48 | 48 | ||
| 49 | - self.total = len(subjects) | 49 | + self.total = subjects.count() |
| 50 | 50 | ||
| 51 | return subjects | 51 | return subjects |
| 52 | 52 |
topics/templates/topics/list.html
| @@ -34,7 +34,7 @@ | @@ -34,7 +34,7 @@ | ||
| 34 | </div> | 34 | </div> |
| 35 | </div> | 35 | </div> |
| 36 | </div> | 36 | </div> |
| 37 | - <div id="{{topic.slug}}" class="panel-collapse collapse category-panel-content topic-panel"> | 37 | + <div id="{{topic.slug}}" class="panel-collapse collapse category-panel-content"> |
| 38 | <input type="hidden" class="id_inp" name="id" value="{{ topic.id }}" /> | 38 | <input type="hidden" class="id_inp" name="id" value="{{ topic.id }}" /> |
| 39 | <input type="hidden" class="order_inp" name="order" value="{{ topic.order }}" /> | 39 | <input type="hidden" class="order_inp" name="order" value="{{ topic.order }}" /> |
| 40 | 40 |