فهرست منبع

Ajout du module notifications (en cours)

omassot 7 سال پیش
والد
کامیت
ac162da98e
5فایلهای تغییر یافته به همراه56 افزوده شده و 56 حذف شده
  1. 2 0
      backlog/settings.py
  2. 1 0
      backlog/urls.py
  3. 22 18
      main/models.py
  4. 17 1
      main/templates/_layout.html
  5. 14 37
      main/views.py

+ 2 - 0
backlog/settings.py

@@ -37,6 +37,7 @@ INSTALLED_APPS = [
     'django.contrib.messages',
     'django.contrib.staticfiles',
     'martor',
+    'notifications',
 ]
 
 MIDDLEWARE = [
@@ -126,6 +127,7 @@ LOGIN_REDIRECT_URL = '/'
 STATIC_URL = '/static/'
 STATIC_ROOT = os.path.join(BASE_DIR, 'main/static/')
 
+DJANGO_NOTIFICATIONS_CONFIG = { 'USE_JSONFIELD': True}
 
 MARTOR_ENABLE_CONFIGS = {
     'imgur': 'false',     # to enable/disable imgur/custom uploader.

+ 1 - 0
backlog/urls.py

@@ -22,4 +22,5 @@ urlpatterns = [
     path('admin/', admin.site.urls, name='admin'),
     path('accounts/', include('django.contrib.auth.urls')),
     path('martor/', include('martor.urls')),
+    path('inbox/notifications/', include(notifications.urls, namespace='notifications')),
 ]

+ 22 - 18
main/models.py

@@ -7,6 +7,7 @@ from django.core import serializers
 from django.db import models, connection
 from django.db.models.aggregates import Sum
 from martor.models import MartorField
+from notifications.signals import notify
 
 
 class Member(models.Model):
@@ -97,6 +98,14 @@ class Epic(BaseModel):
     def contributors_str(self):
         return ", ".join([c.member.trigram for c in self.contributors()])
     
+    def close(self):
+        self.closed = True
+        self.save()
+        
+    def reopen(self):
+        self.closed = False
+        self.save()
+        
 class Sprint(BaseModel):
     class Meta:
         verbose_name_plural = "sprint"
@@ -194,6 +203,19 @@ class Story(BaseModel):
                 return True
         return False
 
+    def contributors(self):
+        contributors = list(self.assignees.all())
+        if not self.author in contributors:
+            contributors += [self.author]
+        return contributors
+
+    def close(self):
+        self.closed = True
+        self.save()
+
+    def reopen(self):
+        self.closed = False
+        self.save()
 
 class Comment(BaseModel):
     class Meta:
@@ -209,21 +231,3 @@ class Comment(BaseModel):
     author = models.ForeignKey(User, 
                                on_delete=models.PROTECT, 
                                verbose_name="Auteur")
-    
-    
-class Notification(BaseModel):
-    class Meta:
-        verbose_name = "notification"
-        verbose_name_plural = "notifications"
-        ordering = ('-created', )
-    
-    title = models.CharField(max_length=200, default="")
-    description = models.TextField()
-    recipient = models.ForeignKey(User)
-    obj_type = models.IntegerField()
-    obj_id = models.IntegerField()
-    unread = models.BooleanField(default=True)
-    emailed = models.BooleanField(default=False)
-
-    def __str__(self):
-        return self.title

+ 17 - 1
main/templates/_layout.html

@@ -1,5 +1,7 @@
 
 {% load staticfiles %}
+{% load notifications_tags %}
+{% notifications_unread as notif_unread_count %}
 
 <!DOCTYPE html>
 <html lang="fr">
@@ -58,10 +60,24 @@
 	        		
 	        		<div id="notif-panel" class="dropdown">
 	        			<a href="#" id="notif-show-btn">
-	        				<i class="fa fa-bell"></i>0 <i class="fa fa-caret-down" style="margin-left: 0.5em;"></i>
+	        				<i class="fa fa-bell"></i>{{ notif_unread_count }} <i class="fa fa-caret-down" style="margin-left: 0.5em;"></i>
 	        			</a>
 	        		
 						<div id="notif-dropdown" class="dropdown-content">
+							<ul class="alt notif-list" style="margin-bottom: 0.5em;">
+							{% for notification in request.user.notifications.unread %}
+								<li class="notif">
+									<span class="flex-row" style="line-height: 2em;">
+										<i class="flex-extend"style="text-align:left; margin-left: 0.5em; padding: 3px;">Il y a {{ notification.timesince }}</i>
+										<a href="" class="notif-seen"><i class="fa fa-check"></i> Vu</a>
+									</span>
+									<span class="flex-row flex-space-around" style="padding: 0 1em;line-height: 2em;">{{ notification.description }}</span>
+								</li>
+							{% endfor %}
+								<li class="flex-row flex-space-around" style="line-height: 2em;padding:0;">
+									<a href="" class="notif-all-seen">Tout marquer comme vu</a>
+								</li>
+							</ul>
 						</div>
 					</div>
 	        		

+ 14 - 37
main/views.py

@@ -14,11 +14,6 @@ from main.forms import StoryForm, EpicForm, RegisterForm, ProfileForm, \
 from main.models import Story, Epic, Sprint, Comment, Project
 
 
-@login_required
-def index(request):
-    epics = Epic.objects.filter(closed=False)
-    return render(request, 'index.html', {'current_sprint': Sprint.current(), 'epics': epics})
-
 def register(request):
     if request.method == 'POST':
         form = RegisterForm(request.POST)
@@ -30,6 +25,11 @@ def register(request):
         form = RegisterForm()
     return render(request, 'registration/register.html', {'form': form})
 
+@login_required
+def index(request):
+    epics = Epic.objects.filter(closed=False)
+    return render(request, 'index.html', {'current_sprint': Sprint.current(), 'epics': epics})
+
 @login_required
 def profile_update(request):
     if request.method == 'POST':
@@ -158,35 +158,16 @@ def story_delete(request, story_id):
         story = get_object_or_404(Story, id=story_id)
         return render(request, 'deletion.html', {'object': story})
 
-def _story_close(request, story_id):
-    story = get_object_or_404(Story, id=story_id)
-    story.closed = True
-    story.save()
-    
-#     for u in story.assignees.all():
-#     for u in story.assignees.filter(id != request.user.id):
-#         notify.send(request.user, 
-#                     recipient=u, 
-#                     verb="Story clôturée",
-#                     action_object=story,
-#                     description="La story #{} a été clôturée".format(story.id))
-    return story
-
 @login_required
-def story_close(request, story_id):
-    story = _story_close(request, story_id)
+def story_close(_, story_id):
+    story = get_object_or_404(Story, id=story_id)
+    story.close()
     return redirect('epic_details', story.epic.id)
 
-def story_close_ajax(request, story_id):
-    story = _story_close(request, story_id)
-    return HttpResponse(story.to_json())
-
-@login_required
-def story_close_from_sprintend(_, story_id):
+def story_close_ajax(_, story_id):
     story = get_object_or_404(Story, id=story_id)
-    story.closed = True
-    story.save()
-    return redirect("sprint_end")
+    story.close()
+    return HttpResponse(story.to_json())
 
 @login_required
 def story_reaffect_ajax(_, story_id):
@@ -199,8 +180,7 @@ def story_reaffect_ajax(_, story_id):
 @login_required
 def story_reopen(_, story_id):
     story = get_object_or_404(Story, id=story_id)
-    story.closed = False
-    story.save()
+    story.reopen()
     return redirect('story_details', story_id)
 
 @login_required
@@ -263,15 +243,14 @@ def epic_value_update(request, epic_id):
 @login_required
 def epic_close(_, epic_id):
     epic = get_object_or_404(Epic, id=epic_id)
-    epic.closed = True
+    epic.close()
     epic.save()
     return redirect("backlog_editor")
 
 @login_required
 def epic_reopen(_, epic_id):
     epic = get_object_or_404(Epic, id=epic_id)
-    epic.closed = False
-    epic.save()
+    epic.reopen()
     return redirect("backlog_editor")
 
 @login_required
@@ -286,8 +265,6 @@ def report_sprints(request):
 @login_required
 def report_projects(request):
     epics = Epic.objects.filter(closed=False)
-    
-
     return render(request, 'reports/report_projects.html', {'epics': epics})
 
 def report_activity(request):