Rename Item model to Task

- With matching model, view, template changes
This commit is contained in:
Scot Hacker 2018-03-28 22:51:10 -07:00
parent a2d02b0a8c
commit d3d8d5e46c
15 changed files with 133 additions and 119 deletions

View file

@ -21,7 +21,6 @@ setup(
'License :: OSI Approved :: BSD License',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 3',
'Topic :: Office/Business :: Groupware',
'Topic :: Software Development :: Bug Tracking',

View file

@ -1,8 +1,8 @@
from django.contrib import admin
from todo.models import Item, TaskList, Comment
from todo.models import Task, TaskList, Comment
class ItemAdmin(admin.ModelAdmin):
class TaskAdmin(admin.ModelAdmin):
list_display = ('title', 'task_list', 'completed', 'priority', 'due_date')
list_filter = ('task_list',)
ordering = ('priority',)
@ -15,4 +15,4 @@ class CommentAdmin(admin.ModelAdmin):
admin.site.register(TaskList)
admin.site.register(Comment, CommentAdmin)
admin.site.register(Item, ItemAdmin)
admin.site.register(Task, TaskAdmin)

View file

@ -1,7 +1,7 @@
from django import forms
from django.forms import ModelForm
from django.contrib.auth.models import Group
from todo.models import Item, TaskList
from todo.models import Task, TaskList
from django.contrib.auth import get_user_model
@ -21,7 +21,7 @@ class AddTaskListForm(ModelForm):
exclude = []
class AddEditItemForm(ModelForm):
class AddEditTaskForm(ModelForm):
"""The picklist showing the users to which a new task can be assigned
must find other members of the groups the current list belongs to."""
@ -43,11 +43,11 @@ class AddEditItemForm(ModelForm):
widget=forms.Textarea(), required=False)
class Meta:
model = Item
model = Task
exclude = []
class AddExternalItemForm(ModelForm):
class AddExternalTaskForm(ModelForm):
"""Form to allow users who are not part of the GTD system to file a ticket."""
title = forms.CharField(
@ -63,7 +63,7 @@ class AddExternalItemForm(ModelForm):
)
class Meta:
model = Item
model = Task
exclude = (
'task_list', 'created_date', 'due_date', 'created_by', 'assigned_to', 'completed', 'completed_date', )

View file

@ -0,0 +1,19 @@
# Generated by Django 2.0.3 on 2018-03-28 22:40
from django.conf import settings
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('todo', '0005_auto_20180212_2325'),
]
operations = [
migrations.RenameModel(
old_name='Item',
new_name='Task',
),
]

View file

@ -1,14 +1,12 @@
from __future__ import unicode_literals
import datetime
from django.db import models
from django.contrib.auth.models import Group
from django.urls import reverse
from django.utils.encoding import python_2_unicode_compatible
from django.conf import settings
from django.contrib.auth.models import Group
from django.db import models
from django.urls import reverse
@python_2_unicode_compatible
class TaskList(models.Model):
name = models.CharField(max_length=60)
slug = models.SlugField(default='',)
@ -25,8 +23,7 @@ class TaskList(models.Model):
unique_together = ("group", "slug")
@python_2_unicode_compatible
class Item(models.Model):
class Task(models.Model):
title = models.CharField(max_length=140)
task_list = models.ForeignKey(TaskList, on_delete=models.CASCADE, null=True)
created_date = models.DateField(auto_now=True)
@ -41,7 +38,7 @@ class Item(models.Model):
# Has due date for an instance of this object passed?
def overdue_status(self):
"Returns whether the item's due date has passed or not."
"Returns whether the Tasks's due date has passed or not."
if self.due_date and datetime.date.today() > self.due_date:
return True
@ -51,25 +48,24 @@ class Item(models.Model):
def get_absolute_url(self):
return reverse('todo:task_detail', kwargs={'task_id': self.id, })
# Auto-set the item creation / completed date
# Auto-set the Task creation / completed date
def save(self, **kwargs):
# If Item is being marked complete, set the completed_date
# If Task is being marked complete, set the completed_date
if self.completed:
self.completed_date = datetime.datetime.now()
super(Item, self).save()
super(Task, self).save()
class Meta:
ordering = ["priority"]
@python_2_unicode_compatible
class Comment(models.Model):
"""
Not using Django's built-in comments because we want to be able to save
a comment and change task details at the same time. Rolling our own since it's easy.
"""
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
task = models.ForeignKey(Item, on_delete=models.CASCADE)
task = models.ForeignKey(Task, on_delete=models.CASCADE)
date = models.DateTimeField(default=datetime.datetime.now)
body = models.TextField(blank=True)

View file

@ -8,10 +8,10 @@
<p>Category tally:</p>
<ul>
<li>Incomplete: {{ item_count_undone }} </li>
<li>Complete: {{ item_count_done }} </li>
<li>Incomplete: {{ task_count_undone }} </li>
<li>Complete: {{ task_count_done }} </li>
<li>
<strong>Total: {{ item_count_total }}</strong>
<strong>Total: {{ task_count_total }}</strong>
</li>
</ul>

View file

@ -27,7 +27,7 @@
<div class="form-group">
<label for="id_assigned_to">Assigned To</label>
{# See todo.forms.AddEditItemForm #}
{# See todo.forms.AddEditTaskForm #}
{{form.assigned_to}}
</div>

View file

@ -14,7 +14,7 @@
<hr />
{% endif %}
{% if items %}
{% if tasks %}
{% if list_slug == "mine" %}
<h1>Tasks assigned to me (in all groups)</h1>
{% else %}
@ -37,7 +37,7 @@
<th>Del</th>
</tr>
{% for task in items %}
{% for task in tasks %}
<tr id="{{ task.id }}">
<td>
<input type="checkbox" name="toggle_done_tasks"
@ -80,7 +80,7 @@
</form>
{% else %}
<h4>No items on this list yet (add one!)</h4>
<h4>No tasks on this list yet (add one!)</h4>
{% include 'todo/include/toggle_delete.html' %}
{% endif %}

View file

@ -5,16 +5,16 @@
{% block content %}
<h1>Todo Lists</h1>
<p>{{ item_count }} items in {{ list_count }} list{{ list_count|pluralize }}</p>
<p>{{ task_count }} tasks in {{ list_count }} list{{ list_count|pluralize }}</p>
{% regroup lists by group as section_list %}
{% for group in section_list %}
<h3>Group: {{ group.grouper }}</h3>
<ul class="list-group mb-4">
{% for item in group.list %}
{% for task in group.list %}
<li class="list-group-item d-flex justify-content-between align-items-center">
<a href="{% url 'todo:list_detail' item.id item.slug %}">{{ item.name }}</a>
<span class="badge badge-primary badge-pill">{{ item.item_set.count }}</span>
<a href="{% url 'todo:list_detail' task.id task.slug %}">{{ task.name }}</a>
<span class="badge badge-primary badge-pill">{{ task.task_set.count }}</span>
</li>
{% endfor %}
</ul>

View file

@ -4,10 +4,10 @@
{% block content_title %}<h2 class="page_title">Search</h2>{% endblock %}
{% block content %}
{% if found_items %}
<h2>{{found_items.count}} search results for term: "{{ query_string }}"</h2>
{% if found_tasks %}
<h2>{{found_tasks.count}} search results for term: "{{ query_string }}"</h2>
<div class="post_list">
{% for f in found_items %}
{% for f in found_tasks %}
<p>
<strong>
<a href="{% url 'todo:task_detail' f.id %}">{{ f.title }}</a>

View file

@ -2,7 +2,7 @@ import pytest
from django.contrib.auth.models import Group
from todo.models import Item, TaskList
from todo.models import Task, TaskList
@pytest.fixture
@ -13,14 +13,14 @@ def todo_setup(django_user_model):
u1 = django_user_model.objects.create_user(username="u1", password="password", email="u1@example.com")
u1.groups.add(g1)
tlist1 = TaskList.objects.create(group=g1, name="Zip", slug="zip")
Item.objects.create(created_by=u1, title="Task 1", task_list=tlist1, priority=1)
Item.objects.create(created_by=u1, title="Task 2", task_list=tlist1, priority=2, completed=True)
Item.objects.create(created_by=u1, title="Task 3", task_list=tlist1, priority=3)
Task.objects.create(created_by=u1, title="Task 1", task_list=tlist1, priority=1)
Task.objects.create(created_by=u1, title="Task 2", task_list=tlist1, priority=2, completed=True)
Task.objects.create(created_by=u1, title="Task 3", task_list=tlist1, priority=3)
g2 = Group.objects.create(name="Workgroup Two")
u2 = django_user_model.objects.create_user(username="u2", password="password", email="u2@example.com")
u2.groups.add(g2)
tlist2 = TaskList.objects.create(group=g2, name="Zap", slug="zap")
Item.objects.create(created_by=u2, title="Task 1", task_list=tlist2, priority=1)
Item.objects.create(created_by=u2, title="Task 2", task_list=tlist2, priority=2, completed=True)
Item.objects.create(created_by=u2, title="Task 3", task_list=tlist2, priority=3)
Task.objects.create(created_by=u2, title="Task 1", task_list=tlist2, priority=1)
Task.objects.create(created_by=u2, title="Task 2", task_list=tlist2, priority=2, completed=True)
Task.objects.create(created_by=u2, title="Task 3", task_list=tlist2, priority=3)

View file

@ -2,7 +2,7 @@ import pytest
from django.core import mail
from todo.models import Item, Comment
from todo.models import Task, Comment
from todo.utils import toggle_done, toggle_deleted, send_notify_mail, send_email_to_thread_participants
@ -15,7 +15,7 @@ def email_backend_setup(settings):
def test_toggle_done(todo_setup):
"""Utility function takes an array of POSTed IDs and changes their `completed` status.
"""
u1_tasks = Item.objects.filter(created_by__username="u1")
u1_tasks = Task.objects.filter(created_by__username="u1")
completed = u1_tasks.filter(completed=True)
incomplete = u1_tasks.filter(completed=False)
@ -38,13 +38,13 @@ def test_toggle_done(todo_setup):
def test_toggle_deleted(todo_setup):
"""Unlike toggle_done, delete means delete, so it's not really a toggle.
"""
u1_tasks = Item.objects.filter(created_by__username="u1")
u1_tasks = Task.objects.filter(created_by__username="u1")
assert u1_tasks.count() == 3
t1 = u1_tasks.first()
t2 = u1_tasks.last()
toggle_deleted([t1.id, t2.id, ])
u1_tasks = Item.objects.filter(created_by__username="u1")
u1_tasks = Task.objects.filter(created_by__username="u1")
assert u1_tasks.count() == 1
@ -56,7 +56,7 @@ def test_send_notify_mail_not_me(todo_setup, django_user_model, email_backend_se
u1 = django_user_model.objects.get(username="u1")
u2 = django_user_model.objects.get(username="u2")
task = Item.objects.filter(created_by=u1).first()
task = Task.objects.filter(created_by=u1).first()
task.assigned_to = u2
task.save()
send_notify_mail(task)
@ -68,7 +68,7 @@ def test_send_notify_mail_myself(todo_setup, django_user_model, email_backend_se
"""
u1 = django_user_model.objects.get(username="u1")
task = Item.objects.filter(created_by=u1).first()
task = Task.objects.filter(created_by=u1).first()
task.assigned_to = u1
task.save()
send_notify_mail(task)
@ -80,7 +80,7 @@ def test_send_email_to_thread_participants(todo_setup, django_user_model, email_
Notification email should be sent to all three users."""
u1 = django_user_model.objects.get(username="u1")
task = Item.objects.filter(created_by=u1).first()
task = Task.objects.filter(created_by=u1).first()
u3 = django_user_model.objects.create_user(username="u3", password="zzz", email="u3@example.com")
u4 = django_user_model.objects.create_user(username="u4", password="zzz", email="u4@example.com")

View file

@ -3,7 +3,7 @@ import pytest
from django.contrib.auth.models import Group
from django.urls import reverse
from todo.models import Item, TaskList
from todo.models import Task, TaskList
"""
First the "smoketests" - do they respond at all for a logged in admin user?
@ -15,7 +15,7 @@ After that, view contents and behaviors.
@pytest.mark.django_db
def test_todo_setup(todo_setup):
assert Item.objects.all().count() == 6
assert Task.objects.all().count() == 6
def test_view_list_lists(todo_setup, admin_client):
@ -73,7 +73,7 @@ def test_view_add_list(todo_setup, admin_client):
def test_view_task_detail(todo_setup, admin_client):
task = Item.objects.first()
task = Task.objects.first()
url = reverse('todo:task_detail', kwargs={'task_id': task.id})
response = admin_client.get(url)
assert response.status_code == 200
@ -131,7 +131,7 @@ def test_view_list_not_mine(todo_setup, client):
def test_view_task_mine(todo_setup, client):
# Users can always view their own tasks
task = Item.objects.filter(created_by__username="u1").first()
task = Task.objects.filter(created_by__username="u1").first()
client.login(username="u1", password="password")
url = reverse('todo:task_detail', kwargs={'task_id': task.id})
response = client.get(url)
@ -147,7 +147,7 @@ def test_view_task_my_group(todo_setup, client, django_user_model):
u2.groups.add(g1)
# Now u2 should be able to view one of u1's tasks.
task = Item.objects.filter(created_by__username="u1").first()
task = Task.objects.filter(created_by__username="u1").first()
url = reverse('todo:task_detail', kwargs={'task_id': task.id})
client.login(username="u2", password="password")
response = client.get(url)
@ -157,7 +157,7 @@ def test_view_task_my_group(todo_setup, client, django_user_model):
def test_view_task_not_in_my_group(todo_setup, client):
# User canNOT view a task that isn't theirs if the two users are not in a shared group.
# For this we can use the fixture data as-is.
task = Item.objects.filter(created_by__username="u1").first()
task = Task.objects.filter(created_by__username="u1").first()
url = reverse('todo:task_detail', kwargs={'task_id': task.id})
client.login(username="u2", password="password")
response = client.get(url)

View file

@ -4,35 +4,35 @@ from django.contrib.sites.models import Site
from django.core.mail import send_mail
from django.template.loader import render_to_string
from todo.models import Item, Comment
from todo.models import Task, Comment
def toggle_done(item_ids):
"""Check for items in the mark_done POST array. If present, change status to complete.
def toggle_done(task_ids):
"""Check for tasks in the mark_done POST array. If present, change status to complete.
Takes a list of task IDs. Returns list of status change strings.
"""
_ret = []
for item_id in item_ids:
i = Item.objects.get(id=item_id)
for task_id in task_ids:
i = Task.objects.get(id=task_id)
old_state = "completed" if i.completed else "incomplete"
i.completed = not i.completed # Invert the done state, either way
new_state = "completed" if i.completed else "incomplete"
i.completed_date = datetime.datetime.now()
i.save()
_ret.append("Item \"{i}\" changed from {o} to {n}.".format(i=i.title, o=old_state, n=new_state))
_ret.append("Task \"{i}\" changed from {o} to {n}.".format(i=i.title, o=old_state, n=new_state))
return _ret
def toggle_deleted(deleted_item_ids):
"""Delete selected items. Returns list of status change strings.
def toggle_deleted(deleted_task_ids):
"""Delete selected tasks. Returns list of status change strings.
"""
_ret = []
for item_id in deleted_item_ids:
i = Item.objects.get(id=item_id)
_ret.append("Item \"{i}\" deleted.".format(i=i.title))
for task_id in deleted_task_ids:
i = Task.objects.get(id=task_id)
_ret.append("Task \"{i}\" deleted.".format(i=i.title))
i.delete()
return _ret

View file

@ -14,8 +14,8 @@ from django.shortcuts import get_object_or_404, render, redirect
from django.template.loader import render_to_string
from django.views.decorators.csrf import csrf_exempt
from todo.forms import AddTaskListForm, AddEditItemForm, AddExternalItemForm, SearchForm
from todo.models import Item, TaskList, Comment
from todo.forms import AddTaskListForm, AddEditTaskForm, AddExternalTaskForm, SearchForm
from todo.models import Task, TaskList, Comment
from todo.utils import (
toggle_done,
toggle_deleted,
@ -62,16 +62,16 @@ def list_lists(request) -> HttpResponse:
# superusers see all lists, so count shouldn't filter by just lists the admin belongs to
if request.user.is_superuser:
item_count = Item.objects.filter(completed=0).count()
task_count = Task.objects.filter(completed=0).count()
else:
item_count = Item.objects.filter(completed=0).filter(task_list__group__in=request.user.groups.all()).count()
task_count = Task.objects.filter(completed=0).filter(task_list__group__in=request.user.groups.all()).count()
context = {
"lists": lists,
"thedate": thedate,
"searchform": searchform,
"list_count": list_count,
"item_count": item_count,
"task_count": task_count,
}
return render(request, 'todo/list_lists.html', context)
@ -95,15 +95,15 @@ def del_list(request, list_id: int, list_slug: str) -> HttpResponse:
messages.success(request, "{list_name} is gone.".format(list_name=task_list.name))
return redirect('todo:lists')
else:
item_count_done = Item.objects.filter(task_list=task_list.id, completed=True).count()
item_count_undone = Item.objects.filter(task_list=task_list.id, completed=False).count()
item_count_total = Item.objects.filter(task_list=task_list.id).count()
task_count_done = Task.objects.filter(task_list=task_list.id, completed=True).count()
task_count_undone = Task.objects.filter(task_list=task_list.id, completed=False).count()
task_count_total = Task.objects.filter(task_list=task_list.id).count()
context = {
"task_list": task_list,
"item_count_done": item_count_done,
"item_count_undone": item_count_undone,
"item_count_total": item_count_total,
"task_count_done": task_count_done,
"task_count_undone": task_count_undone,
"task_count_total": task_count_total,
}
return render(request, 'todo/del_list.html', context)
@ -111,37 +111,37 @@ def del_list(request, list_id: int, list_slug: str) -> HttpResponse:
@login_required
def list_detail(request, list_id=None, list_slug=None, view_completed=False):
"""Display and manage items in a todo list.
"""Display and manage tasks in a todo list.
"""
# Defaults
task_list = None
form = None
# Which items to show on this list view?
# Which tasks to show on this list view?
if list_slug == "mine":
items = Item.objects.filter(assigned_to=request.user)
tasks =Task.objects.filter(assigned_to=request.user)
else:
# Show a specific list, ensuring permissions.
task_list = get_object_or_404(TaskList, id=list_id)
if task_list.group not in request.user.groups.all() and not request.user.is_staff:
raise PermissionDenied
items = Item.objects.filter(task_list=task_list.id)
tasks = Task.objects.filter(task_list=task_list.id)
# Additional filtering
if view_completed:
items = items.filter(completed=True)
tasks = tasks.filter(completed=True)
else:
items = items.filter(completed=False)
tasks = tasks.filter(completed=False)
if request.POST:
# Process completed and deleted items on each POST
# Process completed and deleted tasks on each POST
results_changed = toggle_done(request.POST.getlist('toggle_done_tasks'))
for res in results_changed:
messages.success(request, res)
results_changed = toggle_deleted(request, request.POST.getlist('toggle_deleted_tasks'))
results_changed = toggle_deleted(request.POST.getlist('toggle_deleted_tasks'))
for res in results_changed:
messages.success(request, res)
@ -150,7 +150,7 @@ def list_detail(request, list_id=None, list_slug=None, view_completed=False):
# ######################
if request.POST.getlist('add_edit_task'):
form = AddEditItemForm(request.user, request.POST, initial={
form = AddEditTaskForm(request.user, request.POST, initial={
'assigned_to': request.user.id,
'priority': 999,
'task_list': task_list
@ -168,7 +168,7 @@ def list_detail(request, list_id=None, list_slug=None, view_completed=False):
else:
# Don't allow adding new tasks on some views
if list_slug not in ["mine", "recent-add", "recent-complete", ]:
form = AddEditItemForm(request.user, initial={
form = AddEditTaskForm(request.user, initial={
'assigned_to': request.user.id,
'priority': 999,
'task_list': task_list
@ -179,7 +179,7 @@ def list_detail(request, list_id=None, list_slug=None, view_completed=False):
"list_slug": list_slug,
"task_list": task_list,
"form": form,
"items": items,
"tasks": tasks,
"view_completed": view_completed,
}
@ -191,10 +191,10 @@ def task_detail(request, task_id: int) -> HttpResponse:
"""View task details. Allow task details to be edited. Process new comments on task.
"""
task = get_object_or_404(Item, pk=task_id)
task = get_object_or_404(Task, pk=task_id)
comment_list = Comment.objects.filter(task=task_id)
# Ensure user has permission to view item. Admins can view all tasks.
# Ensure user has permission to view task. Admins can view all tasks.
# Get the group this task belongs to, and check whether current user is a member of that group.
if task.task_list.group not in request.user.groups.all() and not request.user.is_staff:
raise PermissionDenied
@ -212,14 +212,14 @@ def task_detail(request, task_id: int) -> HttpResponse:
# Save task edits
if request.POST.get('add_edit_task'):
form = AddEditItemForm(request.user, request.POST, instance=task, initial={'task_list': task.task_list})
form = AddEditTaskForm(request.user, request.POST, instance=task, initial={'task_list': task.task_list})
if form.is_valid():
form.save()
messages.success(request, "The task has been edited.")
return redirect('todo:list_detail', list_id=task.task_list.id, list_slug=task.task_list.slug)
else:
form = AddEditItemForm(request.user, instance=task, initial={'task_list': task.task_list})
form = AddEditTaskForm(request.user, instance=task, initial={'task_list': task.task_list})
# Mark complete
if request.POST.get('toggle_done'):
@ -251,15 +251,15 @@ def reorder_tasks(request) -> HttpResponse:
"""
newtasklist = request.POST.getlist('tasktable[]')
if newtasklist:
# First item in received list is always empty - remove it
# First task in received list is always empty - remove it
del newtasklist[0]
# Re-prioritize each item in list
# Re-prioritize each task in list
i = 1
for t in newtasklist:
newitem = Item.objects.get(pk=t)
newitem.priority = i
newitem.save()
newtask = Task.objects.get(pk=t)
newtask.priority = i
newtask.save()
i += 1
# All views must return an httpresponse of some kind ... without this we get
@ -306,33 +306,33 @@ def search(request) -> HttpResponse:
if request.GET:
query_string = ''
found_items = None
found_tasks = None
if ('q' in request.GET) and request.GET['q'].strip():
query_string = request.GET['q']
found_items = Item.objects.filter(
found_tasks = Task.objects.filter(
Q(title__icontains=query_string) |
Q(note__icontains=query_string)
)
else:
# What if they selected the "completed" toggle but didn't enter a query string?
# We still need found_items in a queryset so it can be "excluded" below.
found_items = Item.objects.all()
# We still need found_tasks in a queryset so it can be "excluded" below.
found_tasks = Task.objects.all()
if 'inc_complete' in request.GET:
found_items = found_items.exclude(completed=True)
found_tasks = found_tasks.exclude(completed=True)
else:
query_string = None
found_items = None
found_tasks =None
# Only include items that are in groups of which this user is a member:
# Only include tasks that are in groups of which this user is a member:
if not request.user.is_superuser:
found_items = found_items.filter(task_list__group__in=request.user.groups.all())
found_tasks = found_tasks.filter(task_list__group__in=request.user.groups.all())
context = {
'query_string': query_string,
'found_items': found_items
'found_tasks': found_tasks
}
return render(request, 'todo/search_results.html', context)
@ -353,25 +353,25 @@ def external_add(request) -> HttpResponse:
raise RuntimeError("There is no TaskList with ID specified for DEFAULT_LIST_ID in settings.")
if request.POST:
form = AddExternalItemForm(request.POST)
form = AddExternalTaskForm(request.POST)
if form.is_valid():
current_site = Site.objects.get_current()
item = form.save(commit=False)
item.task_list = TaskList.objects.get(id=settings.TODO_DEFAULT_LIST_ID)
item.created_by = request.user
task = form.save(commit=False)
task.task_list = TaskList.objects.get(id=settings.TODO_DEFAULT_LIST_ID)
task.created_by = request.user
if settings.TODO_DEFAULT_ASSIGNEE:
item.assigned_to = User.objects.get(username=settings.TODO_DEFAULT_ASSIGNEE)
item.save()
task.assigned_to = User.objects.get(username=settings.TODO_DEFAULT_ASSIGNEE)
task.save()
# Send email to assignee if we have one
if item.assigned_to:
email_subject = render_to_string("todo/email/assigned_subject.txt", {'task': item.title})
email_body = render_to_string("todo/email/assigned_body.txt", {'task': item, 'site': current_site, })
if task.assigned_to:
email_subject = render_to_string("todo/email/assigned_subject.txt", {'task': task.title})
email_body = render_to_string("todo/email/assigned_body.txt", {'task': task, 'site': current_site, })
try:
send_mail(
email_subject, email_body, item.created_by.email,
[item.assigned_to.email, ], fail_silently=False)
email_subject, email_body, task.created_by.email,
[task.assigned_to.email, ], fail_silently=False)
except ConnectionRefusedError:
messages.error(request, "Task saved but mail not sent. Contact your administrator.")
@ -380,7 +380,7 @@ def external_add(request) -> HttpResponse:
return redirect(settings.TODO_PUBLIC_SUBMIT_REDIRECT)
else:
form = AddExternalItemForm(initial={'priority': 999})
form = AddExternalTaskForm(initial={'priority': 999})
context = {
"form": form,