mirror of https://github.com/snachodog/mybuddy.git
Refactor Averages card as Statistics carousel card.
This commit is contained in:
parent
3ae40027f4
commit
770e7a69eb
|
@ -17,22 +17,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-averages {
|
|
||||||
@extend .border-light;
|
|
||||||
|
|
||||||
.card-header {
|
|
||||||
@extend .text-dark, .bg-light;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-body {
|
|
||||||
@extend .text-light;
|
|
||||||
|
|
||||||
.container {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-diaperchange {
|
.card-diaperchange {
|
||||||
@extend .border-danger;
|
@extend .border-danger;
|
||||||
|
|
||||||
|
@ -69,6 +53,22 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.card-statistics {
|
||||||
|
@extend .border-light;
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
@extend .text-dark, .bg-light;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-body {
|
||||||
|
@extend .text-light;
|
||||||
|
|
||||||
|
.container {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.card-timer {
|
.card-timer {
|
||||||
@extend .border-light;
|
@extend .border-light;
|
||||||
|
|
||||||
|
|
|
@ -1,62 +0,0 @@
|
||||||
{% load duration %}
|
|
||||||
|
|
||||||
<div class="card card-dashboard card-averages">
|
|
||||||
<div class="card-header">
|
|
||||||
<i class="icon icon-graph pull-left" aria-hidden="true"></i>
|
|
||||||
Averages
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="container">
|
|
||||||
<div class="row no-gutters text-center">
|
|
||||||
<div class="col-md-6 col-sm-3">
|
|
||||||
<div class="text-muted">Nap time</div>
|
|
||||||
{% if naps.average %}
|
|
||||||
{{ naps.average|duration_string:'m' }}
|
|
||||||
{% else %}
|
|
||||||
<em>Not enough data</em>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6 col-sm-3">
|
|
||||||
<div class="text-muted">Naps/day</div>
|
|
||||||
{% if naps.avg_per_day %}
|
|
||||||
{{ naps.avg_per_day|floatformat }}
|
|
||||||
{% else %}
|
|
||||||
<em>Not enough data</em>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6 col-sm-3 col-xs-12">
|
|
||||||
<div class="text-muted">Sleep time</div>
|
|
||||||
{% if sleep.average %}
|
|
||||||
{{ sleep.average|duration_string:'m' }}
|
|
||||||
{% else %}
|
|
||||||
<em>Not enough data</em>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6 col-sm-3 col-xs-12">
|
|
||||||
<div class="text-muted">Awake time</div>
|
|
||||||
{% if sleep.btwn_average %}
|
|
||||||
{{ sleep.btwn_average|duration_string:'m' }}
|
|
||||||
{% else %}
|
|
||||||
<em>Not enough data</em>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6 col-sm-3 col-xs-12">
|
|
||||||
<div class="text-muted">Change freq.</div>
|
|
||||||
{% if changes.btwn_average %}
|
|
||||||
{{ changes.btwn_average|duration_string:'m' }}
|
|
||||||
{% else %}
|
|
||||||
<em>Not enough data</em>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6 col-sm-3 col-xs-12">
|
|
||||||
<div class="text-muted">Feeding freq.</div>
|
|
||||||
{% if feedings.btwn_average %}
|
|
||||||
{{ feedings.btwn_average|duration_string:'m' }}
|
|
||||||
{% else %}
|
|
||||||
<em>Not enough data</em>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
{% load duration %}
|
||||||
|
|
||||||
|
<div class="card card-dashboard card-statistics">
|
||||||
|
<div class="card-header">
|
||||||
|
<i class="icon icon-graph pull-left" aria-hidden="true"></i>
|
||||||
|
Statistics
|
||||||
|
</div>
|
||||||
|
<div class="card-body text-center">
|
||||||
|
<div id="statistics-carousel" class="carousel slide" data-interval="false">
|
||||||
|
<div class="carousel-inner">
|
||||||
|
{% for stat in stats %}
|
||||||
|
<div class="carousel-item{% if forloop.counter == 1 %} active{% endif %}">
|
||||||
|
<span class="card-title">
|
||||||
|
{% if stat.stat %}
|
||||||
|
{% if stat.type == 'duration' %}
|
||||||
|
{{ stat.stat|duration_string:'m' }}
|
||||||
|
{% elif stat.type == 'float' %}
|
||||||
|
{{ stat.stat|floatformat }}
|
||||||
|
{% else %}
|
||||||
|
{{ stat.stat }}
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
<em>Not enough data</em>
|
||||||
|
{% endif %}
|
||||||
|
</span>
|
||||||
|
<div class="card-text">{{ stat.title }}</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
<a class="carousel-control-prev" href="#statistics-carousel" role="button" data-slide="prev">
|
||||||
|
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
|
||||||
|
<span class="sr-only">Previous</span>
|
||||||
|
</a>
|
||||||
|
<a class="carousel-control-next" href="#statistics-carousel" role="button" data-slide="next">
|
||||||
|
<span class="carousel-control-next-icon" aria-hidden="true"></span>
|
||||||
|
<span class="sr-only">Next</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -15,7 +15,7 @@
|
||||||
{% card_feeding_last object %}
|
{% card_feeding_last object %}
|
||||||
{% card_feeding_last_method object %}
|
{% card_feeding_last_method object %}
|
||||||
{% card_timer_list %}
|
{% card_timer_list %}
|
||||||
{% card_averages object %}
|
{% card_statistics object %}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-4 col-md-6 col-sm-12">
|
<div class="col-lg-4 col-md-6 col-sm-12">
|
||||||
{% card_sleep_last object %}
|
{% card_sleep_last object %}
|
||||||
|
|
|
@ -12,121 +12,6 @@ from core.models import DiaperChange, Feeding, Sleep, Timer, TummyTime
|
||||||
register = template.Library()
|
register = template.Library()
|
||||||
|
|
||||||
|
|
||||||
@register.inclusion_tag('cards/averages.html')
|
|
||||||
def card_averages(child):
|
|
||||||
"""
|
|
||||||
Averages data for all models.
|
|
||||||
:param child: an instance of the Child model.
|
|
||||||
:returns: a dictionary of Diaper Change, Feeding and Sleep averages data.
|
|
||||||
"""
|
|
||||||
return {
|
|
||||||
'changes': _diaperchange_averages(child),
|
|
||||||
'feedings': _feeding_averages(child),
|
|
||||||
'naps': _nap_averages(child),
|
|
||||||
'sleep': _sleep_averages(child)}
|
|
||||||
|
|
||||||
|
|
||||||
def _diaperchange_averages(child):
|
|
||||||
"""
|
|
||||||
Averaged Diaper Change data.
|
|
||||||
:param child: an instance of the Child model.
|
|
||||||
:returns: a dictionary of Diaper Change averages data.
|
|
||||||
"""
|
|
||||||
instances = DiaperChange.objects.filter(child=child).order_by('time')
|
|
||||||
changes = {
|
|
||||||
'btwn_total': timezone.timedelta(0),
|
|
||||||
'btwn_count': instances.count() - 1,
|
|
||||||
'btwn_average': 0}
|
|
||||||
last_instance = None
|
|
||||||
|
|
||||||
for instance in instances:
|
|
||||||
if last_instance:
|
|
||||||
changes['btwn_total'] += instance.time - last_instance.time
|
|
||||||
last_instance = instance
|
|
||||||
|
|
||||||
if changes['btwn_count'] > 0:
|
|
||||||
changes['btwn_average'] = changes['btwn_total'] / changes['btwn_count']
|
|
||||||
|
|
||||||
return changes
|
|
||||||
|
|
||||||
|
|
||||||
def _feeding_averages(child):
|
|
||||||
"""
|
|
||||||
Averaged Feeding data.
|
|
||||||
:param child: an instance of the Child model.
|
|
||||||
:returns: a dictionary of Feeding averages data.
|
|
||||||
"""
|
|
||||||
instances = Feeding.objects.filter(child=child).order_by('start')
|
|
||||||
feedings = {
|
|
||||||
'btwn_total': timezone.timedelta(0),
|
|
||||||
'btwn_count': instances.count() - 1,
|
|
||||||
'btwn_average': 0}
|
|
||||||
last_instance = None
|
|
||||||
|
|
||||||
for instance in instances:
|
|
||||||
if last_instance:
|
|
||||||
feedings['btwn_total'] += instance.start - last_instance.end
|
|
||||||
last_instance = instance
|
|
||||||
|
|
||||||
if feedings['btwn_count'] > 0:
|
|
||||||
feedings['btwn_average'] = \
|
|
||||||
feedings['btwn_total'] / feedings['btwn_count']
|
|
||||||
|
|
||||||
return feedings
|
|
||||||
|
|
||||||
|
|
||||||
def _nap_averages(child):
|
|
||||||
"""
|
|
||||||
Averaged nap data.
|
|
||||||
:param child: an instance of the Child model.
|
|
||||||
:returns: a dictionary of nap (Sleep) averages data.
|
|
||||||
"""
|
|
||||||
instances = Sleep.naps.filter(child=child).order_by('start')
|
|
||||||
naps = {
|
|
||||||
'total': instances.aggregate(Sum('duration'))['duration__sum'],
|
|
||||||
'count': instances.count(),
|
|
||||||
'average': 0,
|
|
||||||
'avg_per_day': 0}
|
|
||||||
if naps['count'] > 0:
|
|
||||||
naps['average'] = naps['total'] / naps['count']
|
|
||||||
|
|
||||||
naps_avg = instances.annotate(date=TruncDate('start')).values('date') \
|
|
||||||
.annotate(naps_count=Count('id')).order_by() \
|
|
||||||
.aggregate(Avg('naps_count'))
|
|
||||||
naps['avg_per_day'] = naps_avg['naps_count__avg']
|
|
||||||
|
|
||||||
return naps
|
|
||||||
|
|
||||||
|
|
||||||
def _sleep_averages(child):
|
|
||||||
"""
|
|
||||||
Averaged Sleep data.
|
|
||||||
:param child: an instance of the Child model.
|
|
||||||
:returns: a dictionary of Sleep averages data.
|
|
||||||
"""
|
|
||||||
instances = Sleep.objects.filter(child=child).order_by('start')
|
|
||||||
sleep = {
|
|
||||||
'total': instances.aggregate(Sum('duration'))['duration__sum'],
|
|
||||||
'count': instances.count(),
|
|
||||||
'average': 0,
|
|
||||||
'btwn_total': timezone.timedelta(0),
|
|
||||||
'btwn_count': instances.count() - 1,
|
|
||||||
'btwn_average': 0}
|
|
||||||
|
|
||||||
last_instance = None
|
|
||||||
for instance in instances:
|
|
||||||
if last_instance:
|
|
||||||
sleep['btwn_total'] += instance.start - last_instance.end
|
|
||||||
last_instance = instance
|
|
||||||
|
|
||||||
if sleep['count'] > 0:
|
|
||||||
sleep['average'] = sleep['total'] / sleep['count']
|
|
||||||
if sleep['btwn_count'] > 0:
|
|
||||||
sleep['btwn_average'] = sleep['btwn_total'] / sleep['btwn_count']
|
|
||||||
|
|
||||||
return sleep
|
|
||||||
|
|
||||||
|
|
||||||
@register.inclusion_tag('cards/diaperchange_last.html')
|
@register.inclusion_tag('cards/diaperchange_last.html')
|
||||||
def card_diaperchange_last(child):
|
def card_diaperchange_last(child):
|
||||||
"""
|
"""
|
||||||
|
@ -261,6 +146,151 @@ def card_sleep_naps_day(child, date=None):
|
||||||
'count': len(instances)}
|
'count': len(instances)}
|
||||||
|
|
||||||
|
|
||||||
|
@register.inclusion_tag('cards/statistics.html')
|
||||||
|
def card_statistics(child):
|
||||||
|
"""
|
||||||
|
Statistics data for all models.
|
||||||
|
:param child: an instance of the Child model.
|
||||||
|
:returns: a list of dictionaries with "type", "stat" and "title" entries.
|
||||||
|
"""
|
||||||
|
stats = []
|
||||||
|
|
||||||
|
changes = _diaperchange_averages(child)
|
||||||
|
stats.append({
|
||||||
|
'type': 'duration',
|
||||||
|
'stat': changes['btwn_average'],
|
||||||
|
'title': 'Diaper change frequency'})
|
||||||
|
|
||||||
|
feedings = _feeding_averages(child)
|
||||||
|
stats.append({
|
||||||
|
'type': 'duration',
|
||||||
|
'stat': feedings['btwn_average'],
|
||||||
|
'title': 'Feeding frequency'})
|
||||||
|
|
||||||
|
naps = _nap_averages(child)
|
||||||
|
stats.append({
|
||||||
|
'type': 'duration',
|
||||||
|
'stat': naps['average'],
|
||||||
|
'title': 'Average nap duration'})
|
||||||
|
stats.append({
|
||||||
|
'type': 'float',
|
||||||
|
'stat': naps['avg_per_day'],
|
||||||
|
'title': 'Average naps per day'})
|
||||||
|
|
||||||
|
sleep = _sleep_averages(child)
|
||||||
|
stats.append({
|
||||||
|
'type': 'duration',
|
||||||
|
'stat': sleep['average'],
|
||||||
|
'title': 'Average sleep duration'})
|
||||||
|
stats.append({
|
||||||
|
'type': 'duration',
|
||||||
|
'stat': sleep['btwn_average'],
|
||||||
|
'title': 'Average awake duration'})
|
||||||
|
|
||||||
|
return {'stats': stats}
|
||||||
|
|
||||||
|
|
||||||
|
def _diaperchange_averages(child):
|
||||||
|
"""
|
||||||
|
Averaged Diaper Change data.
|
||||||
|
:param child: an instance of the Child model.
|
||||||
|
:returns: a dictionary of Diaper Change averages data.
|
||||||
|
"""
|
||||||
|
instances = DiaperChange.objects.filter(child=child).order_by('time')
|
||||||
|
changes = {
|
||||||
|
'btwn_total': timezone.timedelta(0),
|
||||||
|
'btwn_count': instances.count() - 1,
|
||||||
|
'btwn_average': 0}
|
||||||
|
last_instance = None
|
||||||
|
|
||||||
|
for instance in instances:
|
||||||
|
if last_instance:
|
||||||
|
changes['btwn_total'] += instance.time - last_instance.time
|
||||||
|
last_instance = instance
|
||||||
|
|
||||||
|
if changes['btwn_count'] > 0:
|
||||||
|
changes['btwn_average'] = changes['btwn_total'] / changes['btwn_count']
|
||||||
|
|
||||||
|
return changes
|
||||||
|
|
||||||
|
|
||||||
|
def _feeding_averages(child):
|
||||||
|
"""
|
||||||
|
Averaged Feeding data.
|
||||||
|
:param child: an instance of the Child model.
|
||||||
|
:returns: a dictionary of Feeding averages data.
|
||||||
|
"""
|
||||||
|
instances = Feeding.objects.filter(child=child).order_by('start')
|
||||||
|
feedings = {
|
||||||
|
'btwn_total': timezone.timedelta(0),
|
||||||
|
'btwn_count': instances.count() - 1,
|
||||||
|
'btwn_average': 0}
|
||||||
|
last_instance = None
|
||||||
|
|
||||||
|
for instance in instances:
|
||||||
|
if last_instance:
|
||||||
|
feedings['btwn_total'] += instance.start - last_instance.end
|
||||||
|
last_instance = instance
|
||||||
|
|
||||||
|
if feedings['btwn_count'] > 0:
|
||||||
|
feedings['btwn_average'] = \
|
||||||
|
feedings['btwn_total'] / feedings['btwn_count']
|
||||||
|
|
||||||
|
return feedings
|
||||||
|
|
||||||
|
|
||||||
|
def _nap_averages(child):
|
||||||
|
"""
|
||||||
|
Averaged nap data.
|
||||||
|
:param child: an instance of the Child model.
|
||||||
|
:returns: a dictionary of nap (Sleep) averages data.
|
||||||
|
"""
|
||||||
|
instances = Sleep.naps.filter(child=child).order_by('start')
|
||||||
|
naps = {
|
||||||
|
'total': instances.aggregate(Sum('duration'))['duration__sum'],
|
||||||
|
'count': instances.count(),
|
||||||
|
'average': 0,
|
||||||
|
'avg_per_day': 0}
|
||||||
|
if naps['count'] > 0:
|
||||||
|
naps['average'] = naps['total'] / naps['count']
|
||||||
|
|
||||||
|
naps_avg = instances.annotate(date=TruncDate('start')).values('date') \
|
||||||
|
.annotate(naps_count=Count('id')).order_by() \
|
||||||
|
.aggregate(Avg('naps_count'))
|
||||||
|
naps['avg_per_day'] = naps_avg['naps_count__avg']
|
||||||
|
|
||||||
|
return naps
|
||||||
|
|
||||||
|
|
||||||
|
def _sleep_averages(child):
|
||||||
|
"""
|
||||||
|
Averaged Sleep data.
|
||||||
|
:param child: an instance of the Child model.
|
||||||
|
:returns: a dictionary of Sleep averages data.
|
||||||
|
"""
|
||||||
|
instances = Sleep.objects.filter(child=child).order_by('start')
|
||||||
|
sleep = {
|
||||||
|
'total': instances.aggregate(Sum('duration'))['duration__sum'],
|
||||||
|
'count': instances.count(),
|
||||||
|
'average': 0,
|
||||||
|
'btwn_total': timezone.timedelta(0),
|
||||||
|
'btwn_count': instances.count() - 1,
|
||||||
|
'btwn_average': 0}
|
||||||
|
|
||||||
|
last_instance = None
|
||||||
|
for instance in instances:
|
||||||
|
if last_instance:
|
||||||
|
sleep['btwn_total'] += instance.start - last_instance.end
|
||||||
|
last_instance = instance
|
||||||
|
|
||||||
|
if sleep['count'] > 0:
|
||||||
|
sleep['average'] = sleep['total'] / sleep['count']
|
||||||
|
if sleep['btwn_count'] > 0:
|
||||||
|
sleep['btwn_average'] = sleep['btwn_total'] / sleep['btwn_count']
|
||||||
|
|
||||||
|
return sleep
|
||||||
|
|
||||||
|
|
||||||
@register.inclusion_tag('cards/timer_list.html')
|
@register.inclusion_tag('cards/timer_list.html')
|
||||||
def card_timer_list():
|
def card_timer_list():
|
||||||
"""
|
"""
|
||||||
|
|
Loading…
Reference in New Issue