mirror of https://github.com/snachodog/mybuddy.git
Refactor "fake" command.
This commit simplifies the "fake" code code and makes the data much more realistic by preventing activity intersection. It also uses atomic transactions to make fake data generation _much_ faster on slow computers (like my old laptop!).
This commit is contained in:
parent
d2227e7747
commit
5dec231147
|
@ -5,6 +5,7 @@ from random import choice, randint, uniform
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
|
from django.db import transaction
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
|
@ -19,6 +20,10 @@ class Command(BaseCommand):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(Command, self).__init__(*args, **kwargs)
|
super(Command, self).__init__(*args, **kwargs)
|
||||||
self.faker = Factory.create()
|
self.faker = Factory.create()
|
||||||
|
self.child = None
|
||||||
|
self.weight = None
|
||||||
|
self.time = None
|
||||||
|
self.time_now = timezone.localtime()
|
||||||
|
|
||||||
def add_arguments(self, parser):
|
def add_arguments(self, parser):
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
|
@ -39,132 +44,158 @@ class Command(BaseCommand):
|
||||||
children = int(kwargs['children']) or 1
|
children = int(kwargs['children']) or 1
|
||||||
days = int(kwargs['days']) or 31
|
days = int(kwargs['days']) or 31
|
||||||
|
|
||||||
# User first day of data that will created for birth date.
|
|
||||||
birth_date = (timezone.localtime() - timedelta(days=days))
|
birth_date = (timezone.localtime() - timedelta(days=days))
|
||||||
for i in range(0, children):
|
for i in range(0, children):
|
||||||
child = models.Child.objects.create(
|
self.child = models.Child.objects.create(
|
||||||
first_name=self.faker.first_name(),
|
first_name=self.faker.first_name(),
|
||||||
last_name=self.faker.last_name(),
|
last_name=self.faker.last_name(),
|
||||||
birth_date=birth_date
|
birth_date=birth_date
|
||||||
)
|
)
|
||||||
child.save()
|
self.child.save()
|
||||||
|
self._add_child_data()
|
||||||
for j in range(days - 1, -1, -1):
|
|
||||||
date = (timezone.localtime() - timedelta(days=j)).replace(
|
|
||||||
hour=0, minute=0, second=0)
|
|
||||||
self._add_child_data(child, date)
|
|
||||||
|
|
||||||
if verbosity > 0:
|
if verbosity > 0:
|
||||||
self.stdout.write(
|
self.stdout.write(
|
||||||
self.style.SUCCESS('Successfully added fake data.')
|
self.style.SUCCESS('Successfully added fake data.')
|
||||||
)
|
)
|
||||||
|
|
||||||
def _add_child_data(self, child, date):
|
@transaction.atomic
|
||||||
now = timezone.localtime()
|
def _add_child_data(self):
|
||||||
|
"""
|
||||||
|
Adds fake data for child from child.birth_date to now. The fake data
|
||||||
|
follows a semi-regular pattern of sleep, feed, change, tummy time,
|
||||||
|
change, tummy time, sleep, etc.
|
||||||
|
:returns:
|
||||||
|
"""
|
||||||
|
self.time = self.child.birth_date
|
||||||
|
last_weight_entry_time = self.time
|
||||||
|
self.weight = uniform(2.0, 5.0)
|
||||||
|
self._add_weight_entry()
|
||||||
|
self._add_note_entry()
|
||||||
|
while self.time < self.time_now:
|
||||||
|
self._add_sleep_entry()
|
||||||
|
if choice([True, False]):
|
||||||
|
self._add_diaperchange_entry()
|
||||||
|
self._add_feeding_entry()
|
||||||
|
self._add_diaperchange_entry()
|
||||||
|
if choice([True, False]):
|
||||||
|
self._add_tummytime_entry()
|
||||||
|
if choice([True, False]):
|
||||||
|
self._add_diaperchange_entry()
|
||||||
|
self._add_tummytime_entry()
|
||||||
|
if (self.time - last_weight_entry_time).days > 6:
|
||||||
|
self._add_weight_entry()
|
||||||
|
last_weight_entry_time = self.time
|
||||||
|
|
||||||
for i in (range(0, randint(5, 20))):
|
@transaction.atomic
|
||||||
solid = choice([True, False])
|
def _add_diaperchange_entry(self):
|
||||||
if solid:
|
"""
|
||||||
wet = False
|
Add a Diaper Change entry and advance self.time.
|
||||||
color = choice(
|
:returns:
|
||||||
models.DiaperChange._meta.get_field('color').choices)[0]
|
"""
|
||||||
else:
|
solid = choice([True, False, False, False])
|
||||||
wet = True
|
wet = choice([True, False])
|
||||||
color = ''
|
color = ''
|
||||||
|
if not wet and not solid:
|
||||||
|
wet = True
|
||||||
|
time = self.time + timedelta(minutes=randint(1, 60))
|
||||||
|
|
||||||
time = date + timedelta(minutes=randint(0, 60 * 24))
|
if time < self.time_now:
|
||||||
|
models.DiaperChange.objects.create(
|
||||||
|
child=self.child,
|
||||||
|
time=time,
|
||||||
|
wet=wet,
|
||||||
|
solid=solid,
|
||||||
|
color=color
|
||||||
|
).save()
|
||||||
|
self.time = time
|
||||||
|
|
||||||
if time < now:
|
@transaction.atomic
|
||||||
models.DiaperChange.objects.create(
|
def _add_feeding_entry(self):
|
||||||
child=child,
|
"""
|
||||||
time=time,
|
Add a Feeding entry and advance self.time.
|
||||||
wet=wet,
|
:returns:
|
||||||
solid=solid,
|
"""
|
||||||
color=color
|
method = choice(models.Feeding._meta.get_field('method').choices)[0]
|
||||||
).save()
|
amount = None
|
||||||
|
if method is 'bottle':
|
||||||
last_end = date
|
amount = Decimal('%d.%d' % (randint(0, 6), randint(0, 9)))
|
||||||
while last_end < date + timedelta(days=1):
|
start = self.time + timedelta(minutes=randint(1, 60))
|
||||||
method = choice(models.Feeding._meta.get_field(
|
end = start + timedelta(minutes=randint(5, 20))
|
||||||
'method').choices)[0]
|
|
||||||
if method is 'bottle':
|
|
||||||
amount = Decimal('%d.%d' % (randint(0, 6), randint(0, 9)))
|
|
||||||
else:
|
|
||||||
amount = None
|
|
||||||
|
|
||||||
start = last_end + timedelta(minutes=randint(0, 60 * 2))
|
|
||||||
end = start + timedelta(minutes=randint(5, 20))
|
|
||||||
if end > now:
|
|
||||||
break
|
|
||||||
|
|
||||||
|
if end < self.time_now:
|
||||||
models.Feeding.objects.create(
|
models.Feeding.objects.create(
|
||||||
child=child,
|
child=self.child,
|
||||||
start=start,
|
start=start,
|
||||||
end=end,
|
end=end,
|
||||||
type=choice(models.Feeding._meta.get_field('type').choices)[0],
|
type=choice(models.Feeding._meta.get_field('type').choices)[0],
|
||||||
method=method,
|
method=method,
|
||||||
amount=amount
|
amount=amount
|
||||||
).save()
|
).save()
|
||||||
last_end = end
|
self.time = end
|
||||||
|
|
||||||
last_end = date
|
@transaction.atomic
|
||||||
|
def _add_note_entry(self):
|
||||||
|
"""
|
||||||
|
Add a Note entry.
|
||||||
|
:returns:
|
||||||
|
"""
|
||||||
|
note = self.faker.sentence()
|
||||||
|
models.Note.objects.create(child=self.child, note=note).save()
|
||||||
|
|
||||||
# Adjust last_end if the last sleep entry crossed in to date.
|
@transaction.atomic
|
||||||
last_entry = models.Sleep.objects.filter(
|
def _add_sleep_entry(self):
|
||||||
child=child).order_by('end').last()
|
"""
|
||||||
if last_entry:
|
Add a Sleep entry and advance self.time. Between the hours of 18 and 6,
|
||||||
last_entry_end = timezone.localtime(last_entry.end)
|
extend the minimum and maximum sleep duration settings.
|
||||||
if last_entry_end > last_end:
|
:returns:
|
||||||
last_end = last_entry_end
|
"""
|
||||||
|
if self.time.hour < 6 or self.time.hour > 18:
|
||||||
while last_end < date + timedelta(days=1):
|
minutes = randint(60 * 2, 60 * 6)
|
||||||
start = last_end + timedelta(minutes=randint(0, 60 * 2))
|
else:
|
||||||
if start.date() != date.date():
|
minutes = randint(30, 60 * 2)
|
||||||
break
|
end = self.time + timedelta(minutes=minutes)
|
||||||
|
|
||||||
end = start + timedelta(minutes=randint(10, 60 * 3))
|
|
||||||
if end > now:
|
|
||||||
break
|
|
||||||
|
|
||||||
|
if end < self.time_now:
|
||||||
models.Sleep.objects.create(
|
models.Sleep.objects.create(
|
||||||
child=child, start=start, end=end).save()
|
child=self.child,
|
||||||
last_end = end
|
start=self.time,
|
||||||
|
end=end
|
||||||
|
).save()
|
||||||
|
self.time = end
|
||||||
|
|
||||||
last_end = date
|
@transaction.atomic
|
||||||
while last_end < date + timedelta(days=1):
|
def _add_tummytime_entry(self):
|
||||||
if choice([True, False]):
|
"""
|
||||||
milestone = self.faker.sentence()
|
Add a Tummy time entry and advance self.time.
|
||||||
else:
|
:returns:
|
||||||
milestone = ''
|
"""
|
||||||
|
milestone = ''
|
||||||
start = last_end + timedelta(minutes=randint(0, 60 * 5))
|
if choice([True, False]):
|
||||||
end = start + timedelta(minutes=randint(1, 10))
|
milestone = self.faker.sentence()
|
||||||
if end > now:
|
start = self.time + timedelta(minutes=randint(1, 60))
|
||||||
break
|
end = start + timedelta(minutes=randint(0, 10), seconds=randint(0, 59))
|
||||||
|
if (end - start).seconds < 20:
|
||||||
|
end = start + timedelta(minutes=1, seconds=30)
|
||||||
|
|
||||||
|
if end < self.time_now:
|
||||||
models.TummyTime.objects.create(
|
models.TummyTime.objects.create(
|
||||||
child=child,
|
child=self.child,
|
||||||
start=start,
|
start=start,
|
||||||
end=end,
|
end=end,
|
||||||
milestone=milestone
|
milestone=milestone
|
||||||
).save()
|
).save()
|
||||||
last_end = end
|
self.time = end
|
||||||
|
|
||||||
last_entry = models.Weight.objects.filter(child=child) \
|
@transaction.atomic
|
||||||
.order_by('date').last()
|
def _add_weight_entry(self):
|
||||||
if not last_entry:
|
"""
|
||||||
weight = uniform(2.0, 5.0)
|
Add a Weight entry. This assumes a weekly interval.
|
||||||
else:
|
:returns:
|
||||||
weight = last_entry.weight + uniform(0, 0.04)
|
"""
|
||||||
|
self.weight += uniform(0.1, 0.3)
|
||||||
models.Weight.objects.create(
|
models.Weight.objects.create(
|
||||||
child=child,
|
child=self.child,
|
||||||
weight=weight,
|
weight=self.weight,
|
||||||
date=date.date()
|
date=self.time.date()
|
||||||
).save()
|
|
||||||
|
|
||||||
note = self.faker.sentence()
|
|
||||||
models.Note.objects.create(
|
|
||||||
child=child,
|
|
||||||
note=note,
|
|
||||||
time=date + timedelta(minutes=randint(0, 60 * 24))
|
|
||||||
).save()
|
).save()
|
||||||
|
|
Loading…
Reference in New Issue