Populate the all-children timeline

This commit is contained in:
Christopher C. Wells 2021-08-29 13:50:22 -07:00 committed by Christopher Charbonneau Wells
parent 0990678325
commit 10af931279
5 changed files with 118 additions and 97 deletions

View File

@ -29,77 +29,7 @@
</div> </div>
<div class="row"> <div class="row">
<div class="col-lg-8 offset-lg-4 col-md-6 offset-md-6"> <div class="col-lg-8 offset-lg-4 col-md-6 offset-md-6">
<h3 class="text-center"> {% include 'timeline/_timeline.html' %}
{% if date_previous %}
<a class="btn btn-sm btn-default" href="?date={{ date_previous|date:"Y-m-d" }}" aria-label="{% trans "Previous" %}">
<i class="icon icon-chevron-left" aria-hidden="true"></i>
<span class="sr-only">{% trans "Previous" %}</span>
</a>
{% endif %}
{{ date|date }}
{% if date_next %}
<a class="btn btn-sm btn-default" href="?date={{ date_next|date:"Y-m-d" }}" aria-label="{% trans "Next" %}">
<i class="icon icon-chevron-right" aria-hidden="true"></i>
<span class="sr-only">{% trans "Next" %}</span>
</a>
{% endif %}
</h3>
{% if timeline_objects %}
<ul class="timeline m-auto">
{% for object in timeline_objects %}
<li{% cycle "" ' class="timeline-inverted"' %}>
<div class="timeline-badge {% if object.type == "start" %}bg-success{% elif object.type == "end" %}bg-danger{% else %}bg-info{% endif %}">
<i class="icon icon-{{ object.model_name }}"></i>
</div>
<div class="card text-right">
<div class="card-body">
{{ object.event }}
{% for detail in object.details %}
<div><small>{{ detail }}</small></div>
{% endfor %}
</div>
<div class="card-footer text-muted">
{% blocktrans trimmed with since=object.time|timesince time=object.time|time %}
{{ since }} ago ({{ time }})
{% endblocktrans %}
{% if object.duration %}
<div>
<small>Duration: {{ object.duration }}</small>
</div>
{% endif %}
{% if object.time_since_prev %}
<div>
<small>{{ object.time_since_prev }} since previous</small>
</div>
{% endif %}
{% if object.edit_link %}
<div>
<small><a href="{{ object.edit_link }}">Edit</a></small>
</div>
{% endif %}
</div>
</div>
</li>
{% endfor %}
</ul>
<h3 class="text-center">
{% if date_previous %}
<a class="btn btn-sm btn-default" href="?date={{ date_previous|date:"Y-m-d" }}" aria-label="{% trans "Previous" %}">
<i class="icon icon-chevron-left" aria-hidden="true"></i>
<span class="sr-only">{% trans "Previous" %}</span>
</a>
{% endif %}
{{ date|date }}
{% if date_next %}
<a class="btn btn-sm btn-default" href="?date={{ date_next|date:"Y-m-d" }}" aria-label="{% trans "Next" %}">
<i class="icon icon-chevron-right" aria-hidden="true"></i>
<span class="sr-only">{% trans "Next" %}</span>
</a>
{% endif %}
</h3>
{% else %}
<div class="text-center">No events</div>
{% endif %}
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -0,0 +1,73 @@
{% load i18n %}
<h3 class="text-center">
{% if date_previous %}
<a class="btn btn-sm btn-default" href="?date={{ date_previous|date:"Y-m-d" }}" aria-label="{% trans "Previous" %}">
<i class="icon icon-chevron-left" aria-hidden="true"></i>
<span class="sr-only">{% trans "Previous" %}</span>
</a>
{% endif %}
{{ date|date }}
{% if date_next %}
<a class="btn btn-sm btn-default" href="?date={{ date_next|date:"Y-m-d" }}" aria-label="{% trans "Next" %}">
<i class="icon icon-chevron-right" aria-hidden="true"></i>
<span class="sr-only">{% trans "Next" %}</span>
</a>
{% endif %}
</h3>
{% if timeline_objects %}
<ul class="timeline m-auto">
{% for object in timeline_objects %}
<li{% cycle "" ' class="timeline-inverted"' %}>
<div class="timeline-badge {% if object.type == "start" %}bg-success{% elif object.type == "end" %}bg-danger{% else %}bg-info{% endif %}">
<i class="icon icon-{{ object.model_name }}"></i>
</div>
<div class="card text-right">
<div class="card-body">
{{ object.event }}
{% for detail in object.details %}
<div><small>{{ detail }}</small></div>
{% endfor %}
</div>
<div class="card-footer text-muted">
{% blocktrans trimmed with since=object.time|timesince time=object.time|time %}
{{ since }} ago ({{ time }})
{% endblocktrans %}
{% if object.duration %}
<div>
<small>Duration: {{ object.duration }}</small>
</div>
{% endif %}
{% if object.time_since_prev %}
<div>
<small>{{ object.time_since_prev }} since previous</small>
</div>
{% endif %}
{% if object.edit_link %}
<div>
<small><a href="{{ object.edit_link }}">Edit</a></small>
</div>
{% endif %}
</div>
</div>
</li>
{% endfor %}
</ul>
<h3 class="text-center">
{% if date_previous %}
<a class="btn btn-sm btn-default" href="?date={{ date_previous|date:"Y-m-d" }}" aria-label="{% trans "Previous" %}">
<i class="icon icon-chevron-left" aria-hidden="true"></i>
<span class="sr-only">{% trans "Previous" %}</span>
</a>
{% endif %}
{{ date|date }}
{% if date_next %}
<a class="btn btn-sm btn-default" href="?date={{ date_next|date:"Y-m-d" }}" aria-label="{% trans "Next" %}">
<i class="icon icon-chevron-right" aria-hidden="true"></i>
<span class="sr-only">{% trans "Next" %}</span>
</a>
{% endif %}
</h3>
{% else %}
<div class="text-center">No events</div>
{% endif %}

View File

@ -8,5 +8,9 @@
{% endblock %} {% endblock %}
{% block content %} {% block content %}
{# TODO! #} <div class="row">
<div class="col">
{% include 'timeline/_timeline.html' %}
</div>
</div>
{% endblock %} {% endblock %}

View File

@ -7,30 +7,32 @@ from core.models import DiaperChange, Feeding, Sleep, TummyTime
from datetime import timedelta from datetime import timedelta
def get_objects(child, date): def get_objects(date, child=None):
""" """
Create a time-sorted dictionary of all events for a child. Create a time-sorted dictionary of all events for a child.
:param child: an instance of a Child.
:param date: a DateTime instance for the day to be summarized. :param date: a DateTime instance for the day to be summarized.
:param child: Child instance to filter results for (no filter if `None`).
:returns: a list of the day's events. :returns: a list of the day's events.
""" """
min_date = date min_date = date
max_date = date.replace(hour=23, minute=59, second=59) max_date = date.replace(hour=23, minute=59, second=59)
events = [] events = []
_add_diaper_changes(child, min_date, max_date, events) _add_diaper_changes(min_date, max_date, events, child)
_add_feedings(child, min_date, max_date, events) _add_feedings(min_date, max_date, events, child)
_add_sleeps(child, min_date, max_date, events) _add_sleeps(min_date, max_date, events, child)
_add_tummy_times(child, min_date, max_date, events) _add_tummy_times(min_date, max_date, events, child)
events.sort(key=lambda x: x['time'], reverse=True) events.sort(key=lambda x: x['time'], reverse=True)
return events return events
def _add_tummy_times(child, min_date, max_date, events): def _add_tummy_times(min_date, max_date, events, child=None):
instances = TummyTime.objects.filter(child=child).filter( instances = TummyTime.objects.filter(
start__range=(min_date, max_date)).order_by('-start') start__range=(min_date, max_date)).order_by('-start')
if child:
instances = instances.filter(child=child)
for instance in instances: for instance in instances:
details = [] details = []
if instance.milestone: if instance.milestone:
@ -59,9 +61,11 @@ def _add_tummy_times(child, min_date, max_date, events):
}) })
def _add_sleeps(child, min_date, max_date, events): def _add_sleeps(min_date, max_date, events, child=None):
instances = Sleep.objects.filter(child=child).filter( instances = Sleep.objects.filter(
start__range=(min_date, max_date)).order_by('-start') start__range=(min_date, max_date)).order_by('-start')
if child:
instances = instances.filter(child=child)
for instance in instances: for instance in instances:
details = [] details = []
if instance.notes: if instance.notes:
@ -90,12 +94,15 @@ def _add_sleeps(child, min_date, max_date, events):
}) })
def _add_feedings(child, min_date, max_date, events): def _add_feedings(min_date, max_date, events, child=None):
yesterday = min_date - timedelta(days=1) # So first feeding has a previous # Ensure first feeding has a previous.
yesterday = min_date - timedelta(days=1)
prev_start = None prev_start = None
instances = Feeding.objects.filter(child=child).filter( instances = Feeding.objects.filter(
start__range=(yesterday, max_date)).order_by('start') start__range=(yesterday, max_date)).order_by('start')
if child:
instances = instances.filter(child=child)
for instance in instances: for instance in instances:
details = [] details = []
if instance.notes: if instance.notes:
@ -136,9 +143,11 @@ def _add_feedings(child, min_date, max_date, events):
}) })
def _add_diaper_changes(child, min_date, max_date, events): def _add_diaper_changes(min_date, max_date, events, child):
instances = DiaperChange.objects.filter(child=child).filter( instances = DiaperChange.objects.filter(
time__range=(min_date, max_date)).order_by('-time') time__range=(min_date, max_date)).order_by('-time')
if child:
instances = instances.filter(child=child)
for instance in instances: for instance in instances:
contents = [] contents = []
if instance.wet: if instance.wet:
@ -153,7 +162,7 @@ def _add_diaper_changes(child, min_date, max_date, events):
events.append({ events.append({
'time': timezone.localtime(instance.time), 'time': timezone.localtime(instance.time),
'event': _('%(child)s had a diaper change.') % { 'event': _('%(child)s had a diaper change.') % {
'child': child.first_name 'child': instance.child.first_name
}, },
'details': details, 'details': details,
'edit_link': reverse('core:diaperchange-update', 'edit_link': reverse('core:diaperchange-update',

View File

@ -17,6 +17,17 @@ from babybuddy.views import BabyBuddyFilterView
from core import forms, models, timeline from core import forms, models, timeline
def _prepare_timeline_context_data(context, date, child=None):
date = timezone.datetime.strptime(date, '%Y-%m-%d')
date = timezone.localtime(timezone.make_aware(date))
context['timeline_objects'] = timeline.get_objects(date, child)
context['date'] = date
context['date_previous'] = date - timezone.timedelta(days=1)
if date.date() < timezone.localdate():
context['date_next'] = date + timezone.timedelta(days=1)
pass
class CoreAddView(PermissionRequired403Mixin, SuccessMessageMixin, CreateView): class CoreAddView(PermissionRequired403Mixin, SuccessMessageMixin, CreateView):
def get_success_message(self, cleaned_data): def get_success_message(self, cleaned_data):
cleaned_data['model'] = self.model._meta.verbose_name.title() cleaned_data['model'] = self.model._meta.verbose_name.title()
@ -92,13 +103,7 @@ class ChildDetail(PermissionRequired403Mixin, DetailView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(ChildDetail, self).get_context_data(**kwargs) context = super(ChildDetail, self).get_context_data(**kwargs)
date = self.request.GET.get('date', str(timezone.localdate())) date = self.request.GET.get('date', str(timezone.localdate()))
date = timezone.datetime.strptime(date, '%Y-%m-%d') _prepare_timeline_context_data(context, date, self.object)
date = timezone.localtime(timezone.make_aware(date))
context['timeline_objects'] = timeline.get_objects(self.object, date)
context['date'] = date
context['date_previous'] = date - timezone.timedelta(days=1)
if date.date() < timezone.localdate():
context['date_next'] = date + timezone.timedelta(days=1)
return context return context
@ -284,8 +289,8 @@ class Timeline(LoginRequiredMixin, TemplateView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(Timeline, self).get_context_data(**kwargs) context = super(Timeline, self).get_context_data(**kwargs)
# TODO: Get relevant data for a given day. date = self.request.GET.get('date', str(timezone.localdate()))
context['objects'] = models.Child.objects.all() _prepare_timeline_context_data(context, date)
return context return context