Commit 3764c7fd authored by Élie Bouttier's avatar Élie Bouttier

notification par e-mail

parent fea96244
from django.conf import settings
from django.core import mail
from django.template.loader import render_to_string
from django.contrib.auth import get_user_model
import hashlib
from .models import TaskComment
def send_task_notification(task, user, created):
if not created:
return
subject = settings.EMAIL_SUBJECT_PREFIX + task.title
context = {
'task': task,
'body': task.note,
'domain': settings.TODO_DOMAIN,
'user': user,
}
txt_body = render_to_string('todo/email/newtask.txt', context)
to_addresses = task.watchers \
.exclude(email='') \
.values_list('email', flat=True)
send_task_email(task, user, subject, txt_body, to_addresses)
def send_comment_notification(comment, user):
task = comment.task
subject = 'Re: ' + settings.TODO_SUBJECT_PREFIX + task.title
context = {
'task': task,
'body': comment.body,
'domain': settings.TODO_DOMAIN,
'user': user,
}
txt_body = render_to_string('todo/email/newcomment.txt', context)
to_addresses = task.watchers \
.exclude(email='') \
.values_list('email', flat=True)
send_task_email(task, user, subject, txt_body, to_addresses, comment=comment)
def send_task_email(task, user, subject, body, to_addresses, alternatives=[], comment=None):
from_address = '{} <{}>'.format(user.profile, settings.DEFAULT_FROM_EMAIL)
thread_id = '<todo/{}@{}>'.format(task.pk, settings.TODO_DOMAIN)
if comment:
headers = {
'Message-ID': '<todo/{}/{}@{}>'.format(task.pk, comment.pk, settings.TODO_DOMAIN),
'References': thread_id,
'In-reply-to': thread_id,
}
else:
headers = {
'Message-ID': thread_id,
}
with mail.get_connection() as connection:
message = mail.EmailMultiAlternatives(
subject=subject,
body=body,
from_email=from_address,
to=to_addresses,
connection=connection,
headers=headers,
)
for alternative_content, alternative_mime in alternatives:
message.attach_alternative(alternative_content, alternative_mime)
message.send()
# Generated by Django 2.2 on 2019-05-11 13:31
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('todo', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='task',
name='subscribers',
field=models.ManyToManyField(blank=True, related_name='subscribed_tasks', to=settings.AUTH_USER_MODEL),
),
migrations.AddField(
model_name='task',
name='unsubscribers',
field=models.ManyToManyField(blank=True, related_name='unsubscribed_tasks', to=settings.AUTH_USER_MODEL),
),
migrations.AddField(
model_name='tasklist',
name='subscribers',
field=models.ManyToManyField(blank=True, related_name='subscribed_tasklists', to=settings.AUTH_USER_MODEL),
),
]
from django.db import models
from django.db.models import Q
from django.utils import timezone
from django.conf import settings
from django.contrib.auth.models import Group
from django.contrib.auth import get_user_model
import datetime
......@@ -12,6 +14,11 @@ class TaskList(models.Model):
name = models.CharField(max_length=60, verbose_name='nom')
slug = models.SlugField(null=False, blank=False, unique=True)
groups = models.ManyToManyField(Group, verbose_name='groupe', blank=True)
subscribers = models.ManyToManyField(
settings.AUTH_USER_MODEL,
blank=True,
related_name='subscribed_tasklists',
)
@property
def completed_task_set(self):
......@@ -52,6 +59,29 @@ class Task(models.Model):
)
note = models.TextField(blank=True, null=True)
priority = models.PositiveIntegerField(blank=True, null=True)
subscribers = models.ManyToManyField(
settings.AUTH_USER_MODEL,
blank=True,
related_name='subscribed_tasks',
)
unsubscribers = models.ManyToManyField(
settings.AUTH_USER_MODEL,
blank=True,
related_name='unsubscribed_tasks',
)
def subscribed(self, user):
if user in self.task_list.subscribers.all():
return user not in self.unsubscribers.all()
else:
return user in self.subscribers.all()
@property
def watchers(self):
return get_user_model().objects.filter(
( Q(subscribed_tasklists=self.task_list) & ~Q(unsubscribed_tasks=self)) \
| (~Q(subscribed_tasklists=self.task_list) & Q(subscribed_tasks=self))
)
def overdue_status(self):
"Returns whether the Tasks's due date has passed or not."
......
Un nouveau commentaire a été ajouté.
Tâche : {{ task.title }}
Commentateur : {{ user.profile }}
Commentaire :
{% autoescape off %}
{{ body }}
{% endautoescape %}
Tâche :
https://{{ domain }}{% url 'todo:show-task' task.task_list.slug task.id %}
Liste « {{ task.task_list.name }} » :
https://{{ domain }}{% url 'todo:show-tasklist' task.task_list.slug %}
Une nouvelle tâche a été ajoutée.
Tâche : {{ task.title }}
Auteur : {{ user.profile }}
{% if task.assigned_to %}Assigné à : {{ task.assigned_to }}
{% endif %}
Note :
{% autoescape off %}
{{ body }}
{% endautoescape %}
Tâche :
https://{{ domain }}{% url 'todo:show-task' task.task_list.slug task.id %}
Liste « {{ task.task_list.name }} » :
https://{{ domain }}{% url 'todo:show-tasklist' task.task_list.slug %}
......@@ -15,17 +15,35 @@
<div class="card col-sm-4 p-0">
<ul class="list-group list-group-flush">
<li class="list-group-item">
<a href="{% url 'todo:edit-task' tasklist.slug task.id %}" class="btn btn-sm btn-primary">Éditer</a>
<a href="{% url 'todo:edit-task' tasklist.slug task.id %}" class="btn btn-sm btn-primary">
<span class="fas fa-pen"></span>&nbsp;Éditer
</a>
<form method="post" action="{% url "todo:toggle-task-done" tasklist.slug task.id %}" role="form" class="d-inline">
{% csrf_token %}
<div style="display:inline;">
<button class="btn btn-info btn-sm" type="submit" name="toggle_done">
{% if task.completed_date %} Marquer non terminée {% else %} Marquer terminée {% endif %}
{% if task.completed_date %}
<button class="btn btn-danger btn-sm" type="submit" name="toggle_done">
<span class="far fa-square"></span>&nbsp;Non terminée
</button>
{% else %}
<button class="btn btn-success btn-sm" type="submit" name="toggle_done">
<span class="far fa-check-square"></span>&nbsp;Terminée
</button>
{% endif %}
</div>
</form>
{% if subscribed %}
<a href="{% url 'todo:unsubscribe-task' tasklist.slug task.id %}" class="btn btn-sm btn-secondary">
<span class="fas fa-eye-slash"></span>&nbsp;Se désabonner
</a>
{% else %}
<a href="{% url 'todo:subscribe-task' tasklist.slug task.id %}" class="btn btn-sm btn-secondary">
<span class="fas fa-eye"></span>&nbsp;S’abonner
</a>
{% endif %}
{% comment %}
<form method="post" action="{% url "todo:delete-task" tasklist.slug task.id %}" role="form" class="d-inline">
{% csrf_token %}
......@@ -67,17 +85,6 @@
</div>
</div>
<div class="mt-3">
<h5>Ajouter un commentaire</h5>
<form action="" method="post">
{% csrf_token %}
<div class="form-group">
<textarea name="body" rows="3" class="form-control" title="" id="id_body"></textarea>
</div>
<input class="btn btn-sm btn-primary" type="submit" name="add_comment" value="Ajouter un commentaire">
</form>
</div>
<div class="task_comments mt-4">
{% if task.comments.exists %}
<h5>Commentaires</h5>
......@@ -100,4 +107,15 @@
<h5>Pas de commentaire</h5>
{% endif %}
</div>
<div class="mt-3">
<h5>Ajouter un commentaire</h5>
<form action="" method="post">
{% csrf_token %}
<div class="form-group">
<textarea name="body" rows="3" class="form-control" title="" id="id_body"></textarea>
</div>
<input class="btn btn-sm btn-primary" type="submit" name="add_comment" value="Ajouter un commentaire">
</form>
</div>
{% endblock %}
......@@ -14,7 +14,7 @@ $(document).ready(function() {
{% endblock %}
{% block content %}
<h1>{% if task %}Ajouter{% else %}Modifier{% endif %} une tâche</h1>
<h1>{% if task %}Modifier{% else %}Ajouter{% endif %} une tâche</h1>
<form action="" method="post">
{% csrf_token %}
......
......@@ -2,7 +2,18 @@
{% block content %}
<a href="{% url 'todo:add-task' tasklist.slug %}" class="btn btn-primary">Ajouter une tâche</a>
<a href="{% url 'todo:add-task' tasklist.slug %}" class="btn btn-primary">
<span class="fas fa-plus"></span>&nbsp;Ajouter une tâche
</a>
{% if request.user in tasklist.subscribers.all %}
<a href="{% url 'todo:unsubscribe-tasklist' tasklist.slug %}" class="btn btn-secondary">
<span class="fas fa-eye-slash"></span>&nbsp;Se désabonner des notifications
</a>
{% else %}
<a href="{% url 'todo:subscribe-tasklist' tasklist.slug %}" class="btn btn-secondary">
<span class="fas fa-eye"></span>&nbsp;S’abonner aux notifications
</a>
{% endif %}
<hr />
{% if task_list %}
......@@ -69,9 +80,9 @@
{% endif %}
{% if completed %}
<a href="{% url 'todo:show-tasklist' tasklist.slug %}" class="btn btn-sm btn-warning">Voir les tâches non terminées</a>
<a href="{% url 'todo:show-tasklist' tasklist.slug %}" class="btn btn-info">Voir les tâches non terminées</a>
{% else %}
<a href="{% url 'todo:show-tasklist-completed' tasklist.slug %}" class="btn btn-sm btn-warning">Voir les tâches terminées</a>
<a href="{% url 'todo:show-tasklist-completed' tasklist.slug %}" class="btn btn-info">Voir les tâches terminées</a>
{% endif %}
{% endblock %}
......
from django.test import TestCase
from django.contrib.auth import get_user_model
# Create your tests here.
from .models import TaskList, Task
class TestTodo(TestCase):
def setUp(self):
User = get_user_model()
u1 = User.objects.create(username='u1', password='u1')
u2 = User.objects.create(username='u2', password='u2')
u3 = User.objects.create(username='u3', password='u3')
u4 = User.objects.create(username='u4', password='u4')
u5 = User.objects.create(username='u5', password='u5')
u6 = User.objects.create(username='u6', password='u6')
u7 = User.objects.create(username='u7', password='u7')
u8 = User.objects.create(username='u8', password='u8')
tasklist = TaskList.objects.create(name='todo')
tasklist.subscribers.add(u1)
tasklist.subscribers.add(u2)
tasklist.subscribers.add(u3)
tasklist.subscribers.add(u4)
task = Task.objects.create(title='Tâche 1', task_list=tasklist, created_by=u1)
task.subscribers.add(u1)
task.subscribers.add(u2)
task.subscribers.add(u5)
task.subscribers.add(u6)
task.unsubscribers.add(u1)
task.unsubscribers.add(u3)
task.unsubscribers.add(u5)
task.unsubscribers.add(u7)
def test_watchers(self):
User = get_user_model()
task = Task.objects.get(pk=1)
self.assertEqual(set(task.watchers.values_list('pk', flat=True)), {2, 4, 5, 6})
......@@ -11,9 +11,13 @@ urlpatterns = [
path('<str:tasklist_slug>/', views.tasklist_detail, name='show-tasklist'),
path('<str:tasklist_slug>/completed/', views.tasklist_detail, {'completed': True}, name='show-tasklist-completed'),
path('<str:tasklist_slug>/reorder/', views.tasklist_reorder, name='reorder-tasklist'),
path('<str:tasklist_slug>/subscribe/', views.tasklist_subscribe, {'subscribe': True}, name='subscribe-tasklist'),
path('<str:tasklist_slug>/unsubscribe/', views.tasklist_subscribe, {'subscribe': False}, name='unsubscribe-tasklist'),
path('<str:tasklist_slug>/add/', views.task_form, name='add-task'),
path('<str:tasklist_slug>/<int:task_id>/', views.task_detail, name='show-task'),
path('<str:tasklist_slug>/<int:task_id>/toggle-done/', views.task_toggle_done, name='toggle-task-done'),
path('<str:tasklist_slug>/<int:task_id>/subscribe/', views.task_subscribe, {'subscribe': True}, name='subscribe-task'),
path('<str:tasklist_slug>/<int:task_id>/unsubscribe/', views.task_subscribe, {'subscribe': False}, name='unsubscribe-task'),
path('<str:tasklist_slug>/<int:task_id>/edit/', views.task_form, name='edit-task'),
path('<str:tasklist_slug>/<int:task_id>/delete/', views.task_delete, name='delete-task'),
]
......@@ -12,6 +12,7 @@ from django.db.models import Count, Q
from .models import TaskList, Task
from .forms import TaskForm, CommentForm
from .decorators import allowed_tasklist_required
from .email import send_task_notification, send_comment_notification
@login_required
......@@ -59,6 +60,15 @@ def tasklist_reorder(request, tasklist):
return HttpResponse(status=201)
@allowed_tasklist_required
def tasklist_subscribe(request, tasklist, subscribe):
if subscribe:
tasklist.subscribers.add(request.user)
else:
tasklist.subscribers.remove(request.user)
return redirect(reverse('todo:show-tasklist', kwargs={'tasklist_slug': tasklist.slug}))
@allowed_tasklist_required
def task_form(request, tasklist, task_id=None):
if task_id:
......@@ -70,13 +80,21 @@ def task_form(request, tasklist, task_id=None):
form = TaskForm(request.POST or None, tasklist=tasklist, instance=task)
if request.method == 'POST' and form.is_valid():
if task:
form.save()
task = form.save()
task.subscribers.add(request.user)
if task.assigned_to and task.assigned_to not in task.unsubscribers.all():
task.subscribers.add(task.assigned_to)
send_task_notification(task, request.user, created=False)
messages.success(request, 'Tâche mise à jour avec succès.')
else:
task = form.save(commit=False)
task.task_list = tasklist
task.created_by = request.user
task.save()
task.subscribers.add(request.user)
if task.assigned_to and task.assigned_to not in task.unsubscribers.all():
task.subscribers.add(task.assigned_to)
send_task_notification(task, request.user, created=True)
messages.success(request, 'Tâche créée avec succès.')
return redirect(redirect_url)
return render(request, 'todo/task_form.html', {
......@@ -96,12 +114,15 @@ def task_detail(request, tasklist, task_id):
comment.task = task
comment.author = request.user
comment.save()
task.subscribers.add(request.user)
send_comment_notification(comment, request.user)
messages.success(request, 'Commentaire ajouté avec succès.')
return redirect(reverse('todo:show-task', kwargs={'tasklist_slug': tasklist.slug, 'task_id': task_id}))
return render(request, 'todo/task_detail.html', {
'tasklist': tasklist,
'task': task,
'form': form,
'subscribed': task.subscribed(request.user),
})
......@@ -119,13 +140,16 @@ def task_toggle_done(request, tasklist, task_id):
@allowed_tasklist_required
def task_edit(request, tasklist, task_id):
def task_subscribe(request, tasklist, task_id, subscribe):
task = get_object_or_404(Task, task_list=tasklist, pk=task_id)
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.save()
messages.success(request, "La tâche « {} » a été marquée complétée.".format(task.title))
if subscribe:
task.unsubscribers.remove(request.user)
#if request.user not in tasklist.subscribers.all():
task.subscribers.add(request.user)
else:
task.subscribers.remove(request.user)
#if request.user in tasklist.subscribers.all():
task.unsubscribers.add(request.user)
return redirect(reverse('todo:show-task', kwargs={'tasklist_slug': tasklist.slug, 'task_id': task_id}))
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment