Black formatting

This commit is contained in:
Scot Hacker 2018-12-21 00:38:44 -08:00
parent f526ed5166
commit 21ec87cee4
10 changed files with 276 additions and 227 deletions

View file

@ -5,7 +5,7 @@ from setuptools import setup, find_packages
import todo as package
setup(
name='django-todo',
name="django-todo",
version=package.__version__,
description=package.__doc__.strip(),
author=package.__author__,
@ -14,18 +14,18 @@ setup(
license=package.__license__,
packages=find_packages(),
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Web Environment',
'Framework :: Django',
'Intended Audience :: Developers',
'License :: OSI Approved :: BSD License',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Topic :: Office/Business :: Groupware',
'Topic :: Software Development :: Bug Tracking',
"Development Status :: 5 - Production/Stable",
"Environment :: Web Environment",
"Framework :: Django",
"Intended Audience :: Developers",
"License :: OSI Approved :: BSD License",
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Topic :: Office/Business :: Groupware",
"Topic :: Software Development :: Bug Tracking",
],
include_package_data=True,
zip_safe=False,
install_requires=['unidecode', ],
install_requires=["unidecode"],
)

View file

@ -1,64 +1,63 @@
import os
DEBUG = True,
DEBUG = (True,)
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
print("bd ", BASE_DIR)
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
"default": {
"ENGINE": "django.db.backends.sqlite3"
}
}
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
# Document
TODO_STAFF_ONLY = False
TODO_DEFAULT_LIST_SLUG = 'tickets'
TODO_DEFAULT_LIST_SLUG = "tickets"
TODO_DEFAULT_ASSIGNEE = None
TODO_PUBLIC_SUBMIT_REDIRECT = '/'
TODO_PUBLIC_SUBMIT_REDIRECT = "/"
SECRET_KEY = "LKFSD8sdl.,8&sdf--"
SITE_ID = 1
INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.messages',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.staticfiles',
'todo',
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.messages",
"django.contrib.sessions",
"django.contrib.sites",
"django.contrib.staticfiles",
"todo",
)
ROOT_URLCONF = 'base_urls'
ROOT_URLCONF = "base_urls"
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
]
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'todo', 'templates'), ],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.template.context_processors.media',
'django.template.context_processors.static',
'django.contrib.messages.context_processors.messages',
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [os.path.join(BASE_DIR, "todo", "templates")],
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
"django.template.context_processors.debug",
"django.template.context_processors.request",
"django.contrib.auth.context_processors.auth",
"django.template.context_processors.media",
"django.template.context_processors.static",
"django.contrib.messages.context_processors.messages",
# Your stuff: custom template context processors go here
],
]
},
},
]
}
]

View file

@ -3,14 +3,14 @@ from todo.models import Task, TaskList, Comment
class TaskAdmin(admin.ModelAdmin):
list_display = ('title', 'task_list', 'completed', 'priority', 'due_date')
list_filter = ('task_list',)
ordering = ('priority',)
search_fields = ('name',)
list_display = ("title", "task_list", "completed", "priority", "due_date")
list_filter = ("task_list",)
ordering = ("priority",)
search_fields = ("name",)
class CommentAdmin(admin.ModelAdmin):
list_display = ('author', 'date', 'snippet')
list_display = ("author", "date", "snippet")
admin.site.register(TaskList)

View file

@ -11,13 +11,16 @@ class AddTaskListForm(ModelForm):
def __init__(self, user, *args, **kwargs):
super(AddTaskListForm, self).__init__(*args, **kwargs)
self.fields['group'].queryset = Group.objects.filter(user=user)
self.fields['group'].widget.attrs = {
'id': 'id_group', 'class': "custom-select mb-3", 'name': 'group'}
self.fields["group"].queryset = Group.objects.filter(user=user)
self.fields["group"].widget.attrs = {
"id": "id_group",
"class": "custom-select mb-3",
"name": "group",
}
class Meta:
model = TaskList
exclude = ['created_date', 'slug', ]
exclude = ["created_date", "slug"]
class AddEditTaskForm(ModelForm):
@ -26,22 +29,25 @@ class AddEditTaskForm(ModelForm):
def __init__(self, user, *args, **kwargs):
super().__init__(*args, **kwargs)
task_list = kwargs.get('initial').get('task_list')
task_list = kwargs.get("initial").get("task_list")
members = task_list.group.user_set.all()
self.fields['assigned_to'].queryset = members
self.fields['assigned_to'].label_from_instance = lambda obj: "%s (%s)" % (obj.get_full_name(), obj.username)
self.fields['assigned_to'].widget.attrs = {
'id': 'id_assigned_to', 'class': "custom-select mb-3", 'name': 'assigned_to'}
self.fields['task_list'].value = kwargs['initial']['task_list'].id
self.fields["assigned_to"].queryset = members
self.fields["assigned_to"].label_from_instance = lambda obj: "%s (%s)" % (
obj.get_full_name(),
obj.username,
)
self.fields["assigned_to"].widget.attrs = {
"id": "id_assigned_to",
"class": "custom-select mb-3",
"name": "assigned_to",
}
self.fields["task_list"].value = kwargs["initial"]["task_list"].id
due_date = forms.DateField(
widget=forms.DateInput(attrs={'type': 'date'}), required=False)
due_date = forms.DateField(widget=forms.DateInput(attrs={"type": "date"}), required=False)
title = forms.CharField(
widget=forms.widgets.TextInput())
title = forms.CharField(widget=forms.widgets.TextInput())
note = forms.CharField(
widget=forms.Textarea(), required=False)
note = forms.CharField(widget=forms.Textarea(), required=False)
class Meta:
model = Task
@ -51,27 +57,24 @@ class AddEditTaskForm(ModelForm):
class AddExternalTaskForm(ModelForm):
"""Form to allow users who are not part of the GTD system to file a ticket."""
title = forms.CharField(
widget=forms.widgets.TextInput(attrs={'size': 35}),
label="Summary"
)
note = forms.CharField(
widget=forms.widgets.Textarea(),
label='Problem Description',
)
priority = forms.IntegerField(
widget=forms.HiddenInput(),
)
title = forms.CharField(widget=forms.widgets.TextInput(attrs={"size": 35}), label="Summary")
note = forms.CharField(widget=forms.widgets.Textarea(), label="Problem Description")
priority = forms.IntegerField(widget=forms.HiddenInput())
class Meta:
model = Task
exclude = (
'task_list', 'created_date', 'due_date', 'created_by', 'assigned_to', 'completed', 'completed_date', )
"task_list",
"created_date",
"due_date",
"created_by",
"assigned_to",
"completed",
"completed_date",
)
class SearchForm(forms.Form):
"""Search."""
q = forms.CharField(
widget=forms.widgets.TextInput(attrs={'size': 35})
)
q = forms.CharField(widget=forms.widgets.TextInput(attrs={"size": 35}))

View file

@ -10,7 +10,7 @@ from django.utils import timezone
class TaskList(models.Model):
name = models.CharField(max_length=60)
slug = models.SlugField(default='',)
slug = models.SlugField(default="")
group = models.ForeignKey(Group, on_delete=models.CASCADE)
def __str__(self):
@ -28,12 +28,19 @@ 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(default=timezone.now, blank=True, null=True)
due_date = models.DateField(blank=True, null=True, )
due_date = models.DateField(blank=True, null=True)
completed = models.BooleanField(default=False)
completed_date = models.DateField(blank=True, null=True)
created_by = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='todo_created_by', on_delete=models.CASCADE)
created_by = models.ForeignKey(
settings.AUTH_USER_MODEL, related_name="todo_created_by", on_delete=models.CASCADE
)
assigned_to = models.ForeignKey(
settings.AUTH_USER_MODEL, blank=True, null=True, related_name='todo_assigned_to', on_delete=models.CASCADE)
settings.AUTH_USER_MODEL,
blank=True,
null=True,
related_name="todo_assigned_to",
on_delete=models.CASCADE,
)
note = models.TextField(blank=True, null=True)
priority = models.PositiveIntegerField()
@ -47,7 +54,7 @@ class Task(models.Model):
return self.title
def get_absolute_url(self):
return reverse('todo:task_detail', kwargs={'task_id': self.id, })
return reverse("todo:task_detail", kwargs={"task_id": self.id})
# Auto-set the Task creation / completed date
def save(self, **kwargs):
@ -65,6 +72,7 @@ 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(Task, on_delete=models.CASCADE)
date = models.DateTimeField(default=datetime.datetime.now)

View file

@ -5,13 +5,14 @@ from django.contrib.auth.models import Group
from todo.models import Task, TaskList
@pytest.fixture
def todo_setup(django_user_model):
# Two groups with different users, two sets of tasks.
g1 = Group.objects.create(name="Workgroup One")
u1 = django_user_model.objects.create_user(username="u1", password="password", email="u1@example.com")
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")
Task.objects.create(created_by=u1, title="Task 1", task_list=tlist1, priority=1)
@ -19,7 +20,9 @@ def todo_setup(django_user_model):
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 = 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")
Task.objects.create(created_by=u2, title="Task 1", task_list=tlist2, priority=1)

View file

@ -9,7 +9,7 @@ from todo.utils import send_notify_mail, send_email_to_thread_participants
@pytest.fixture()
# Set up an in-memory mail server to receive test emails
def email_backend_setup(settings):
settings.EMAIL_BACKEND = 'django.core.mail.backends.locmem.EmailBackend'
settings.EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend"
def test_send_notify_mail_not_me(todo_setup, django_user_model, email_backend_setup):
@ -46,13 +46,17 @@ def test_send_email_to_thread_participants(todo_setup, django_user_model, email_
u1 = django_user_model.objects.get(username="u1")
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")
Comment.objects.create(author=u3, task=task, body="Hello", )
Comment.objects.create(author=u4, task=task, body="Hello", )
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"
)
Comment.objects.create(author=u3, task=task, body="Hello")
Comment.objects.create(author=u4, task=task, body="Hello")
send_email_to_thread_participants(task, "test body", u1)
assert len(mail.outbox) == 1 # One message to multiple recipients
assert 'u1@example.com' in mail.outbox[0].recipients()
assert 'u3@example.com' in mail.outbox[0].recipients()
assert 'u4@example.com' in mail.outbox[0].recipients()
assert "u1@example.com" in mail.outbox[0].recipients()
assert "u3@example.com" in mail.outbox[0].recipients()
assert "u4@example.com" in mail.outbox[0].recipients()

View file

@ -16,19 +16,20 @@ After that, view contents and behaviors.
# ### SMOKETESTS ###
@pytest.mark.django_db
def test_todo_setup(todo_setup):
assert Task.objects.all().count() == 6
def test_view_list_lists(todo_setup, admin_client):
url = reverse('todo:lists')
url = reverse("todo:lists")
response = admin_client.get(url)
assert response.status_code == 200
def test_view_reorder(todo_setup, admin_client):
url = reverse('todo:reorder_tasks')
url = reverse("todo:reorder_tasks")
response = admin_client.get(url)
assert response.status_code == 201 # Special case return value expected
@ -37,53 +38,55 @@ def test_view_external_add(todo_setup, admin_client, settings):
default_list = TaskList.objects.first()
settings.TODO_DEFAULT_LIST_SLUG = default_list.slug
assert settings.TODO_DEFAULT_LIST_SLUG == default_list.slug
url = reverse('todo:external_add')
url = reverse("todo:external_add")
response = admin_client.get(url)
assert response.status_code == 200
def test_view_mine(todo_setup, admin_client):
url = reverse('todo:mine')
url = reverse("todo:mine")
response = admin_client.get(url)
assert response.status_code == 200
def test_view_list_completed(todo_setup, admin_client):
tlist = TaskList.objects.get(slug="zip")
url = reverse('todo:list_detail_completed', kwargs={'list_id': tlist.id, 'list_slug': tlist.slug})
url = reverse(
"todo:list_detail_completed", kwargs={"list_id": tlist.id, "list_slug": tlist.slug}
)
response = admin_client.get(url)
assert response.status_code == 200
def test_view_list(todo_setup, admin_client):
tlist = TaskList.objects.get(slug="zip")
url = reverse('todo:list_detail', kwargs={'list_id': tlist.id, 'list_slug': tlist.slug})
url = reverse("todo:list_detail", kwargs={"list_id": tlist.id, "list_slug": tlist.slug})
response = admin_client.get(url)
assert response.status_code == 200
def test_del_list(todo_setup, admin_client):
tlist = TaskList.objects.get(slug="zip")
url = reverse('todo:del_list', kwargs={'list_id': tlist.id, 'list_slug': tlist.slug})
url = reverse("todo:del_list", kwargs={"list_id": tlist.id, "list_slug": tlist.slug})
response = admin_client.get(url)
assert response.status_code == 200
def test_view_add_list(todo_setup, admin_client):
url = reverse('todo:add_list')
url = reverse("todo:add_list")
response = admin_client.get(url)
assert response.status_code == 200
def test_view_task_detail(todo_setup, admin_client):
task = Task.objects.first()
url = reverse('todo:task_detail', kwargs={'task_id': task.id})
url = reverse("todo:task_detail", kwargs={"task_id": task.id})
response = admin_client.get(url)
assert response.status_code == 200
def test_view_search(todo_setup, admin_client):
url = reverse('todo:search')
url = reverse("todo:search")
response = admin_client.get(url)
assert response.status_code == 200
@ -100,11 +103,11 @@ def test_no_javascript_in_task_note(todo_setup, client):
"priority": 10,
"title": title,
"note": note,
'add_edit_task': 'Submit'
"add_edit_task": "Submit",
}
client.login(username='u2', password="password")
url = reverse('todo:list_detail', kwargs={"list_id": task_list.id, "list_slug": task_list.slug})
client.login(username="u2", password="password")
url = reverse("todo:list_detail", kwargs={"list_id": task_list.id, "list_slug": task_list.slug})
response = client.post(url, data)
assert response.status_code == 302
@ -118,7 +121,7 @@ def test_no_javascript_in_task_note(todo_setup, client):
@pytest.mark.django_db
def test_no_javascript_in_comments(todo_setup, client):
user = get_user_model().objects.get(username="u2")
client.login(username='u2', password="password")
client.login(username="u2", password="password")
task = Task.objects.first()
task.created_by = user
@ -127,11 +130,8 @@ def test_no_javascript_in_comments(todo_setup, client):
user.groups.add(task.task_list.group)
comment = "foo <script>alert('oh noez');</script> bar"
data = {
"comment-body": comment,
"add_comment": 'Submit'
}
url = reverse('todo:task_detail', kwargs={"task_id": task.id})
data = {"comment-body": comment, "add_comment": "Submit"}
url = reverse("todo:task_detail", kwargs={"task_id": task.id})
response = client.post(url, data)
assert response.status_code == 200
@ -152,7 +152,7 @@ These exercise our custom @staff_only decorator without calling that function ex
def test_view_add_list_nonadmin(todo_setup, client):
url = reverse('todo:add_list')
url = reverse("todo:add_list")
client.login(username="you", password="password")
response = client.get(url)
assert response.status_code == 403
@ -160,7 +160,7 @@ def test_view_add_list_nonadmin(todo_setup, client):
def test_view_del_list_nonadmin(todo_setup, client):
tlist = TaskList.objects.get(slug="zip")
url = reverse('todo:del_list', kwargs={'list_id': tlist.id, 'list_slug': tlist.slug})
url = reverse("todo:del_list", kwargs={"list_id": tlist.id, "list_slug": tlist.slug})
client.login(username="you", password="password")
response = client.get(url)
assert response.status_code == 403
@ -170,7 +170,7 @@ def test_view_list_mine(todo_setup, client):
"""View a list in a group I belong to.
"""
tlist = TaskList.objects.get(slug="zip") # User u1 is in this group's list
url = reverse('todo:list_detail', kwargs={'list_id': tlist.id, 'list_slug': tlist.slug})
url = reverse("todo:list_detail", kwargs={"list_id": tlist.id, "list_slug": tlist.slug})
client.login(username="u1", password="password")
response = client.get(url)
assert response.status_code == 200
@ -180,7 +180,7 @@ def test_view_list_not_mine(todo_setup, client):
"""View a list in a group I don't belong to.
"""
tlist = TaskList.objects.get(slug="zip") # User u1 is in this group, user u2 is not.
url = reverse('todo:list_detail', kwargs={'list_id': tlist.id, 'list_slug': tlist.slug})
url = reverse("todo:list_detail", kwargs={"list_id": tlist.id, "list_slug": tlist.slug})
client.login(username="u2", password="password")
response = client.get(url)
assert response.status_code == 403
@ -190,7 +190,7 @@ def test_view_task_mine(todo_setup, client):
# Users can always view their own tasks
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})
url = reverse("todo:task_detail", kwargs={"task_id": task.id})
response = client.get(url)
assert response.status_code == 200
@ -205,7 +205,7 @@ def test_view_task_my_group(todo_setup, client, django_user_model):
# Now u2 should be able to view one of u1's tasks.
task = Task.objects.filter(created_by__username="u1").first()
url = reverse('todo:task_detail', kwargs={'task_id': task.id})
url = reverse("todo:task_detail", kwargs={"task_id": task.id})
client.login(username="u2", password="password")
response = client.get(url)
assert response.status_code == 200
@ -215,7 +215,8 @@ 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 = Task.objects.filter(created_by__username="u1").first()
url = reverse('todo:task_detail', kwargs={'task_id': task.id})
url = reverse("todo:task_detail", kwargs={"task_id": task.id})
client.login(username="u2", password="password")
response = client.get(url)
assert response.status_code == 403

View file

@ -11,24 +11,30 @@ def send_notify_mail(new_task):
if not new_task.assigned_to == new_task.created_by:
current_site = Site.objects.get_current()
email_subject = render_to_string("todo/email/assigned_subject.txt", {'task': new_task})
email_subject = render_to_string("todo/email/assigned_subject.txt", {"task": new_task})
email_body = render_to_string(
"todo/email/assigned_body.txt",
{'task': new_task, 'site': current_site, })
"todo/email/assigned_body.txt", {"task": new_task, "site": current_site}
)
send_mail(
email_subject, email_body, new_task.created_by.email,
[new_task.assigned_to.email], fail_silently=False)
email_subject,
email_body,
new_task.created_by.email,
[new_task.assigned_to.email],
fail_silently=False,
)
def send_email_to_thread_participants(task, msg_body, user, subject=None):
# Notify all previous commentors on a Task about a new comment.
current_site = Site.objects.get_current()
email_subject = subject if subject else render_to_string("todo/email/assigned_subject.txt", {'task': task})
email_subject = (
subject if subject else render_to_string("todo/email/assigned_subject.txt", {"task": task})
)
email_body = render_to_string(
"todo/email/newcomment_body.txt",
{'task': task, 'body': msg_body, 'site': current_site, 'user': user}
{"task": task, "body": msg_body, "site": current_site, "user": user},
)
# Get list of all thread participants - everyone who has commented, plus task creator.

View file

@ -20,10 +20,7 @@ from django.views.decorators.csrf import csrf_exempt
from todo.forms import AddTaskListForm, AddEditTaskForm, AddExternalTaskForm, SearchForm
from todo.models import Task, TaskList, Comment
from todo.utils import (
send_notify_mail,
send_email_to_thread_participants,
)
from todo.utils import send_notify_mail, send_email_to_thread_participants
def staff_only(function):
@ -31,6 +28,7 @@ def staff_only(function):
Custom view decorator allows us to raise 403 on insufficient permissions,
rather than redirect user to login view.
"""
def wrap(request, *args, **kwargs):
if request.user.is_staff:
return function(request, *args, **kwargs)
@ -52,13 +50,18 @@ def list_lists(request) -> HttpResponse:
# Make sure user belongs to at least one group.
if request.user.groups.all().count() == 0:
messages.warning(request, "You do not yet belong to any groups. Ask your administrator to add you to one.")
messages.warning(
request,
"You do not yet belong to any groups. Ask your administrator to add you to one.",
)
# Superusers see all lists
if request.user.is_superuser:
lists = TaskList.objects.all().order_by('group', 'name')
lists = TaskList.objects.all().order_by("group", "name")
else:
lists = TaskList.objects.filter(group__in=request.user.groups.all()).order_by('group', 'name')
lists = TaskList.objects.filter(group__in=request.user.groups.all()).order_by(
"group", "name"
)
list_count = lists.count()
@ -66,17 +69,21 @@ def list_lists(request) -> HttpResponse:
if request.user.is_superuser:
task_count = Task.objects.filter(completed=0).count()
else:
task_count = Task.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,
"task_count": task_count,
"lists": lists,
"thedate": thedate,
"searchform": searchform,
"list_count": list_count,
"task_count": task_count,
}
return render(request, 'todo/list_lists.html', context)
return render(request, "todo/list_lists.html", context)
@staff_only
@ -92,10 +99,10 @@ def del_list(request, list_id: int, list_slug: str) -> HttpResponse:
if task_list.group not in request.user.groups.all() and not request.user.is_staff:
raise PermissionDenied
if request.method == 'POST':
if request.method == "POST":
TaskList.objects.get(id=task_list.id).delete()
messages.success(request, "{list_name} is gone.".format(list_name=task_list.name))
return redirect('todo:lists')
return redirect("todo:lists")
else:
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()
@ -108,7 +115,7 @@ def del_list(request, list_id: int, list_slug: str) -> HttpResponse:
"task_count_total": task_count_total,
}
return render(request, 'todo/del_list.html', context)
return render(request, "todo/del_list.html", context)
@login_required
@ -141,33 +148,36 @@ def list_detail(request, list_id=None, list_slug=None, view_completed=False):
# Add New Task Form
# ######################
if request.POST.getlist('add_edit_task'):
form = AddEditTaskForm(request.user, request.POST, initial={
'assigned_to': request.user.id,
'priority': 999,
'task_list': task_list
})
if request.POST.getlist("add_edit_task"):
form = AddEditTaskForm(
request.user,
request.POST,
initial={"assigned_to": request.user.id, "priority": 999, "task_list": task_list},
)
if form.is_valid():
new_task = form.save(commit=False)
new_task.created_date = timezone.now()
new_task.note = bleach.clean(form.cleaned_data['note'], strip=True)
new_task.note = bleach.clean(form.cleaned_data["note"], strip=True)
form.save()
# Send email alert only if Notify checkbox is checked AND assignee is not same as the submitter
if "notify" in request.POST and new_task.assigned_to and new_task.assigned_to != request.user:
if (
"notify" in request.POST
and new_task.assigned_to
and new_task.assigned_to != request.user
):
send_notify_mail(new_task)
messages.success(request, "New task \"{t}\" has been added.".format(t=new_task.title))
messages.success(request, 'New task "{t}" has been added.'.format(t=new_task.title))
return redirect(request.path)
else:
# Don't allow adding new tasks on some views
if list_slug not in ["mine", "recent-add", "recent-complete", ]:
form = AddEditTaskForm(request.user, initial={
'assigned_to': request.user.id,
'priority': 999,
'task_list': task_list,
})
if list_slug not in ["mine", "recent-add", "recent-complete"]:
form = AddEditTaskForm(
request.user,
initial={"assigned_to": request.user.id, "priority": 999, "task_list": task_list},
)
context = {
"list_id": list_id,
@ -178,7 +188,7 @@ def list_detail(request, list_id=None, list_slug=None, view_completed=False):
"view_completed": view_completed,
}
return render(request, 'todo/list_detail.html', context)
return render(request, "todo/list_detail.html", context)
@login_required
@ -195,52 +205,54 @@ def task_detail(request, task_id: int) -> HttpResponse:
raise PermissionDenied
# Save submitted comments
if request.POST.get('add_comment'):
if request.POST.get("add_comment"):
Comment.objects.create(
author=request.user,
task=task,
body=bleach.clean(request.POST['comment-body'], strip=True),
body=bleach.clean(request.POST["comment-body"], strip=True),
)
send_email_to_thread_participants(
task, request.POST['comment-body'], request.user,
subject='New comment posted on task "{}"'.format(task.title))
task,
request.POST["comment-body"],
request.user,
subject='New comment posted on task "{}"'.format(task.title),
)
messages.success(request, "Comment posted. Notification email sent to thread participants.")
# Save task edits
if request.POST.get('add_edit_task'):
form = AddEditTaskForm(request.user, request.POST, instance=task, initial={'task_list': task.task_list})
if request.POST.get("add_edit_task"):
form = AddEditTaskForm(
request.user, request.POST, instance=task, initial={"task_list": task.task_list}
)
if form.is_valid():
item = form.save(commit=False)
item.note = bleach.clean(form.cleaned_data['note'], strip=True)
item.note = bleach.clean(form.cleaned_data["note"], strip=True)
item.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)
return redirect(
"todo:list_detail", list_id=task.task_list.id, list_slug=task.task_list.slug
)
else:
form = AddEditTaskForm(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'):
results_changed = toggle_done([task.id, ])
if request.POST.get("toggle_done"):
results_changed = toggle_done([task.id])
for res in results_changed:
messages.success(request, res)
return redirect('todo:task_detail', task_id=task.id,)
return redirect("todo:task_detail", task_id=task.id)
if task.due_date:
thedate = task.due_date
else:
thedate = datetime.datetime.now()
context = {
"task": task,
"comment_list": comment_list,
"form": form,
"thedate": thedate,
}
context = {"task": task, "comment_list": comment_list, "form": form, "thedate": thedate}
return render(request, 'todo/task_detail.html', context)
return render(request, "todo/task_detail.html", context)
@csrf_exempt
@ -248,7 +260,7 @@ def task_detail(request, task_id: int) -> HttpResponse:
def reorder_tasks(request) -> HttpResponse:
"""Handle task re-ordering (priorities) from JQuery drag/drop in list_detail.html
"""
newtasklist = request.POST.getlist('tasktable[]')
newtasklist = request.POST.getlist("tasktable[]")
if newtasklist:
# First task in received list is always empty - remove it
del newtasklist[0]
@ -280,24 +292,23 @@ def add_list(request) -> HttpResponse:
newlist.slug = slugify(newlist.name)
newlist.save()
messages.success(request, "A new list has been added.")
return redirect('todo:lists')
return redirect("todo:lists")
except IntegrityError:
messages.warning(
request,
"There was a problem saving the new list. "
"Most likely a list with the same name in the same group already exists.")
"Most likely a list with the same name in the same group already exists.",
)
else:
if request.user.groups.all().count() == 1:
form = AddTaskListForm(request.user, initial={"group": request.user.groups.all()[0]})
else:
form = AddTaskListForm(request.user)
context = {
"form": form,
}
context = {"form": form}
return render(request, 'todo/add_list.html', context)
return render(request, "todo/add_list.html", context)
@login_required
@ -306,36 +317,32 @@ def search(request) -> HttpResponse:
"""
if request.GET:
query_string = ''
query_string = ""
found_tasks = None
if ('q' in request.GET) and request.GET['q'].strip():
query_string = request.GET['q']
if ("q" in request.GET) and request.GET["q"].strip():
query_string = request.GET["q"]
found_tasks = Task.objects.filter(
Q(title__icontains=query_string) |
Q(note__icontains=query_string)
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_tasks in a queryset so it can be "excluded" below.
found_tasks = Task.objects.all()
if 'inc_complete' in request.GET:
if "inc_complete" in request.GET:
found_tasks = found_tasks.exclude(completed=True)
else:
query_string = None
found_tasks =None
found_tasks = None
# Only include tasks that are in groups of which this user is a member:
if not request.user.is_superuser:
found_tasks = found_tasks.filter(task_list__group__in=request.user.groups.all())
context = {
'query_string': query_string,
'found_tasks': found_tasks
}
return render(request, 'todo/search_results.html', context)
context = {"query_string": query_string, "found_tasks": found_tasks}
return render(request, "todo/search_results.html", context)
@login_required
@ -348,10 +355,14 @@ def external_add(request) -> HttpResponse:
"""
if not settings.TODO_DEFAULT_LIST_SLUG:
raise RuntimeError("This feature requires TODO_DEFAULT_LIST_SLUG: in settings. See documentation.")
raise RuntimeError(
"This feature requires TODO_DEFAULT_LIST_SLUG: in settings. See documentation."
)
if not TaskList.objects.filter(slug=settings.TODO_DEFAULT_LIST_SLUG).exists():
raise RuntimeError("There is no TaskList with slug specified for TODO_DEFAULT_LIST_SLUG in settings.")
raise RuntimeError(
"There is no TaskList with slug specified for TODO_DEFAULT_LIST_SLUG in settings."
)
if request.POST:
form = AddExternalTaskForm(request.POST)
@ -367,26 +378,36 @@ def external_add(request) -> HttpResponse:
# Send email to assignee if we have one
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, })
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, task.created_by.email,
[task.assigned_to.email, ], fail_silently=False)
email_subject,
email_body,
task.created_by.email,
[task.assigned_to.email],
fail_silently=False,
)
except ConnectionRefusedError:
messages.warning(request, "Task saved but mail not sent. Contact your administrator.")
messages.warning(
request, "Task saved but mail not sent. Contact your administrator."
)
messages.success(request, "Your trouble ticket has been submitted. We'll get back to you soon.")
messages.success(
request, "Your trouble ticket has been submitted. We'll get back to you soon."
)
return redirect(settings.TODO_PUBLIC_SUBMIT_REDIRECT)
else:
form = AddExternalTaskForm(initial={'priority': 999})
form = AddExternalTaskForm(initial={"priority": 999})
context = {
"form": form,
}
context = {"form": form}
return render(request, 'todo/add_task_external.html', context)
return render(request, "todo/add_task_external.html", context)
@login_required
@ -399,9 +420,9 @@ def toggle_done(request, task_id: int) -> HttpResponse:
# Permissions
if not (
(task.created_by == request.user) or
(task.assigned_to == request.user) or
(task.task_list.group in request.user.groups.all())
(task.created_by == request.user)
or (task.assigned_to == request.user)
or (task.task_list.group in request.user.groups.all())
):
raise PermissionDenied
@ -410,8 +431,9 @@ def toggle_done(request, task_id: int) -> HttpResponse:
task.save()
messages.success(request, "Task status changed for '{}'".format(task.title))
return redirect(reverse('todo:list_detail', kwargs={"list_id": tlist.id, "list_slug": tlist.slug}))
return redirect(
reverse("todo:list_detail", kwargs={"list_id": tlist.id, "list_slug": tlist.slug})
)
@login_required
@ -424,9 +446,9 @@ def delete_task(request, task_id: int) -> HttpResponse:
# Permissions
if not (
(task.created_by == request.user) or
(task.assigned_to == request.user) or
(task.task_list.group in request.user.groups.all())
(task.created_by == request.user)
or (task.assigned_to == request.user)
or (task.task_list.group in request.user.groups.all())
):
raise PermissionDenied
@ -434,4 +456,7 @@ def delete_task(request, task_id: int) -> HttpResponse:
task.delete()
messages.success(request, "Task '{}' has been deleted".format(task.title))
return redirect(reverse('todo:list_detail', kwargs={"list_id": tlist.id, "list_slug": tlist.slug}))
return redirect(
reverse("todo:list_detail", kwargs={"list_id": tlist.id, "list_slug": tlist.slug})
)