2 Commits bd9b81c2f0 ... 5ca005008e

Author SHA1 Message Date
  Élie Bouttier 5ca005008e gestion des tâches 6 years ago
  Élie Bouttier bd9b81c2f0 gestion des tâches 6 years ago

+ 0 - 6
todo/admin.py

@@ -8,10 +8,4 @@ class TaskListAdmin(admin.ModelAdmin):
     prepopulated_fields = {'slug': ('name',)}
 
 
-#class TaskAdmin(admin.ModelAdmin):
-#    list_display = ('task_list', 'title',)
-#'author', 'data', 'snippet')
-
-
 admin.site.register(TaskList, TaskListAdmin)
-#admin.site.register(Task, TaskAdmin)

+ 27 - 2
todo/forms.py

@@ -1,9 +1,34 @@
 from django import forms
 
-from .models import Task
+from adhesions.models import User
+from .models import Task, TaskComment
 
 
 class TaskForm(forms.ModelForm):
+    def __init__(self, *args, **kwargs):
+        tasklist = kwargs.pop('tasklist')
+        super().__init__(*args, **kwargs)
+        if tasklist.groups.exists():
+            members = User.objects.filter(groups__in=tasklist.groups.all())
+        else:
+            members = User.objects.all()
+        members = members.order_by('adhesion__id')
+        members = members.select_related('adhesion')
+        self.fields['assigned_to'].queryset = members
+        self.fields["assigned_to"].label_from_instance = lambda obj: "ADT%d %s" % (
+            obj.adhesion.id,
+            str(obj.profile),
+        )
+
     class Meta:
         model = Task
-        fields = ('title', 'note', 'due_date',) # TODO: add assigned_to, notify
+        fields = ('title', 'note', 'due_date', 'assigned_to',)
+        widgets = {
+            'due_date': forms.DateInput(attrs={"type": "date"}),
+        }
+
+
+class CommentForm(forms.ModelForm):
+    class Meta:
+        model = TaskComment
+        fields = ('body',)

+ 67 - 0
todo/migrations/0001_initial.py

@@ -0,0 +1,67 @@
+# Generated by Django 2.2 on 2019-04-19 22:16
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+import django.utils.timezone
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = [
+        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+        ('auth', '0011_update_proxy_permissions'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='Task',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('title', models.CharField(max_length=140, verbose_name='titre')),
+                ('created_date', models.DateField(blank=True, default=django.utils.timezone.now, null=True)),
+                ('due_date', models.DateField(blank=True, null=True, verbose_name='due pour le')),
+                ('completed_date', models.DateField(blank=True, null=True)),
+                ('note', models.TextField(blank=True, null=True)),
+                ('priority', models.PositiveIntegerField(blank=True, null=True)),
+                ('assigned_to', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='assigned_task_set', to=settings.AUTH_USER_MODEL, verbose_name='assignée à')),
+                ('created_by', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='created_task_set', to=settings.AUTH_USER_MODEL, verbose_name='créée par')),
+            ],
+            options={
+                'verbose_name': 'tâche',
+                'verbose_name_plural': 'tâches',
+                'ordering': ['priority', 'created_date'],
+            },
+        ),
+        migrations.CreateModel(
+            name='TaskList',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('name', models.CharField(max_length=60, verbose_name='nom')),
+                ('slug', models.SlugField(unique=True)),
+                ('groups', models.ManyToManyField(blank=True, to='auth.Group', verbose_name='groupe')),
+            ],
+            options={
+                'verbose_name': 'liste de tâches',
+                'verbose_name_plural': 'listes de tâches',
+                'ordering': ['name'],
+            },
+        ),
+        migrations.CreateModel(
+            name='TaskComment',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('date', models.DateTimeField(default=django.utils.timezone.now)),
+                ('body', models.TextField(blank=True)),
+                ('author', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)),
+                ('task', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comments', to='todo.Task')),
+            ],
+        ),
+        migrations.AddField(
+            model_name='task',
+            name='task_list',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='todo.TaskList'),
+        ),
+    ]

+ 0 - 0
todo/migrations/__init__.py


+ 20 - 3
todo/models.py

@@ -3,6 +3,8 @@ from django.utils import timezone
 from django.conf import settings
 from django.contrib.auth.models import Group
 
+import datetime
+
 import textwrap
 
 
@@ -11,6 +13,14 @@ class TaskList(models.Model):
     slug = models.SlugField(null=False, blank=False, unique=True)
     groups = models.ManyToManyField(Group, verbose_name='groupe', blank=True)
 
+    @property
+    def completed_task_set(self):
+        return self.task_set.filter(completed_date__isnull=False)
+
+    @property
+    def uncompleted_task_set(self):
+        return self.task_set.filter(completed_date__isnull=True)
+
     def __str__(self):
         return self.name
 
@@ -21,15 +31,16 @@ class TaskList(models.Model):
 
 
 class Task(models.Model):
-    title = models.CharField(max_length=140)
+    title = models.CharField(max_length=140, verbose_name='titre')
     task_list = models.ForeignKey(TaskList, on_delete=models.PROTECT)
     created_date = models.DateField(default=timezone.now, blank=True, null=True)
-    due_date = models.DateField(blank=True, null=True)
+    due_date = models.DateField(blank=True, null=True, verbose_name='due pour le')
     completed_date = models.DateField(blank=True, null=True)
     created_by = models.ForeignKey(
         settings.AUTH_USER_MODEL,
         on_delete=models.PROTECT,
         related_name='created_task_set',
+        verbose_name='créée par',
     )
     assigned_to = models.ForeignKey(
         settings.AUTH_USER_MODEL,
@@ -37,10 +48,16 @@ class Task(models.Model):
         null=True,
         on_delete=models.SET_NULL,
         related_name='assigned_task_set',
+        verbose_name='assignée à',
     )
     note = models.TextField(blank=True, null=True)
     priority = models.PositiveIntegerField(blank=True, null=True)
 
+    def overdue_status(self):
+        "Returns whether the Tasks's due date has passed or not."
+        if self.due_date and datetime.date.today() > self.due_date:
+            return True
+
     def __str__(self):
         return self.title
 
@@ -52,7 +69,7 @@ class Task(models.Model):
 
 class TaskComment(models.Model):
     author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT)
-    task = models.ForeignKey(Task, on_delete=models.CASCADE)
+    task = models.ForeignKey(Task, on_delete=models.CASCADE, related_name='comments')
     date = models.DateTimeField(default=timezone.now)
     body = models.TextField(blank=True)
 

+ 0 - 8
todo/templates/todo/_task_edit.html

@@ -1,8 +0,0 @@
-{# Form used by both Add Task and Edit Task views #}
-{% load bootstrap4 %}
-
-<form action="" name="add_task" method="post">
-  {% csrf_token %}
-  {% bootstrap_form form %}
-  {% buttons submit="Ajouter" reset="Annuler" %}{% endbuttons %}
-</form>

+ 0 - 10
todo/templates/todo/task_add.html

@@ -1,10 +0,0 @@
-{% extends "base.html" %}
-
-{% block todotab %} active{% endblock %}
-
-{% block content %}
-  <h1>Ajouter une tâche</h1>
-
-  {% include 'todo/_task_edit.html' %}
-
-{% endblock %}

+ 33 - 82
todo/templates/todo/task_detail.html

@@ -1,20 +1,5 @@
 {% extends "todo/base.html" %}
-
-{% block extrahead %}
-<style>
-.select2 {
-    width: 100% !important;
-}
-
-.select2-container {
-    min-width: 0 !important;
-}
-</style>
-{{ form.media }}
-{{ merge_form.media }}
-{% endblock %}
-
-
+{% load bootstrap4 %}
 
 {% block content %}
   <div class="card-deck">
@@ -30,123 +15,89 @@
     <div class="card col-sm-4 p-0">
       <ul class="list-group list-group-flush">
         <li class="list-group-item">
-          <button
-            class="btn btn-sm btn-primary"
-            id="EditTaskButton"
-            type="button"
-            data-toggle="collapse"
-            data-target="#TaskEdit">
-            Edit Task
-          </button>
+          <a href="{% url 'todo:edit-task' tasklist.slug task.id %}" class="btn btn-sm btn-primary">É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 %} Mark Not Done {% else %} Mark Done {% endif %}
+                {% if task.completed_date %} Marquer non terminée {% else %} Marquer terminée {% endif %}
               </button>
             </div>
           </form>
 
+          {% comment %}
           <form method="post" action="{% url "todo:delete-task" tasklist.slug task.id %}" role="form" class="d-inline">
             {% csrf_token %}
             <div style="display:inline;">
               <button class="btn btn-danger btn-sm" type="submit" name="submit_delete">
-                Delete
+                Supprimer
               </button>
             </div>
           </form>
+          {% endcomment %}
         </li>
         <li class="list-group-item">
-          <strong>Assigned to:</strong>
-          {% if task.assigned_to %} {{ task.assigned_to.get_full_name }} {% else %} Anyone {% endif %}
+          <strong>Assignée à :</strong>
+          {% if task.assigned_to %}{{ task.assigned_to.profile }}{% else %}–{% endif %}
         </li>
         <li class="list-group-item">
-          <strong>Reported by:</strong> {{ task.created_by.get_full_name }}
+          <strong>Créée par :</strong> {{ task.created_by.profile }}
         </li>
         <li class="list-group-item">
-          <strong>Due date:</strong> {{ task.due_date }}
+          <strong>Due pour le :</strong>
+          {% if task.overdue_status %}
+          <span class="text-danger">{{ task.due_date|default:"–" }}</span>
+          {% else %}
+          {{ task.due_date|default:"–" }}
+          {% endif %}
+        </li>
+        <li class="list-group-item">
+          <strong>Terminée le :</strong> {{ task.completed_date|default:"–" }}
         </li>
-
-        {% if task.completed %}
-          <li class="list-group-item">
-            <strong>Completed on:</strong> {{ task.completed_date}}
-          </li>
-        {% else %}
-          <li class="list-group-item">
-            <strong>Completed:</strong> {{ task.completed|yesno:"Yes,No" }}
-          </li>
-        {% endif %}
-
         <li class="list-group-item">
-          <strong>In list:</strong>
-          <a href="{% url 'todo:show-tasklist' tasklist.slug %}">
-            {{ task.task_list }}
-          </a>
+          <strong>Dans la liste :</strong>
+          {% if task.completed_date %}
+          <a href="{% url 'todo:show-tasklist-completed' tasklist.slug %}">{{ task.task_list }}</a>
+          {% else %}
+          <a href="{% url 'todo:show-tasklist' tasklist.slug %}">{{ task.task_list }}</a>
+          {% endif %}
         </li>
       </ul>
     </div>
   </div>
 
-  <div id="TaskEdit" class="collapse">
-    {# Task edit / new task form #}
-    {% include 'todo/include/task_edit.html' %}
-    {% if merge_form is not None %}
-    <form action="" method="post">
-      <div class="card border-danger">
-      <div class="card-header">Merge task</div>
-      <div class="card-body">
-        <div class="">
-          <p>Merging is a destructive operation. This task will not exist anymore, and comments will be moved to the target task.</p>
-        {% csrf_token %}
-        {% for field in merge_form.visible_fields %}
-        <p>
-          {{ field.errors }}
-          {{ field }}
-        </p>
-        {% endfor %}
-        <input class="d-inline btn btn-sm btn-outline-danger" type="submit" name="merge_task_into" value="Merge">
-      </div>
-      </div>
-      </div>
-    </form>
-    {% endif %}
-  </div>
-
   <div class="mt-3">
-    <h5>Add comment</h5>
+    <h5>Ajouter un commentaire</h5>
     <form action="" method="post">
       {% csrf_token %}
       <div class="form-group">
-        <textarea class="form-control" name="comment-body" rows="3" required></textarea>
+        <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="Add Comment">
+      <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 comment_list %}
-      <h5>Comments on this task</h5>
-      {% for comment in comment_list %}
+    {% if task.comments.exists %}
+      <h5>Commentaires</h5>
+      {% for comment in task.comments.all %}
       <div class="mb-3 card">
         <div class="card-header">
           <div class="float-left">
-            {% if comment.email_message_id %}
-            <span class="badge badge-warning">email</span>
-            {% endif %}
-            {{ comment.author_text }}
+            {{ comment.author }}
           </div>
           <span class="float-right d-inline-block text-muted">
             {{ comment.date|date:"F d Y P" }}
           </span>
         </div>
-        <div class="{{ comment_classes | join:" " }} card-body">
+        <div class="card-body">
           {{ comment.body|safe|urlize|linebreaks }}
         </div>
       </div>
       {% endfor %}
     {% else %}
-        <h5>No comments (yet).</h5>
+        <h5>Pas de commentaire</h5>
     {% endif %}
   </div>
 {% endblock %}

+ 32 - 0
todo/templates/todo/task_form.html

@@ -0,0 +1,32 @@
+{% extends "base.html" %}
+{% load bootstrap4 %}
+
+{% block todotab %} active{% endblock %}
+
+{% block extrahead %}
+<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.6-rc.0/css/select2.min.css" rel="stylesheet" />
+<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.6-rc.0/js/select2.min.js"></script>
+<script type="text/javascript">
+$(document).ready(function() {
+    $('#id_assigned_to').select2();
+});
+</script>
+{% endblock %}
+
+{% block content %}
+  <h1>{% if task %}Ajouter{% else %}Modifier{% endif %} une tâche</h1>
+
+  <form action="" method="post">
+    {% csrf_token %}
+    {% bootstrap_form form %}
+    {% buttons %}
+    {% if task %}
+    {% bootstrap_button "Modifier" button_type="submit" button_class="btn-primary" %}
+    {% else %}
+    {% bootstrap_button "Ajouter" button_type="submit" button_class="btn-primary" %}
+    {% endif %}
+    {% bootstrap_button "Annuler" button_type="link" href=cancel_url %}
+    {% endbuttons %}
+  </form>
+
+{% endblock %}

+ 20 - 10
todo/templates/todo/tasklist_detail.html

@@ -5,21 +5,29 @@
   <a href="{% url 'todo:add-task' tasklist.slug %}" class="btn btn-primary">Ajouter une tâche</a>
   <hr />
 
-  {% if tasklist.task_set %}
+  {% if task_list %}
+      {% if completed %}
+      <h1>Tâches terminées de la liste « {{ tasklist.name }} »</h1>
+      {% else %}
       <h1>Tâches de la liste « {{ tasklist.name }} »</h1>
       <p><small><i>Faites glisser les tâches pour définir les priorités.</i></small></p>
+      {% endif %}
 
       <table class="table" id="tasktable">
         <tr class="nodrop">
           <th>Tâche</th>
           <th>Créé le</th>
+          {% if completed %}
+          <th>Terminée le</th>
+          {% else %}
           <th>Due pour le</th>
+          {% endif %}
           <th>Créé par</th>
-          {% comment %}<th>Assigné à</th>{% endcomment %}
+          <th>Assigné à</th>
           {% comment %}<th>Mark</th{% endcomment %}
         </tr>
 
-        {% for task in tasklist.task_set.all %}
+        {% for task in task_list %}
           <tr id="{{ task.id }}">
             <td>
                 <a href="{% url 'todo:show-task' tasklist.slug task.id %}">{{ task.title|truncatewords:10 }}</a>
@@ -28,17 +36,17 @@
                 {{ task.created_date|date:"m/d/Y" }}
             </td>
             <td>
-              <span {% if task.overdue_status %}class="overdue"{% endif %}>
+              <span{% if task.overdue_status %} class="text-danger"{% endif %}>
                 {{ task.due_date|date:"m/d/Y"|default:"–" }}
               </span>
             </td>
             <td>
               {{ task.created_by }}
             </td>
-            {% comment %}
             <td>
-              {% if task.assigned_to %}{{ task.assigned_to }}{% else %}Anyone{% endif %}
+              {% if task.assigned_to %}{{ task.assigned_to }}{% else %}{% endif %}
             </td>
+            {% comment %}
             <td>
               <form method="post" action="{% url "todo:task_toggle_done" task.id %}" role="form">
                 {% csrf_token %}
@@ -56,12 +64,14 @@
         {% endfor %}
       </table>
 
-      {% comment %}{% include 'todo/include/toggle_delete.html' %}{% endcomment %}
-
   {% else %}
-    <h4>Il n’y a aucune tâche dans cette liste.</h4>
-    {% comment %}{% include 'todo/include/toggle_delete.html' %}{% endcomment %}
+    <h4>Aucune tâche {% if completed %}terminée{% else %}en cours{% endif %} dans la liste « {{ tasklist.name }} ».</h4>
+  {% 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>
+  {% else %}
+  <a href="{% url 'todo:show-tasklist-completed' tasklist.slug %}" class="btn btn-sm btn-warning">Voir les tâches terminées</a>
   {% endif %}
 
 {% endblock %}

+ 1 - 1
todo/templates/todo/tasklist_list.html

@@ -9,7 +9,7 @@
     {% for list in lists %}
     <li class="list-group-item d-flex justify-content-between align-items-center">
       <a href="{% url 'todo:show-tasklist' list.slug %}">{{ list.name }}</a>
-      <span class="badge badge-primary badge-pill">{{ list.task_set.count }}</span>
+      <span class="badge badge-primary badge-pill">{{ list.uncompleted_task_set.count }}</span>
     </li>
     {% endfor %}
   </ul>

+ 3 - 2
todo/urls.py

@@ -9,10 +9,11 @@ app_name = 'todo'
 urlpatterns = [
     path('', views.tasklist_list, name='list-tasklists'),
     path('<str:tasklist_slug>/', views.tasklist_detail, name='show-tasklist'),
-    path('<str:tasklist_slug>/delete/', views.tasklist_delete, name='delete-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>/add/', views.task_add, name='add-task'),
+    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>/edit/', views.task_form, name='edit-task'),
     path('<str:tasklist_slug>/<int:task_id>/delete/', views.task_delete, name='delete-task'),
 ]

+ 67 - 23
todo/views.py

@@ -5,9 +5,11 @@ from django.contrib import messages
 from django.urls import reverse
 from django.http import HttpResponse
 from django.views.decorators.csrf import csrf_exempt
+from django.views.decorators.http import require_POST
+from django.utils import timezone
 
 from .models import TaskList, Task
-from .forms import TaskForm
+from .forms import TaskForm, CommentForm
 from .decorators import allowed_tasklist_required
 
 
@@ -19,24 +21,22 @@ def tasklist_list(request):
     lists = lists.order_by('name')
     return render(request, 'todo/tasklist_list.html', {
         'lists': lists,
-        'task_count': Task.objects.filter(task_list__in=lists).count(), ## TODO check it works properly
+        'task_count': Task.objects.filter(completed_date__isnull=True, task_list__in=lists).count(),
         'list_count': lists.count(),
     })
 
 
 @allowed_tasklist_required
-def tasklist_detail(request, tasklist):
+def tasklist_detail(request, tasklist, completed=False):
+    task_list = tasklist.task_set.filter(completed_date__isnull=not completed)
     return render(request, 'todo/tasklist_detail.html', {
         'tasklist': tasklist,
+        'task_list': task_list,
+        'completed': completed,
     })
 
 
 @allowed_tasklist_required
-def tasklist_delete(request, tasklist):
-    pass
-
-
-@allowed_tasklist_required
 def tasklist_reorder(request, tasklist):
     newtasklist = request.POST.getlist("tasktable[]")
     if newtasklist:
@@ -58,36 +58,80 @@ def tasklist_reorder(request, tasklist):
 
 
 @allowed_tasklist_required
-def task_add(request, tasklist):
-    form = TaskForm(request.POST or None)
+def task_form(request, tasklist, task_id=None):
+    if task_id:
+        task = get_object_or_404(Task, task_list=tasklist, pk=task_id)
+        redirect_url = reverse('todo:show-task', kwargs={'tasklist_slug': tasklist.slug, 'task_id': task.pk})
+    else:
+        task = None
+        redirect_url = reverse('todo:show-tasklist', kwargs={'tasklist_slug': tasklist.slug})
+    form = TaskForm(request.POST or None, tasklist=tasklist, instance=task)
     if request.method == 'POST' and form.is_valid():
-        task = form.save(commit=False)
-        task.task_list = tasklist
-        task.created_by = request.user
-        task.save()
-        messages.success(request, 'Tâche créée avec succès.')
-        return redirect(reverse('todo:show-tasklist', kwargs={'tasklist_slug': tasklist.slug}))
-    return render(request, 'todo/task_add.html', {
+        if task:
+            form.save()
+            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()
+            messages.success(request, 'Tâche créée avec succès.')
+        return redirect(redirect_url)
+    return render(request, 'todo/task_form.html', {
         'tasklist': tasklist,
+        'task': task,
         'form': form,
+        'cancel_url': redirect_url,
     })
 
 
 @allowed_tasklist_required
 def task_detail(request, tasklist, task_id):
-    task = get_object_or_404(Task, pk=task_id)
+    task = get_object_or_404(Task, task_list=tasklist, pk=task_id)
+    form = CommentForm(request.POST or None)
+    if request.method == 'POST' and form.is_valid():
+        comment = form.save(commit=False)
+        comment.task = task
+        comment.author = request.user
+        comment.save()
+        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,
     })
-    
+
 
 @allowed_tasklist_required
 def task_toggle_done(request, tasklist, task_id):
-    task = get_object_or_404(Task, pk=task_id)
-    return redirect() # TODO
+    task = get_object_or_404(Task, task_list=tasklist, pk=task_id)
+    if task.completed_date:
+        task.completed_date = None
+        messages.success(request, "La tâche « {} » a été marquée en cours.".format(task.title))
+    else:
+        task.completed_date = timezone.now()
+        messages.success(request, "La tâche « {} » a été marquée complétée.".format(task.title))
+    task.save()
+    return redirect(reverse('todo:show-task', kwargs={'tasklist_slug': tasklist.slug, 'task_id': task_id}))
 
 
 @allowed_tasklist_required
-def task_delete(request, tasklist):
-    pass
+def task_edit(request, tasklist, task_id):
+    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))
+    return redirect(reverse('todo:show-task', kwargs={'tasklist_slug': tasklist.slug, 'task_id': task_id}))
+
+
+# TODO: are you sure?
+@require_POST
+@allowed_tasklist_required
+def task_delete(request, tasklist, task_id):
+    task = get_object_or_404(Task, task_list=tasklist, pk=task_id)
+    task.delete()
+    messages.success(request, "La tâche « {} » a été supprimée.".format(task.title))
+    return redirect(reverse('todo:show-tasklist', kwargs={'tasklist_slug': tasklist.slug}))