Complete fake data loading via hopper

- Includes mods to task.created_date to allow overrides
This commit is contained in:
Scot Hacker 2018-04-05 00:27:53 -07:00
parent 403d1483f6
commit 1f27688aa4
9 changed files with 256 additions and 92 deletions

View file

@ -1,24 +1,19 @@
[[source]]
url = "https://pypi.python.org/simple"
verify_ssl = true
name = "pypi"
[packages]
django = "*"
django-extensions = "*"
"psycopg2-binary" = "*"
pytest = "*"
pytest-django = "*"
"flake8" = "*"
factory-boy = "*"
titlecase = "*"
[dev-packages]
[requires]
python_version = "3.6"

142
Pipfile.lock generated
View file

@ -1,20 +1,7 @@
{
"_meta": {
"hash": {
"sha256": "5a7237884b6690e23782690ae05e2d2ffd38a04d142f9fccc926860494073e9b"
},
"host-environment-markers": {
"implementation_name": "cpython",
"implementation_version": "3.6.3",
"os_name": "posix",
"platform_machine": "x86_64",
"platform_python_implementation": "CPython",
"platform_release": "17.4.0",
"platform_system": "Darwin",
"platform_version": "Darwin Kernel Version 17.4.0: Sun Dec 17 09:19:54 PST 2017; root:xnu-4570.41.2~1/RELEASE_X86_64",
"python_full_version": "3.6.3",
"python_version": "3.6",
"sys_platform": "darwin"
"sha256": "8aa62fe5923a75a6df757c643dbd98177e66bd0bc9e429d65006f042f73f5a32"
},
"pipfile-spec": 6,
"requires": {
@ -31,30 +18,48 @@
"default": {
"attrs": {
"hashes": [
"sha256:a17a9573a6f475c99b551c0e0a812707ddda1ec9653bed04c13841404ed6f450",
"sha256:1c7960ccfd6a005cd9f7ba884e6316b5e430a3f1a6c37c5f87d8b43f83b54ec9"
"sha256:1c7960ccfd6a005cd9f7ba884e6316b5e430a3f1a6c37c5f87d8b43f83b54ec9",
"sha256:a17a9573a6f475c99b551c0e0a812707ddda1ec9653bed04c13841404ed6f450"
],
"version": "==17.4.0"
},
"django": {
"hashes": [
"sha256:3d9916515599f757043c690ae2b5ea28666afa09779636351da505396cbb2f19",
"sha256:769f212ffd5762f72c764fa648fca3b7f7dd4ec27407198b68e7c4abf4609fd0"
"sha256:2d8b9eed8815f172a8e898678ae4289a5e9176bc08295676eff4228dd638ea61",
"sha256:d81a1652963c81488e709729a80b510394050e312f386037f26b54912a3a10d0"
],
"version": "==2.0.3"
"index": "pypi",
"version": "==2.0.4"
},
"django-extensions": {
"hashes": [
"sha256:37a543af370ee3b0721ff50442d33c357dd083e6ea06c5b94a199283b6f9e361",
"sha256:bc9f2946c117bb2f49e5e0633eba783787790ae810ea112fe7fd82fa64de2ff1"
],
"index": "pypi",
"version": "==2.0.6"
},
"factory-boy": {
"hashes": [
"sha256:bd5a096d0f102d79b6c78cef1c8c0b650f2e1a3ecba351c735c6d2df8dabd29c",
"sha256:be2abc8092294e4097935a29b4e37f5b9ed3e4205e2e32df215c0315b625995e"
],
"index": "pypi",
"version": "==2.10.0"
},
"faker": {
"hashes": [
"sha256:9cc12b821f32ff45f6edfdc1ab7be3893b60b1224e952d68322a57e5b26a4a15",
"sha256:b06d0dc0166618298e668ced513ced7b10df34f3ad2045f22f1d7d88704e8e9c"
],
"version": "==0.8.12"
},
"flake8": {
"hashes": [
"sha256:c7841163e2b576d435799169b78703ad6ac1bbb0f199994fc05f700b2a90ea37",
"sha256:7253265f7abd8b313e3892944044a365e3f4ac3fcdcfb4298f55ee9ddf188ba0"
"sha256:7253265f7abd8b313e3892944044a365e3f4ac3fcdcfb4298f55ee9ddf188ba0",
"sha256:c7841163e2b576d435799169b78703ad6ac1bbb0f199994fc05f700b2a90ea37"
],
"index": "pypi",
"version": "==3.5.0"
},
"mccabe": {
@ -66,8 +71,8 @@
},
"more-itertools": {
"hashes": [
"sha256:11a625025954c20145b37ff6309cd54e39ca94f72f6bb9576d1195db6fa2442e",
"sha256:0dd8f72eeab0d2c3bd489025bb2f6a1b8342f9b198f6fc37b52d15cfa4531fea",
"sha256:11a625025954c20145b37ff6309cd54e39ca94f72f6bb9576d1195db6fa2442e",
"sha256:c9ce7eccdcb901a2c75d326ea134e0886abfbea5f93e91cc95de9507c0816c44"
],
"version": "==4.1.0"
@ -80,47 +85,48 @@
},
"psycopg2-binary": {
"hashes": [
"sha256:b287ddf4cafcfb632974907d1e7862119e36bb758228bdb07dd247553e4cdfc0",
"sha256:d1dd3eb8edd354083f5d27b968c5a17854c41347ba5a480b520be85ec1a8495c",
"sha256:cf3911fba0c47fc1313b5783183cda301032b14637a0b7a336766ae46998c7ee",
"sha256:b039f51bca1ddd70234cc3f84f94f42ad43861b931bdfb497f887c60c39a6565",
"sha256:83af04029bcb4b56c852e5876fef71340dcb465fa44fc99f80bac72e10fb0b74",
"sha256:3a14baeabcebd4662f12f4bff03e0574a2369a2e41baf829e6fb4a24c95cf88b",
"sha256:cb07184a4bfad304831f0a88b1c13fbd8cf9fcdf1f11e71c477dd6d7b1b078a0",
"sha256:d0972f062c73956332e9681dfdb133168618f0abfecc96e89f0205ac89cd454b",
"sha256:ab1db8f3e96570d9f7ebc45133ce2574804b2280499baade178e163d022107b5",
"sha256:d51c7ed810fce1e50464088c37cc8da05534de8afb12a732500827ebcc480081",
"sha256:b6b2b26590304d97ef2af28d153ee99ace6fe0806934f4618edfc87216c77f91",
"sha256:9b5ddbed85ec73293695d7116589d956ef0dd3fcf7bf3b2a3bc1e8e54c1d543a",
"sha256:02eb674e3d5810e19b4d5d00720b17130e182da1ba259dda608aaf33d787347d",
"sha256:3a14baeabcebd4662f12f4bff03e0574a2369a2e41baf829e6fb4a24c95cf88b",
"sha256:436a503eda41f6adb08f292f40a3784fce0a5f351b6ae7b19a911904db53af93",
"sha256:8014c06a9ed7b78ba81beff3ae71acd78c212390f8ed839e9ce22735880bd5b4",
"sha256:c4c6004d410c77bfa5389ae9485498ce32805447a67afbfe8db0d247a5c88fa1",
"sha256:d8940b5104588d6313315e037f0f5ed68d2e5f62ccc1c429d3cff11d2ba6de3f",
"sha256:c606bff0978ee4858d86d40f6b6ab0c4cac4474f627bd054683dc03a4fc1a366",
"sha256:9305d7cbc802aaefac5c75a3df725f2654797369f32b18d4d0adb382dfab6c09",
"sha256:4a1a5ea2fa4b53191637b162873a82822d92a85a08beefe28296b8eb5cf2fea5",
"sha256:a3d2cc0cb0b988dbfd0d11f7fac34058b25a6ce533ed5b8e88d6cb315e77d54a",
"sha256:86c0d2587f56776f25d52cca8e275adf495c8e01933fbfc2ca23b124610ab761",
"sha256:77a2fc622a1f2d08a707673c9be5769d521f03d867d305f172bb417fa7882754",
"sha256:4a4f23a08fbccbe40ecdb5384d807bcb469ea71dd87e6be2e80b036b8e6d47df",
"sha256:c8220c521a408b41c4f14036004a621ed0d965941286b978cd2ea2623fabd755",
"sha256:465ff1d427ed42c31e456dbbd9edab3552be18a0edaef7450c5b3e6fee745052",
"sha256:4a1a5ea2fa4b53191637b162873a82822d92a85a08beefe28296b8eb5cf2fea5",
"sha256:4a4f23a08fbccbe40ecdb5384d807bcb469ea71dd87e6be2e80b036b8e6d47df",
"sha256:77a2fc622a1f2d08a707673c9be5769d521f03d867d305f172bb417fa7882754",
"sha256:8014c06a9ed7b78ba81beff3ae71acd78c212390f8ed839e9ce22735880bd5b4",
"sha256:83af04029bcb4b56c852e5876fef71340dcb465fa44fc99f80bac72e10fb0b74",
"sha256:86c0d2587f56776f25d52cca8e275adf495c8e01933fbfc2ca23b124610ab761",
"sha256:9305d7cbc802aaefac5c75a3df725f2654797369f32b18d4d0adb382dfab6c09",
"sha256:9b5ddbed85ec73293695d7116589d956ef0dd3fcf7bf3b2a3bc1e8e54c1d543a",
"sha256:a3d2cc0cb0b988dbfd0d11f7fac34058b25a6ce533ed5b8e88d6cb315e77d54a",
"sha256:ab1db8f3e96570d9f7ebc45133ce2574804b2280499baade178e163d022107b5",
"sha256:b039f51bca1ddd70234cc3f84f94f42ad43861b931bdfb497f887c60c39a6565",
"sha256:b287ddf4cafcfb632974907d1e7862119e36bb758228bdb07dd247553e4cdfc0",
"sha256:b6b2b26590304d97ef2af28d153ee99ace6fe0806934f4618edfc87216c77f91",
"sha256:c4c6004d410c77bfa5389ae9485498ce32805447a67afbfe8db0d247a5c88fa1",
"sha256:c606bff0978ee4858d86d40f6b6ab0c4cac4474f627bd054683dc03a4fc1a366",
"sha256:c8220c521a408b41c4f14036004a621ed0d965941286b978cd2ea2623fabd755",
"sha256:cb07184a4bfad304831f0a88b1c13fbd8cf9fcdf1f11e71c477dd6d7b1b078a0",
"sha256:cf3911fba0c47fc1313b5783183cda301032b14637a0b7a336766ae46998c7ee",
"sha256:d0972f062c73956332e9681dfdb133168618f0abfecc96e89f0205ac89cd454b",
"sha256:d1dd3eb8edd354083f5d27b968c5a17854c41347ba5a480b520be85ec1a8495c",
"sha256:d51c7ed810fce1e50464088c37cc8da05534de8afb12a732500827ebcc480081",
"sha256:d8940b5104588d6313315e037f0f5ed68d2e5f62ccc1c429d3cff11d2ba6de3f",
"sha256:de4f88f823037a71ea5ef3c1041d96b8a68d73343133edda684fd42f575bd9d7"
],
"index": "pypi",
"version": "==2.7.4"
},
"py": {
"hashes": [
"sha256:983f77f3331356039fdd792e9220b7b8ee1aa6bd2b25f567a963ff1de5a64f6a",
"sha256:29c9fab495d7528e80ba1e343b958684f4ace687327e6f789a94bf3d1915f881"
"sha256:29c9fab495d7528e80ba1e343b958684f4ace687327e6f789a94bf3d1915f881",
"sha256:983f77f3331356039fdd792e9220b7b8ee1aa6bd2b25f567a963ff1de5a64f6a"
],
"version": "==1.5.3"
},
"pycodestyle": {
"hashes": [
"sha256:6c4245ade1edfad79c3446fadfc96b0de2759662dc29d07d80a6f27ad1ca6ba9",
"sha256:682256a5b318149ca0d2a9185d365d8864a768a28db66a84a2ea946bcc426766"
"sha256:682256a5b318149ca0d2a9185d365d8864a768a28db66a84a2ea946bcc426766",
"sha256:6c4245ade1edfad79c3446fadfc96b0de2759662dc29d07d80a6f27ad1ca6ba9"
],
"version": "==2.3.1"
},
@ -136,6 +142,7 @@
"sha256:6266f87ab64692112e5477eba395cfedda53b1933ccd29478e671e73b420c19c",
"sha256:fae491d1874f199537fd5872b5e1f0e74a009b979df9d53d1553fd03da1703e1"
],
"index": "pypi",
"version": "==3.5.0"
},
"pytest-django": {
@ -143,28 +150,51 @@
"sha256:00995c2999b884a38ae9cd30a8c00ed32b3d38c1041250ea84caf18085589662",
"sha256:038ccc5a9daa1b1b0eb739ab7dce54e495811eca5ea3af4815a2a3ac45152309"
],
"index": "pypi",
"version": "==3.1.2"
},
"python-dateutil": {
"hashes": [
"sha256:3220490fb9741e2342e1cf29a503394fdac874bc39568288717ee67047ff29df",
"sha256:9d8074be4c993fbe4947878ce593052f71dac82932a677d49194d8ce9778002e"
],
"version": "==2.7.2"
},
"pytz": {
"hashes": [
"sha256:ed6509d9af298b7995d69a440e2822288f2eca1681b8cce37673dbb10091e5fe",
"sha256:f93ddcdd6342f94cea379c73cddb5724e0d6d0a1c91c9bdef364dc0368ba4fda",
"sha256:61242a9abc626379574a166dc0e96a66cd7c3b27fc10868003fa210be4bff1c9",
"sha256:ba18e6a243b3625513d85239b3e49055a2f0318466e0b8a92b8fb8ca7ccdf55f",
"sha256:07edfc3d4d2705a20a6e99d97f0c4b61c800b8232dc1c04d87e8554f130148dd",
"sha256:3a47ff71597f821cd84a162e71593004286e5be07a340fd462f0d33a760782b5",
"sha256:410bcd1d6409026fbaa65d9ed33bf6dd8b1e94a499e32168acfc7b332e4095c0",
"sha256:5bd55c744e6feaa4d599a6cbd8228b4f8f9ba96de2c38d56f08e534b3c9edf0d",
"sha256:61242a9abc626379574a166dc0e96a66cd7c3b27fc10868003fa210be4bff1c9",
"sha256:887ab5e5b32e4d0c86efddd3d055c1f363cbaa583beb8da5e22d2fa2f64d51ef",
"sha256:410bcd1d6409026fbaa65d9ed33bf6dd8b1e94a499e32168acfc7b332e4095c0"
"sha256:ba18e6a243b3625513d85239b3e49055a2f0318466e0b8a92b8fb8ca7ccdf55f",
"sha256:ed6509d9af298b7995d69a440e2822288f2eca1681b8cce37673dbb10091e5fe",
"sha256:f93ddcdd6342f94cea379c73cddb5724e0d6d0a1c91c9bdef364dc0368ba4fda"
],
"version": "==2018.3"
},
"six": {
"hashes": [
"sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb",
"sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9"
"sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9",
"sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb"
],
"version": "==1.11.0"
},
"text-unidecode": {
"hashes": [
"sha256:5a1375bb2ba7968740508ae38d92e1f889a0832913cb1c447d5e2046061a396d",
"sha256:801e38bd550b943563660a91de8d4b6fa5df60a542be9093f7abf819f86050cc"
],
"version": "==1.2"
},
"titlecase": {
"hashes": [
"sha256:84de7a97fb702c400e5ba11c6b30849944b39db12e20fbf4515a23c7538a0611",
"sha256:95d643a0c08097c02933aced707adfe1c275c335019e8e514dea782a465c5b84"
],
"index": "pypi",
"version": "==0.12.0"
}
},
"develop": {}

View file

@ -1,8 +1,7 @@
from django import forms
from django.forms import ModelForm
from django.contrib.auth.models import Group
from django.forms import ModelForm
from todo.models import Task, TaskList
from django.contrib.auth import get_user_model
class AddTaskListForm(ModelForm):
@ -18,16 +17,18 @@ class AddTaskListForm(ModelForm):
class Meta:
model = TaskList
exclude = []
exclude = ['created_date', ]
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."""
must find other members of the group this TaskList is attached to."""
def __init__(self, user, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['assigned_to'].queryset = get_user_model().objects.filter(groups__in=user.groups.all()).distinct()
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'}

View file

@ -1,7 +1,39 @@
import factory
from faker import Faker
from titlecase import titlecase
import random
from django.core.management.base import BaseCommand
from django.contrib.auth.models import Group
from todo.models import Task, TaskList
from django.contrib.auth import get_user_model
from django.utils.text import slugify
from todo.models import Task, TaskList
num_lists = 5
num_tasks_per_list = 10
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('.')
if tc:
thestr = titlecase(thestr)
return thestr
def gen_content():
# faker provides paragraphs as a list; convert with linebreaks
fake = Faker()
grafs = fake.paragraphs()
thestr = ''
for g in grafs:
thestr += "{}\n\n".format(g)
return thestr
class Command(BaseCommand):
@ -9,25 +41,99 @@ class Command(BaseCommand):
def handle(self, *args, **options):
# Wipe out previous contents. Cascade deletes the Tasks from the TaskLists.
TaskList.objects.all().delete()
print("Content from previous run deleted.")
print("Working...")
fake = Faker() # Use to create user's names
# Create users and groups, add different users to different groups. Staff user is in both groups.
bw_group = Group.objects.create(name='Basket Weavers')
sd_group = Group.objects.create(name='Scuba Divers')
usernames = ['user1', 'user2', 'staff_user']
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']
for username in usernames:
get_user_model().objects.create_user(username=username, password="todo")
if get_user_model().objects.filter(username=username).exists():
user = get_user_model().objects.get(username=username)
else:
user = get_user_model().objects.create_user(
username=username,
first_name=fake.first_name(),
last_name=fake.last_name(),
email=fake.email(),
password="todo")
if username == 'user1':
u1 = get_user_model().objects.get(username=username)
u1.groups.add(bw_group)
if username in ['user1', 'user2']:
user.groups.add(bw_group)
if username == 'user2':
u2 = get_user_model().objects.get(username=username)
u2.groups.add(sd_group)
if username in ['user3', 'user4']:
user.groups.add(sd_group)
if username == 'staff_user':
staffer = get_user_model().objects.get(username=username, is_staff=True,)
staffer.groups.add(bw_group)
staffer.groups.add(sd_group)
if username == 'staffer':
user.is_staff = True
user.first_name = fake.first_name()
user.last_name = fake.last_name()
user.save()
user.groups.add(bw_group)
user.groups.add(sd_group)
# Create lists with tasks
TaskListFactory.create_batch(5, group=bw_group)
TaskListFactory.create_batch(5, group=sd_group)
print("For each of two groups, created {} fake tasks in each of {} fake lists.".format(
num_lists, num_tasks_per_list)
)
class TaskListFactory(factory.django.DjangoModelFactory):
"""Group not generated here - call with group as arg."""
class Meta:
model = TaskList
name = factory.LazyAttribute(lambda o: gen_title(tc=True))
slug = factory.LazyAttribute(lambda o: slugify(o.name))
group = None # Pass this in
@factory.post_generation
def add_tasks(self, build, extracted, **kwargs):
TaskFactory.create_batch(num_tasks_per_list, task_list=self)
class TaskFactory(factory.django.DjangoModelFactory):
"""TaskList not generated here - call with TaskList as arg."""
class Meta:
model = Task
title = factory.LazyAttribute(lambda o: gen_title(tc=False))
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 = 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):
fake = Faker() # Use to create user's names
taskgroup = self.task_list.group
self.created_by = taskgroup.user_set.all().order_by('?').first()
if self.completed:
self.completed_date = fake.date_this_year()
# 1/3 of generated tasks have a due_date
if random.randint(1, 3) == 1:
self.due_date = fake.date_this_year()
# 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.save()

View file

@ -0,0 +1,19 @@
# Generated by Django 2.0.4 on 2018-04-05 00:24
from django.db import migrations, models
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('todo', '0006_rename_item_model'),
]
operations = [
migrations.AlterField(
model_name='task',
name='created_date',
field=models.DateField(blank=True, default=django.utils.timezone.now, null=True),
),
]

View file

@ -5,6 +5,7 @@ from django.conf import settings
from django.contrib.auth.models import Group
from django.db import models
from django.urls import reverse
from django.utils import timezone
class TaskList(models.Model):
@ -26,7 +27,7 @@ class TaskList(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)
created_date = models.DateField(default=timezone.now, 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)

View file

@ -3,6 +3,7 @@
<form action="" name="add_task" method="post">
{% csrf_token %}
<div id="AddEditTask" class="collapse mt-3">
<div class="form-group">
<label for="id_title" name="title">Task</label>

View file

@ -34,9 +34,17 @@
<li class="list-group-item">
<strong>Due date:</strong> {{ task.due_date }}
</li>
{% if task.completed %}
<li class="list-group-item">
<strong>Completed on:</strong> {{ task.completed_date}}
</li>
{% else %}
<li class="list-group-item">
<strong>Completed:</strong> {{ task.completed|yesno:"Yes,No" }}
</li>
{% endif %}
<li class="list-group-item">
<strong>In list:</strong>
<a href="{% url 'todo:list_detail' task.task_list.id task.task_list.slug %}">

View file

@ -13,6 +13,7 @@ from django.http import HttpResponse
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 django.utils import timezone
from todo.forms import AddTaskListForm, AddEditTaskForm, AddExternalTaskForm, SearchForm
from todo.models import Task, TaskList, Comment
@ -157,10 +158,12 @@ def list_detail(request, list_id=None, list_slug=None, view_completed=False):
})
if form.is_valid():
new_task = form.save()
new_task = form.save(commit=False)
new_task.created_date = timezone.now()
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 != 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))
@ -171,7 +174,7 @@ def list_detail(request, list_id=None, list_slug=None, view_completed=False):
form = AddEditTaskForm(request.user, initial={
'assigned_to': request.user.id,
'priority': 999,
'task_list': task_list
'task_list': task_list,
})
context = {