Formatting
This commit is contained in:
parent
4a385bde6b
commit
befc7ad2cd
28 changed files with 253 additions and 311 deletions
|
@ -10,6 +10,4 @@ For your project, ignore this file and add
|
|||
to your site's urlconf.
|
||||
"""
|
||||
|
||||
urlpatterns = [
|
||||
path('lists/', include('todo.urls')),
|
||||
]
|
||||
urlpatterns = [path("lists/", include("todo.urls"))]
|
||||
|
|
|
@ -2,11 +2,7 @@ import os
|
|||
|
||||
DEBUG = (True,)
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
|
||||
DATABASES = {
|
||||
"default": {
|
||||
"ENGINE": "django.db.backends.sqlite3"
|
||||
}
|
||||
}
|
||||
DATABASES = {"default": {"ENGINE": "django.db.backends.sqlite3"}}
|
||||
|
||||
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
|
||||
|
||||
|
@ -65,28 +61,12 @@ TEMPLATES = [
|
|||
]
|
||||
|
||||
LOGGING = {
|
||||
'version': 1,
|
||||
'disable_existing_loggers': False,
|
||||
'handlers': {
|
||||
'console': {
|
||||
'class': 'logging.StreamHandler',
|
||||
},
|
||||
},
|
||||
'loggers': {
|
||||
'': {
|
||||
'handlers': ['console'],
|
||||
'level': 'DEBUG',
|
||||
'propagate': True,
|
||||
},
|
||||
'django': {
|
||||
'handlers': ['console'],
|
||||
'level': 'WARNING',
|
||||
'propagate': True,
|
||||
},
|
||||
'django.request': {
|
||||
'handlers': ['console'],
|
||||
'level': 'DEBUG',
|
||||
'propagate': True,
|
||||
},
|
||||
"version": 1,
|
||||
"disable_existing_loggers": False,
|
||||
"handlers": {"console": {"class": "logging.StreamHandler"}},
|
||||
"loggers": {
|
||||
"": {"handlers": ["console"], "level": "DEBUG", "propagate": True},
|
||||
"django": {"handlers": ["console"], "level": "WARNING", "propagate": True},
|
||||
"django.request": {"handlers": ["console"], "level": "DEBUG", "propagate": True},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
"""
|
||||
A multi-user, multi-group task management and assignment system for Django.
|
||||
"""
|
||||
__version__ = '2.4.6'
|
||||
__version__ = "2.4.6"
|
||||
|
||||
__author__ = 'Scot Hacker'
|
||||
__email__ = 'shacker@birdhouse.org'
|
||||
__author__ = "Scot Hacker"
|
||||
__email__ = "shacker@birdhouse.org"
|
||||
|
||||
__url__ = 'https://github.com/shacker/django-todo'
|
||||
__license__ = 'BSD License'
|
||||
__url__ = "https://github.com/shacker/django-todo"
|
||||
__license__ = "BSD License"
|
||||
|
||||
from . import check
|
||||
|
|
|
@ -11,9 +11,7 @@ def dal_check(app_configs, **kwargs):
|
|||
return []
|
||||
|
||||
errors = []
|
||||
missing_apps = {'dal', 'dal_select2'} - set(settings.INSTALLED_APPS)
|
||||
missing_apps = {"dal", "dal_select2"} - set(settings.INSTALLED_APPS)
|
||||
for missing_app in missing_apps:
|
||||
errors.append(
|
||||
Error('{} needs to be in INSTALLED_APPS'.format(missing_app))
|
||||
)
|
||||
errors.append(Error("{} needs to be in INSTALLED_APPS".format(missing_app)))
|
||||
return errors
|
||||
|
|
|
@ -11,5 +11,6 @@ except ImportError:
|
|||
HAS_TASK_MERGE = False
|
||||
if HAS_AUTOCOMPLETE:
|
||||
import dal.autocomplete
|
||||
if getattr(dal.autocomplete, 'Select2QuerySetView', None) is not None:
|
||||
|
||||
if getattr(dal.autocomplete, "Select2QuerySetView", None) is not None:
|
||||
HAS_TASK_MERGE = True
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import importlib
|
||||
|
||||
|
||||
def _declare_backend(backend_path):
|
||||
backend_path = backend_path.split('.')
|
||||
backend_module_name = '.'.join(backend_path[:-1])
|
||||
backend_path = backend_path.split(".")
|
||||
backend_module_name = ".".join(backend_path[:-1])
|
||||
class_name = backend_path[-1]
|
||||
|
||||
def backend(*args, headers={}, from_address=None, **kwargs):
|
||||
|
@ -17,9 +18,10 @@ def _declare_backend(backend_path):
|
|||
_backend.from_address = from_address
|
||||
_backend.headers = headers
|
||||
return _backend
|
||||
|
||||
return backend
|
||||
|
||||
|
||||
smtp_backend = _declare_backend('django.core.mail.backends.smtp.EmailBackend')
|
||||
console_backend = _declare_backend('django.core.mail.backends.console.EmailBackend')
|
||||
locmem_backend = _declare_backend('django.core.mail.backends.locmem.EmailBackend')
|
||||
smtp_backend = _declare_backend("django.core.mail.backends.smtp.EmailBackend")
|
||||
console_backend = _declare_backend("django.core.mail.backends.console.EmailBackend")
|
||||
locmem_backend = _declare_backend("django.core.mail.backends.locmem.EmailBackend")
|
||||
|
|
|
@ -70,14 +70,12 @@ def imap_producer(
|
|||
try:
|
||||
yield message
|
||||
except Exception:
|
||||
logger.exception(
|
||||
f"something went wrong while processing {message_uid}"
|
||||
)
|
||||
logger.exception(f"something went wrong while processing {message_uid}")
|
||||
raise
|
||||
|
||||
if not preserve:
|
||||
# tag the message for deletion
|
||||
conn.store(message_uid, '+FLAGS', '\\Deleted')
|
||||
conn.store(message_uid, "+FLAGS", "\\Deleted")
|
||||
else:
|
||||
logger.debug("did not receive any message")
|
||||
finally:
|
||||
|
|
|
@ -18,7 +18,7 @@ def gen_title(tc=True):
|
|||
# faker doesn't provide a way to generate headlines in Title Case, without periods, so make our own.
|
||||
# With arg `tc=True`, Title Cases The Generated Text
|
||||
fake = Faker()
|
||||
thestr = fake.text(max_nb_chars=32).rstrip('.')
|
||||
thestr = fake.text(max_nb_chars=32).rstrip(".")
|
||||
if tc:
|
||||
thestr = titlecase(thestr)
|
||||
|
||||
|
@ -29,7 +29,7 @@ def gen_content():
|
|||
# faker provides paragraphs as a list; convert with linebreaks
|
||||
fake = Faker()
|
||||
grafs = fake.paragraphs()
|
||||
thestr = ''
|
||||
thestr = ""
|
||||
for g in grafs:
|
||||
thestr += "{}\n\n".format(g)
|
||||
return thestr
|
||||
|
@ -43,11 +43,12 @@ class Command(BaseCommand):
|
|||
"-d",
|
||||
"--delete",
|
||||
help="Wipe out existing content before generating new.",
|
||||
action="store_true")
|
||||
action="store_true",
|
||||
)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
|
||||
if options.get('delete'):
|
||||
if options.get("delete"):
|
||||
# Wipe out previous contents? Cascade deletes the Tasks from the TaskLists.
|
||||
TaskList.objects.all().delete()
|
||||
print("Content from previous run deleted.")
|
||||
|
@ -56,11 +57,11 @@ class Command(BaseCommand):
|
|||
fake = Faker() # Use to create user's names
|
||||
|
||||
# Create users and groups, add different users to different groups. Staff user is in both groups.
|
||||
sd_group, created = Group.objects.get_or_create(name='Scuba Divers')
|
||||
bw_group, created = Group.objects.get_or_create(name='Basket Weavers')
|
||||
sd_group, created = Group.objects.get_or_create(name="Scuba Divers")
|
||||
bw_group, created = Group.objects.get_or_create(name="Basket Weavers")
|
||||
|
||||
# Put user1 and user2 in one group, user3 and user4 in another
|
||||
usernames = ['user1', 'user2', 'user3', 'user4', 'staffer']
|
||||
usernames = ["user1", "user2", "user3", "user4", "staffer"]
|
||||
for username in usernames:
|
||||
if get_user_model().objects.filter(username=username).exists():
|
||||
user = get_user_model().objects.get(username=username)
|
||||
|
@ -70,15 +71,16 @@ class Command(BaseCommand):
|
|||
first_name=fake.first_name(),
|
||||
last_name=fake.last_name(),
|
||||
email="{}@example.com".format(username),
|
||||
password="todo")
|
||||
password="todo",
|
||||
)
|
||||
|
||||
if username in ['user1', 'user2']:
|
||||
if username in ["user1", "user2"]:
|
||||
user.groups.add(bw_group)
|
||||
|
||||
if username in ['user3', 'user4']:
|
||||
if username in ["user3", "user4"]:
|
||||
user.groups.add(sd_group)
|
||||
|
||||
if username == 'staffer':
|
||||
if username == "staffer":
|
||||
user.is_staff = True
|
||||
user.first_name = fake.first_name()
|
||||
user.last_name = fake.last_name()
|
||||
|
@ -91,7 +93,9 @@ class Command(BaseCommand):
|
|||
TaskListFactory.create_batch(5, group=sd_group)
|
||||
TaskListFactory.create(name="Public Tickets", slug="tickets", group=bw_group)
|
||||
|
||||
print("For each of two groups, created fake tasks in each of {} fake lists.".format(num_lists))
|
||||
print(
|
||||
"For each of two groups, created fake tasks in each of {} fake lists.".format(num_lists)
|
||||
)
|
||||
|
||||
|
||||
class TaskListFactory(factory.django.DjangoModelFactory):
|
||||
|
@ -120,9 +124,11 @@ class TaskFactory(factory.django.DjangoModelFactory):
|
|||
task_list = None # Pass this in
|
||||
note = factory.LazyAttribute(lambda o: gen_content())
|
||||
priority = factory.LazyAttribute(lambda o: random.randint(1, 100))
|
||||
completed = factory.Faker('boolean', chance_of_getting_true=30)
|
||||
created_by = factory.LazyAttribute(lambda o: get_user_model().objects.get(username='staffer')) # Randomized in post
|
||||
created_date = factory.Faker('date_this_year')
|
||||
completed = factory.Faker("boolean", chance_of_getting_true=30)
|
||||
created_by = factory.LazyAttribute(
|
||||
lambda o: get_user_model().objects.get(username="staffer")
|
||||
) # Randomized in post
|
||||
created_date = factory.Faker("date_this_year")
|
||||
|
||||
@factory.post_generation
|
||||
def add_details(self, build, extracted, **kwargs):
|
||||
|
@ -130,7 +136,7 @@ class TaskFactory(factory.django.DjangoModelFactory):
|
|||
fake = Faker() # Use to create user's names
|
||||
taskgroup = self.task_list.group
|
||||
|
||||
self.created_by = taskgroup.user_set.all().order_by('?').first()
|
||||
self.created_by = taskgroup.user_set.all().order_by("?").first()
|
||||
|
||||
if self.completed:
|
||||
self.completed_date = fake.date_this_year()
|
||||
|
@ -141,6 +147,6 @@ class TaskFactory(factory.django.DjangoModelFactory):
|
|||
|
||||
# 1/3 of generated tasks are assigned to someone in this tasks's group
|
||||
if random.randint(1, 3) == 1:
|
||||
self.assigned_to = taskgroup.user_set.all().order_by('?').first()
|
||||
self.assigned_to = taskgroup.user_set.all().order_by("?").first()
|
||||
|
||||
self.save()
|
||||
|
|
|
@ -26,10 +26,7 @@ class Command(BaseCommand):
|
|||
worker_name = options["worker_name"]
|
||||
tracker = settings.TODO_MAIL_TRACKERS.get(worker_name, None)
|
||||
if tracker is None:
|
||||
logger.error(
|
||||
"couldn't find configuration for %r in TODO_MAIL_TRACKERS",
|
||||
worker_name
|
||||
)
|
||||
logger.error("couldn't find configuration for %r in TODO_MAIL_TRACKERS", worker_name)
|
||||
sys.exit(1)
|
||||
|
||||
# set the default socket timeout (imaplib doesn't enable configuring it)
|
||||
|
|
|
@ -9,70 +9,93 @@ from django.conf import settings
|
|||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('auth', '0001_initial'),
|
||||
("auth", "0001_initial"),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Comment',
|
||||
name="Comment",
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('date', models.DateTimeField(default=datetime.datetime.now)),
|
||||
('body', models.TextField(blank=True)),
|
||||
('author', models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
verbose_name="ID", serialize=False, auto_created=True, primary_key=True
|
||||
),
|
||||
),
|
||||
("date", models.DateTimeField(default=datetime.datetime.now)),
|
||||
("body", models.TextField(blank=True)),
|
||||
(
|
||||
"author",
|
||||
models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE),
|
||||
),
|
||||
],
|
||||
options={
|
||||
},
|
||||
options={},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Item',
|
||||
name="Item",
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('title', models.CharField(max_length=140)),
|
||||
('created_date', models.DateField(auto_now=True, auto_now_add=True)),
|
||||
('due_date', models.DateField(null=True, blank=True)),
|
||||
('completed', models.BooleanField(default=None)),
|
||||
('completed_date', models.DateField(null=True, blank=True)),
|
||||
('note', models.TextField(null=True, blank=True)),
|
||||
('priority', models.PositiveIntegerField(max_length=3)),
|
||||
('assigned_to', models.ForeignKey(related_name='todo_assigned_to', to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)),
|
||||
('created_by', models.ForeignKey(related_name='todo_created_by', to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
verbose_name="ID", serialize=False, auto_created=True, primary_key=True
|
||||
),
|
||||
),
|
||||
("title", models.CharField(max_length=140)),
|
||||
("created_date", models.DateField(auto_now=True, auto_now_add=True)),
|
||||
("due_date", models.DateField(null=True, blank=True)),
|
||||
("completed", models.BooleanField(default=None)),
|
||||
("completed_date", models.DateField(null=True, blank=True)),
|
||||
("note", models.TextField(null=True, blank=True)),
|
||||
("priority", models.PositiveIntegerField(max_length=3)),
|
||||
(
|
||||
"assigned_to",
|
||||
models.ForeignKey(
|
||||
related_name="todo_assigned_to",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
on_delete=models.CASCADE,
|
||||
),
|
||||
),
|
||||
(
|
||||
"created_by",
|
||||
models.ForeignKey(
|
||||
related_name="todo_created_by",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
on_delete=models.CASCADE,
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'ordering': ['priority'],
|
||||
},
|
||||
options={"ordering": ["priority"]},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='List',
|
||||
name="List",
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('name', models.CharField(max_length=60)),
|
||||
('slug', models.SlugField(max_length=60, editable=False)),
|
||||
('group', models.ForeignKey(to='auth.Group', on_delete=models.CASCADE)),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
verbose_name="ID", serialize=False, auto_created=True, primary_key=True
|
||||
),
|
||||
),
|
||||
("name", models.CharField(max_length=60)),
|
||||
("slug", models.SlugField(max_length=60, editable=False)),
|
||||
("group", models.ForeignKey(to="auth.Group", on_delete=models.CASCADE)),
|
||||
],
|
||||
options={
|
||||
'ordering': ['name'],
|
||||
'verbose_name_plural': 'Lists',
|
||||
},
|
||||
options={"ordering": ["name"], "verbose_name_plural": "Lists"},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='list',
|
||||
unique_together=set([('group', 'slug')]),
|
||||
),
|
||||
migrations.AlterUniqueTogether(name="list", unique_together=set([("group", "slug")])),
|
||||
migrations.AddField(
|
||||
model_name='item',
|
||||
name='list',
|
||||
field=models.ForeignKey(to='todo.List', on_delete=models.CASCADE),
|
||||
model_name="item",
|
||||
name="list",
|
||||
field=models.ForeignKey(to="todo.List", on_delete=models.CASCADE),
|
||||
preserve_default=True,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='comment',
|
||||
name='task',
|
||||
field=models.ForeignKey(to='todo.Item', on_delete=models.CASCADE),
|
||||
model_name="comment",
|
||||
name="task",
|
||||
field=models.ForeignKey(to="todo.Item", on_delete=models.CASCADE),
|
||||
preserve_default=True,
|
||||
),
|
||||
]
|
||||
|
|
|
@ -6,19 +6,13 @@ from django.db import models, migrations
|
|||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('todo', '0001_initial'),
|
||||
]
|
||||
dependencies = [("todo", "0001_initial")]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='item',
|
||||
name='created_date',
|
||||
field=models.DateField(auto_now=True),
|
||||
model_name="item", name="created_date", field=models.DateField(auto_now=True)
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='item',
|
||||
name='priority',
|
||||
field=models.PositiveIntegerField(),
|
||||
model_name="item", name="priority", field=models.PositiveIntegerField()
|
||||
),
|
||||
]
|
||||
|
|
|
@ -9,14 +9,18 @@ import django.db.models.deletion
|
|||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('todo', '0002_auto_20150614_2339'),
|
||||
]
|
||||
dependencies = [("todo", "0002_auto_20150614_2339")]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='item',
|
||||
name='assigned_to',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='todo_assigned_to', to=settings.AUTH_USER_MODEL),
|
||||
),
|
||||
model_name="item",
|
||||
name="assigned_to",
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="todo_assigned_to",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
)
|
||||
]
|
||||
|
|
|
@ -7,46 +7,39 @@ import django.db.models.deletion
|
|||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('auth', '0009_alter_user_last_name_max_length'),
|
||||
('todo', '0003_assignee_optional'),
|
||||
("auth", "0009_alter_user_last_name_max_length"),
|
||||
("todo", "0003_assignee_optional"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='TaskList',
|
||||
name="TaskList",
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=60)),
|
||||
('slug', models.SlugField(default='')),
|
||||
('group', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='auth.Group')),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
|
||||
),
|
||||
),
|
||||
("name", models.CharField(max_length=60)),
|
||||
("slug", models.SlugField(default="")),
|
||||
(
|
||||
"group",
|
||||
models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="auth.Group"),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'verbose_name_plural': 'Lists',
|
||||
'ordering': ['name'],
|
||||
},
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='list',
|
||||
unique_together=set(),
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='list',
|
||||
name='group',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='item',
|
||||
name='list',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='List',
|
||||
options={"verbose_name_plural": "Lists", "ordering": ["name"]},
|
||||
),
|
||||
migrations.AlterUniqueTogether(name="list", unique_together=set()),
|
||||
migrations.RemoveField(model_name="list", name="group"),
|
||||
migrations.RemoveField(model_name="item", name="list"),
|
||||
migrations.DeleteModel(name="List"),
|
||||
migrations.AddField(
|
||||
model_name='item',
|
||||
name='task_list',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='todo.TaskList'),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='tasklist',
|
||||
unique_together={('group', 'slug')},
|
||||
model_name="item",
|
||||
name="task_list",
|
||||
field=models.ForeignKey(
|
||||
null=True, on_delete=django.db.models.deletion.CASCADE, to="todo.TaskList"
|
||||
),
|
||||
),
|
||||
migrations.AlterUniqueTogether(name="tasklist", unique_together={("group", "slug")}),
|
||||
]
|
||||
|
|
|
@ -5,18 +5,13 @@ from django.db import migrations, models
|
|||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('todo', '0004_rename_list_tasklist'),
|
||||
]
|
||||
dependencies = [("todo", "0004_rename_list_tasklist")]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='tasklist',
|
||||
options={'ordering': ['name'], 'verbose_name_plural': 'Task Lists'},
|
||||
name="tasklist", options={"ordering": ["name"], "verbose_name_plural": "Task Lists"}
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='item',
|
||||
name='completed',
|
||||
field=models.BooleanField(default=False),
|
||||
model_name="item", name="completed", field=models.BooleanField(default=False)
|
||||
),
|
||||
]
|
||||
|
|
|
@ -8,12 +8,7 @@ class Migration(migrations.Migration):
|
|||
atomic = False
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('todo', '0005_auto_20180212_2325'),
|
||||
("todo", "0005_auto_20180212_2325"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameModel(
|
||||
old_name='Item',
|
||||
new_name='Task',
|
||||
),
|
||||
]
|
||||
operations = [migrations.RenameModel(old_name="Item", new_name="Task")]
|
||||
|
|
|
@ -6,14 +6,12 @@ import django.utils.timezone
|
|||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('todo', '0006_rename_item_model'),
|
||||
]
|
||||
dependencies = [("todo", "0006_rename_item_model")]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='task',
|
||||
name='created_date',
|
||||
model_name="task",
|
||||
name="created_date",
|
||||
field=models.DateField(blank=True, default=django.utils.timezone.now, null=True),
|
||||
),
|
||||
)
|
||||
]
|
||||
|
|
|
@ -5,18 +5,15 @@ from django.db import migrations, models
|
|||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('todo', '0008_mail_tracker'),
|
||||
]
|
||||
dependencies = [("todo", "0008_mail_tracker")]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='task',
|
||||
options={'ordering': ['priority', 'created_date']},
|
||||
name="task", options={"ordering": ["priority", "created_date"]}
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='task',
|
||||
name='priority',
|
||||
model_name="task",
|
||||
name="priority",
|
||||
field=models.PositiveIntegerField(blank=True, null=True),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -11,18 +11,36 @@ class Migration(migrations.Migration):
|
|||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('todo', '0009_priority_optional'),
|
||||
("todo", "0009_priority_optional"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Attachment',
|
||||
name="Attachment",
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('timestamp', models.DateTimeField(default=datetime.datetime.now)),
|
||||
('file', models.FileField(max_length=255, upload_to=todo.models.get_attachment_upload_dir)),
|
||||
('added_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
('task', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='todo.Task')),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
|
||||
),
|
||||
),
|
||||
("timestamp", models.DateTimeField(default=datetime.datetime.now)),
|
||||
(
|
||||
"file",
|
||||
models.FileField(
|
||||
max_length=255, upload_to=todo.models.get_attachment_upload_dir
|
||||
),
|
||||
),
|
||||
(
|
||||
"added_by",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL
|
||||
),
|
||||
),
|
||||
(
|
||||
"task",
|
||||
models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="todo.Task"),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
]
|
||||
|
|
|
@ -45,9 +45,7 @@ class LockedAtomicTransaction(Atomic):
|
|||
cursor = get_connection(self.using).cursor()
|
||||
for model in self.models:
|
||||
cursor.execute(
|
||||
"LOCK TABLE {table_name}".format(
|
||||
table_name=model._meta.db_table
|
||||
)
|
||||
"LOCK TABLE {table_name}".format(table_name=model._meta.db_table)
|
||||
)
|
||||
finally:
|
||||
if cursor and not cursor.closed:
|
||||
|
@ -159,9 +157,7 @@ class Comment(models.Model):
|
|||
def snippet(self):
|
||||
body_snippet = textwrap.shorten(self.body, width=35, placeholder="...")
|
||||
# Define here rather than in __str__ so we can use it in the admin list_display
|
||||
return "{author} - {snippet}...".format(
|
||||
author=self.author_text, snippet=body_snippet
|
||||
)
|
||||
return "{author} - {snippet}...".format(author=self.author_text, snippet=body_snippet)
|
||||
|
||||
def __str__(self):
|
||||
return self.snippet
|
||||
|
@ -173,9 +169,7 @@ class Attachment(models.Model):
|
|||
"""
|
||||
|
||||
task = models.ForeignKey(Task, on_delete=models.CASCADE)
|
||||
added_by = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL, on_delete=models.CASCADE
|
||||
)
|
||||
added_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
|
||||
timestamp = models.DateTimeField(default=datetime.datetime.now)
|
||||
file = models.FileField(upload_to=get_attachment_upload_dir, max_length=255)
|
||||
|
||||
|
|
|
@ -67,7 +67,11 @@ class CSVImporter:
|
|||
# newrow at this point is fully validated, and all FK relations exist,
|
||||
# e.g. `newrow.get("Assigned To")`, is a Django User instance.
|
||||
assignee = newrow.get("Assigned To") if newrow.get("Assigned To") else None
|
||||
created_date = newrow.get("Created Date") if newrow.get("Created Date") else datetime.datetime.today()
|
||||
created_date = (
|
||||
newrow.get("Created Date")
|
||||
if newrow.get("Created Date")
|
||||
else datetime.datetime.today()
|
||||
)
|
||||
due_date = newrow.get("Due Date") if newrow.get("Due Date") else None
|
||||
priority = newrow.get("Priority") if newrow.get("Priority") else None
|
||||
|
||||
|
@ -178,7 +182,7 @@ class CSVImporter:
|
|||
row["Assigned To"] = assignee
|
||||
|
||||
# Set Completed
|
||||
row["Completed"] = (row["Completed"] == "Yes")
|
||||
row["Completed"] = row["Completed"] == "Yes"
|
||||
|
||||
# #######################
|
||||
if row_errors:
|
||||
|
|
|
@ -9,24 +9,21 @@ from email.message import EmailMessage
|
|||
|
||||
def consumer(*args, title_format="[TEST] {subject}", **kwargs):
|
||||
return tracker_consumer(
|
||||
group="Workgroup One",
|
||||
task_list_slug="zip",
|
||||
priority=1,
|
||||
task_title_format=title_format,
|
||||
group="Workgroup One", task_list_slug="zip", priority=1, task_title_format=title_format
|
||||
)(*args, **kwargs)
|
||||
|
||||
|
||||
def make_message(subject, content):
|
||||
msg = EmailMessage()
|
||||
msg.set_content(content)
|
||||
msg['Subject'] = subject
|
||||
msg["Subject"] = subject
|
||||
return msg
|
||||
|
||||
|
||||
def test_tracker_task_creation(todo_setup, django_user_model):
|
||||
msg = make_message("test1 subject", "test1 content")
|
||||
msg['From'] = 'test1@example.com'
|
||||
msg['Message-ID'] = '<a@example.com>'
|
||||
msg["From"] = "test1@example.com"
|
||||
msg["Message-ID"] = "<a@example.com>"
|
||||
|
||||
# test task creation
|
||||
task_count = Task.objects.count()
|
||||
|
@ -38,30 +35,26 @@ def test_tracker_task_creation(todo_setup, django_user_model):
|
|||
|
||||
# test thread answers
|
||||
msg = make_message("test2 subject", "test2 content")
|
||||
msg['From'] = 'test1@example.com'
|
||||
msg['Message-ID'] = '<b@example.com>'
|
||||
msg['References'] = '<nope@example.com> <a@example.com>'
|
||||
msg["From"] = "test1@example.com"
|
||||
msg["Message-ID"] = "<b@example.com>"
|
||||
msg["References"] = "<nope@example.com> <a@example.com>"
|
||||
|
||||
task_count = Task.objects.count()
|
||||
consumer([msg])
|
||||
assert task_count == Task.objects.count(), "comment created another task"
|
||||
Comment.objects.get(
|
||||
task=task,
|
||||
body__contains="test2 content",
|
||||
email_message_id='<b@example.com>'
|
||||
task=task, body__contains="test2 content", email_message_id="<b@example.com>"
|
||||
)
|
||||
|
||||
# test notification answer
|
||||
msg = make_message("test3 subject", "test3 content")
|
||||
msg['From'] = 'test1@example.com'
|
||||
msg['Message-ID'] = '<c@example.com>'
|
||||
msg['References'] = '<thread-{}@django-todo> <unknown@example.com>'.format(task.pk)
|
||||
msg["From"] = "test1@example.com"
|
||||
msg["Message-ID"] = "<c@example.com>"
|
||||
msg["References"] = "<thread-{}@django-todo> <unknown@example.com>".format(task.pk)
|
||||
|
||||
task_count = Task.objects.count()
|
||||
consumer([msg])
|
||||
assert task_count == Task.objects.count(), "comment created another task"
|
||||
Comment.objects.get(
|
||||
task=task,
|
||||
body__contains="test3 content",
|
||||
email_message_id='<c@example.com>'
|
||||
task=task, body__contains="test3 content", email_message_id="<c@example.com>"
|
||||
)
|
||||
|
|
|
@ -55,5 +55,6 @@ def test_send_email_to_thread_participants(todo_setup, django_user_model, email_
|
|||
assert "u3@example.com" in mail.outbox[0].recipients()
|
||||
assert "u4@example.com" in mail.outbox[0].recipients()
|
||||
|
||||
|
||||
# FIXME: Add tests for:
|
||||
# Attachments: Test whether allowed, test multiple, test extensions
|
||||
# Attachments: Test whether allowed, test multiple, test extensions
|
||||
|
|
|
@ -166,6 +166,7 @@ def test_no_javascript_in_comments(todo_setup, client):
|
|||
|
||||
# ### PERMISSIONS ###
|
||||
|
||||
|
||||
def test_view_add_list_nonadmin(todo_setup, client):
|
||||
url = reverse("todo:add_list")
|
||||
client.login(username="you", password="password")
|
||||
|
|
99
todo/urls.py
99
todo/urls.py
|
@ -4,93 +4,46 @@ from django.urls import path
|
|||
from todo import views
|
||||
from todo.features import HAS_TASK_MERGE
|
||||
|
||||
app_name = 'todo'
|
||||
app_name = "todo"
|
||||
|
||||
urlpatterns = [
|
||||
path(
|
||||
'',
|
||||
views.list_lists,
|
||||
name="lists"),
|
||||
|
||||
path("", views.list_lists, name="lists"),
|
||||
# View reorder_tasks is only called by JQuery for drag/drop task ordering.
|
||||
path(
|
||||
'reorder_tasks/',
|
||||
views.reorder_tasks,
|
||||
name="reorder_tasks"),
|
||||
|
||||
path("reorder_tasks/", views.reorder_tasks, name="reorder_tasks"),
|
||||
# Allow users to post tasks from outside django-todo (e.g. for filing tickets - see docs)
|
||||
path(
|
||||
'ticket/add/',
|
||||
views.external_add,
|
||||
name="external_add"),
|
||||
|
||||
path("ticket/add/", views.external_add, name="external_add"),
|
||||
# Three paths into `list_detail` view
|
||||
path("mine/", views.list_detail, {"list_slug": "mine"}, name="mine"),
|
||||
path(
|
||||
'mine/',
|
||||
"<int:list_id>/<str:list_slug>/completed/",
|
||||
views.list_detail,
|
||||
{'list_slug': 'mine'},
|
||||
name="mine"),
|
||||
|
||||
{"view_completed": True},
|
||||
name="list_detail_completed",
|
||||
),
|
||||
path("<int:list_id>/<str:list_slug>/", views.list_detail, name="list_detail"),
|
||||
path("<int:list_id>/<str:list_slug>/delete/", views.del_list, name="del_list"),
|
||||
path("add_list/", views.add_list, name="add_list"),
|
||||
path("task/<int:task_id>/", views.task_detail, name="task_detail"),
|
||||
path(
|
||||
'<int:list_id>/<str:list_slug>/completed/',
|
||||
views.list_detail,
|
||||
{'view_completed': True},
|
||||
name='list_detail_completed'),
|
||||
|
||||
path(
|
||||
'<int:list_id>/<str:list_slug>/',
|
||||
views.list_detail,
|
||||
name='list_detail'),
|
||||
|
||||
path(
|
||||
'<int:list_id>/<str:list_slug>/delete/',
|
||||
views.del_list,
|
||||
name="del_list"),
|
||||
|
||||
path(
|
||||
'add_list/',
|
||||
views.add_list,
|
||||
name="add_list"),
|
||||
|
||||
path(
|
||||
'task/<int:task_id>/',
|
||||
views.task_detail,
|
||||
name='task_detail'),
|
||||
|
||||
path(
|
||||
'attachment/remove/<int:attachment_id>/',
|
||||
views.remove_attachment,
|
||||
name='remove_attachment'),
|
||||
"attachment/remove/<int:attachment_id>/", views.remove_attachment, name="remove_attachment"
|
||||
),
|
||||
]
|
||||
|
||||
if HAS_TASK_MERGE:
|
||||
# ensure mail tracker autocomplete is optional
|
||||
from todo.views.task_autocomplete import TaskAutocomplete
|
||||
|
||||
urlpatterns.append(
|
||||
path(
|
||||
'task/<int:task_id>/autocomplete/',
|
||||
TaskAutocomplete.as_view(),
|
||||
name='task_autocomplete')
|
||||
"task/<int:task_id>/autocomplete/", TaskAutocomplete.as_view(), name="task_autocomplete"
|
||||
)
|
||||
)
|
||||
|
||||
urlpatterns.extend([
|
||||
path(
|
||||
'toggle_done/<int:task_id>/',
|
||||
views.toggle_done,
|
||||
name='task_toggle_done'),
|
||||
|
||||
path(
|
||||
'delete/<int:task_id>/',
|
||||
views.delete_task,
|
||||
name='delete_task'),
|
||||
|
||||
path(
|
||||
'search/',
|
||||
views.search,
|
||||
name="search"),
|
||||
|
||||
path(
|
||||
'import_csv/',
|
||||
views.import_csv,
|
||||
name="import_csv"),
|
||||
])
|
||||
urlpatterns.extend(
|
||||
[
|
||||
path("toggle_done/<int:task_id>/", views.toggle_done, name="task_toggle_done"),
|
||||
path("delete/<int:task_id>/", views.delete_task, name="delete_task"),
|
||||
path("search/", views.search, name="search"),
|
||||
path("import_csv/", views.import_csv, name="import_csv"),
|
||||
]
|
||||
)
|
||||
|
|
|
@ -20,7 +20,7 @@ def staff_check(user):
|
|||
https://github.com/shacker/django-todo/issues/50
|
||||
"""
|
||||
|
||||
if defaults('TODO_STAFF_ONLY'):
|
||||
if defaults("TODO_STAFF_ONLY"):
|
||||
return user.is_staff
|
||||
else:
|
||||
# If unset or False, allow all logged in users
|
||||
|
|
|
@ -43,7 +43,7 @@ def external_add(request) -> HttpResponse:
|
|||
task = form.save(commit=False)
|
||||
task.task_list = TaskList.objects.get(slug=settings.TODO_DEFAULT_LIST_SLUG)
|
||||
task.created_by = request.user
|
||||
if defaults('TODO_DEFAULT_ASSIGNEE'):
|
||||
if defaults("TODO_DEFAULT_ASSIGNEE"):
|
||||
task.assigned_to = User.objects.get(username=settings.TODO_DEFAULT_ASSIGNEE)
|
||||
task.save()
|
||||
|
||||
|
|
|
@ -18,10 +18,7 @@ def remove_attachment(request, attachment_id: int) -> HttpResponse:
|
|||
if request.method == "POST":
|
||||
attachment = get_object_or_404(Attachment, pk=attachment_id)
|
||||
|
||||
redir_url = reverse(
|
||||
"todo:task_detail",
|
||||
kwargs={"task_id": attachment.task.id},
|
||||
)
|
||||
redir_url = reverse("todo:task_detail", kwargs={"task_id": attachment.task.id})
|
||||
|
||||
# Permissions
|
||||
if not (
|
||||
|
@ -33,7 +30,9 @@ def remove_attachment(request, attachment_id: int) -> HttpResponse:
|
|||
if remove_attachment_file(attachment.id):
|
||||
messages.success(request, f"Attachment {attachment.id} removed.")
|
||||
else:
|
||||
messages.error(request, f"Sorry, there was a problem deleting attachment {attachment.id}.")
|
||||
messages.error(
|
||||
request, f"Sorry, there was a problem deleting attachment {attachment.id}."
|
||||
)
|
||||
|
||||
return redirect(redir_url)
|
||||
|
||||
|
|
|
@ -121,13 +121,13 @@ def task_detail(request, task_id: int) -> HttpResponse:
|
|||
if request.FILES.get("attachment_file_input"):
|
||||
file = request.FILES.get("attachment_file_input")
|
||||
|
||||
if file.size > defaults('TODO_MAXIMUM_ATTACHMENT_SIZE'):
|
||||
if file.size > defaults("TODO_MAXIMUM_ATTACHMENT_SIZE"):
|
||||
messages.error(request, f"File exceeds maximum attachment size.")
|
||||
return redirect("todo:task_detail", task_id=task.id)
|
||||
|
||||
name, extension = os.path.splitext(file.name)
|
||||
|
||||
if extension not in defaults('TODO_LIMIT_FILE_ATTACHMENTS'):
|
||||
if extension not in defaults("TODO_LIMIT_FILE_ATTACHMENTS"):
|
||||
messages.error(request, f"This site does not allow upload of {extension} files.")
|
||||
return redirect("todo:task_detail", task_id=task.id)
|
||||
|
||||
|
@ -144,7 +144,7 @@ def task_detail(request, task_id: int) -> HttpResponse:
|
|||
"merge_form": merge_form,
|
||||
"thedate": thedate,
|
||||
"comment_classes": defaults("TODO_COMMENT_CLASSES"),
|
||||
"attachments_enabled": defaults('TODO_ALLOW_FILE_ATTACHMENTS'),
|
||||
"attachments_enabled": defaults("TODO_ALLOW_FILE_ATTACHMENTS"),
|
||||
}
|
||||
|
||||
return render(request, "todo/task_detail.html", context)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue