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 |