mirror of https://github.com/snachodog/mybuddy.git
Move timeline code to core and integrate with Child detail view.
This commit is contained in:
parent
1c5278cebf
commit
162f117cef
|
@ -3,3 +3,12 @@
|
|||
max-width: 150px;
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(md) {
|
||||
#view-child {
|
||||
.child-detail-column {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -118,7 +118,7 @@ $card-shadow: rgba(0, 0, 0, .175);
|
|||
}
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
@include media-breakpoint-down(md) {
|
||||
.timeline {
|
||||
&::before {
|
||||
left: 40px;
|
|
@ -9,13 +9,53 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="jumbotron text-center">
|
||||
<img src="{% static 'babybuddy/img/core/child-placeholder.png' %}" class="child-photo img-fluid rounded-circle" />
|
||||
<div class="child-name display-4">{{ object }}</div>
|
||||
<p class="lead">
|
||||
Born <span class="text-secondary">{{ object.birth_date }}</span><br/>
|
||||
Age <span class="text-secondary">{{ object.birth_date|timesince }}</span>
|
||||
</p>
|
||||
{% include 'dashboard/child_button_group.html' %}
|
||||
<div class="child-detail-column">
|
||||
<div class="row">
|
||||
<div class="col-lg-4 col-md-6 pb-3 text-center">
|
||||
<img src="{% static 'babybuddy/img/core/child-placeholder.png' %}" class="child-photo img-fluid rounded-circle" />
|
||||
<div class="child-name display-4">{{ object }}</div>
|
||||
<p class="lead">
|
||||
Born <span class="text-secondary">{{ object.birth_date }}</span><br/>
|
||||
Age <span class="text-secondary">{{ object.birth_date|timesince }}</span>
|
||||
</p>
|
||||
{% include 'dashboard/child_button_group.html' %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-8 offset-lg-4 col-md-6 offset-md-6">
|
||||
<h3 class="text-center">
|
||||
{% if date_previous %}
|
||||
<a class="btn btn-sm btn-default" href="?date={{ date_previous|date:"Y-m-d" }}" aria-label="Previous">
|
||||
<i class="icon icon-chevron-left" aria-hidden="true"></i>
|
||||
<span class="sr-only">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="Next">
|
||||
<i class="icon icon-chevron-right" aria-hidden="true"></i>
|
||||
<span class="sr-only">Next</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
</h3>
|
||||
<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 }}
|
||||
</div>
|
||||
<div class="card-footer text-muted">
|
||||
{{ object.time|timesince }} ago ({{ object.time|time }})
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -6,9 +6,9 @@ from django.utils import timezone
|
|||
from core.models import DiaperChange, Feeding, Sleep, TummyTime
|
||||
|
||||
|
||||
def timeline(child, date):
|
||||
def get_objects(child, date):
|
||||
"""
|
||||
Create a time-sorted dictionary for 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.
|
||||
:returns: a list of the day's events.
|
109
core/views.py
109
core/views.py
|
@ -3,19 +3,18 @@ from __future__ import unicode_literals
|
|||
|
||||
from django.contrib.auth.mixins import PermissionRequiredMixin
|
||||
from django.urls import reverse
|
||||
from django.utils import timezone
|
||||
from django.views.generic.base import RedirectView
|
||||
from django.views.generic.detail import DetailView
|
||||
from django.views.generic.edit import CreateView, UpdateView, DeleteView
|
||||
|
||||
from django_filters.views import FilterView
|
||||
|
||||
from .models import Child, DiaperChange, Feeding, Note, Sleep, Timer, TummyTime
|
||||
from .forms import (ChildForm, ChildDeleteForm, DiaperChangeForm, FeedingForm,
|
||||
SleepForm, TimerForm, TummyTimeForm)
|
||||
from core import forms, models, timeline
|
||||
|
||||
|
||||
class ChildList(PermissionRequiredMixin, FilterView):
|
||||
model = Child
|
||||
model = models.Child
|
||||
template_name = 'core/child_list.html'
|
||||
permission_required = ('core.view_child',)
|
||||
paginate_by = 10
|
||||
|
@ -23,34 +22,46 @@ class ChildList(PermissionRequiredMixin, FilterView):
|
|||
|
||||
|
||||
class ChildAdd(PermissionRequiredMixin, CreateView):
|
||||
model = Child
|
||||
model = models.Child
|
||||
permission_required = ('core.add_child',)
|
||||
form_class = ChildForm
|
||||
form_class = forms.ChildForm
|
||||
success_url = '/children'
|
||||
|
||||
|
||||
class ChildDetail(PermissionRequiredMixin, DetailView):
|
||||
model = Child
|
||||
model = models.Child
|
||||
permission_required = ('core.view_child',)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(ChildDetail, self).get_context_data(**kwargs)
|
||||
date = self.request.GET.get('date', str(timezone.localdate()))
|
||||
date = timezone.datetime.strptime(date, '%Y-%m-%d')
|
||||
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
|
||||
|
||||
|
||||
class ChildUpdate(PermissionRequiredMixin, UpdateView):
|
||||
model = Child
|
||||
model = models.Child
|
||||
permission_required = ('core.change_child',)
|
||||
form_class = ChildForm
|
||||
form_class = forms.ChildForm
|
||||
success_url = '/children'
|
||||
|
||||
|
||||
class ChildDelete(PermissionRequiredMixin, UpdateView):
|
||||
model = Child
|
||||
form_class = ChildDeleteForm
|
||||
model = models.Child
|
||||
form_class = forms.ChildDeleteForm
|
||||
template_name = 'core/child_confirm_delete.html'
|
||||
permission_required = ('core.delete_child',)
|
||||
success_url = '/children'
|
||||
|
||||
|
||||
class DiaperChangeList(PermissionRequiredMixin, FilterView):
|
||||
model = DiaperChange
|
||||
model = models.DiaperChange
|
||||
template_name = 'core/diaperchange_list.html'
|
||||
permission_required = ('core.view_diaperchange',)
|
||||
paginate_by = 10
|
||||
|
@ -58,27 +69,27 @@ class DiaperChangeList(PermissionRequiredMixin, FilterView):
|
|||
|
||||
|
||||
class DiaperChangeAdd(PermissionRequiredMixin, CreateView):
|
||||
model = DiaperChange
|
||||
model = models.DiaperChange
|
||||
permission_required = ('core.add_diaperchange',)
|
||||
form_class = DiaperChangeForm
|
||||
form_class = forms.DiaperChangeForm
|
||||
success_url = '/changes'
|
||||
|
||||
|
||||
class DiaperChangeUpdate(PermissionRequiredMixin, UpdateView):
|
||||
model = DiaperChange
|
||||
model = models.DiaperChange
|
||||
permission_required = ('core.change_diaperchange',)
|
||||
form_class = DiaperChangeForm
|
||||
form_class = forms.DiaperChangeForm
|
||||
success_url = '/changes'
|
||||
|
||||
|
||||
class DiaperChangeDelete(PermissionRequiredMixin, DeleteView):
|
||||
model = DiaperChange
|
||||
model = models.DiaperChange
|
||||
permission_required = ('core.delete_diaperchange',)
|
||||
success_url = '/changes'
|
||||
|
||||
|
||||
class FeedingList(PermissionRequiredMixin, FilterView):
|
||||
model = Feeding
|
||||
model = models.Feeding
|
||||
template_name = 'core/feeding_list.html'
|
||||
permission_required = ('core.view_feeding',)
|
||||
paginate_by = 10
|
||||
|
@ -86,9 +97,9 @@ class FeedingList(PermissionRequiredMixin, FilterView):
|
|||
|
||||
|
||||
class FeedingAdd(PermissionRequiredMixin, CreateView):
|
||||
model = Feeding
|
||||
model = models.Feeding
|
||||
permission_required = ('core.add_feeding',)
|
||||
form_class = FeedingForm
|
||||
form_class = forms.FeedingForm
|
||||
success_url = '/feedings'
|
||||
|
||||
def get_form_kwargs(self):
|
||||
|
@ -99,20 +110,20 @@ class FeedingAdd(PermissionRequiredMixin, CreateView):
|
|||
|
||||
|
||||
class FeedingUpdate(PermissionRequiredMixin, UpdateView):
|
||||
model = Feeding
|
||||
model = models.Feeding
|
||||
permission_required = ('core.change_feeding',)
|
||||
form_class = FeedingForm
|
||||
form_class = forms.FeedingForm
|
||||
success_url = '/feedings'
|
||||
|
||||
|
||||
class FeedingDelete(PermissionRequiredMixin, DeleteView):
|
||||
model = Feeding
|
||||
model = models.Feeding
|
||||
permission_required = ('core.delete_feeding',)
|
||||
success_url = '/feedings'
|
||||
|
||||
|
||||
class NoteList(PermissionRequiredMixin, FilterView):
|
||||
model = Note
|
||||
model = models.Note
|
||||
template_name = 'core/note_list.html'
|
||||
permission_required = ('core.view_note',)
|
||||
paginate_by = 10
|
||||
|
@ -120,27 +131,27 @@ class NoteList(PermissionRequiredMixin, FilterView):
|
|||
|
||||
|
||||
class NoteAdd(PermissionRequiredMixin, CreateView):
|
||||
model = Note
|
||||
model = models.Note
|
||||
permission_required = ('core.add_note',)
|
||||
fields = ['child', 'note']
|
||||
success_url = '/notes'
|
||||
|
||||
|
||||
class NoteUpdate(PermissionRequiredMixin, UpdateView):
|
||||
model = Note
|
||||
model = models.Note
|
||||
permission_required = ('core.change_note',)
|
||||
fields = ['child', 'note']
|
||||
success_url = '/notes'
|
||||
|
||||
|
||||
class NoteDelete(PermissionRequiredMixin, DeleteView):
|
||||
model = Note
|
||||
model = models.Note
|
||||
permission_required = ('core.delete_note',)
|
||||
success_url = '/notes'
|
||||
|
||||
|
||||
class SleepList(PermissionRequiredMixin, FilterView):
|
||||
model = Sleep
|
||||
model = models.Sleep
|
||||
template_name = 'core/sleep_list.html'
|
||||
permission_required = ('core.view_sleep',)
|
||||
paginate_by = 10
|
||||
|
@ -148,9 +159,9 @@ class SleepList(PermissionRequiredMixin, FilterView):
|
|||
|
||||
|
||||
class SleepAdd(PermissionRequiredMixin, CreateView):
|
||||
model = Sleep
|
||||
model = models.Sleep
|
||||
permission_required = ('core.add_sleep',)
|
||||
form_class = SleepForm
|
||||
form_class = forms.SleepForm
|
||||
success_url = '/sleep'
|
||||
|
||||
def get_form_kwargs(self):
|
||||
|
@ -161,20 +172,20 @@ class SleepAdd(PermissionRequiredMixin, CreateView):
|
|||
|
||||
|
||||
class SleepUpdate(PermissionRequiredMixin, UpdateView):
|
||||
model = Sleep
|
||||
model = models.Sleep
|
||||
permission_required = ('core.change_sleep',)
|
||||
form_class = SleepForm
|
||||
form_class = forms.SleepForm
|
||||
success_url = '/sleep'
|
||||
|
||||
|
||||
class SleepDelete(PermissionRequiredMixin, DeleteView):
|
||||
model = Sleep
|
||||
model = models.Sleep
|
||||
permission_required = ('core.delete_sleep',)
|
||||
success_url = '/sleep'
|
||||
|
||||
|
||||
class TimerList(PermissionRequiredMixin, FilterView):
|
||||
model = Timer
|
||||
model = models.Timer
|
||||
template_name = 'core/timer_list.html'
|
||||
permission_required = ('core.view_timer',)
|
||||
paginate_by = 10
|
||||
|
@ -182,14 +193,14 @@ class TimerList(PermissionRequiredMixin, FilterView):
|
|||
|
||||
|
||||
class TimerDetail(PermissionRequiredMixin, DetailView):
|
||||
model = Timer
|
||||
model = models.Timer
|
||||
permission_required = ('core.view_timer',)
|
||||
|
||||
|
||||
class TimerAdd(PermissionRequiredMixin, CreateView):
|
||||
model = Timer
|
||||
model = models.Timer
|
||||
permission_required = ('core.add_timer',)
|
||||
form_class = TimerForm
|
||||
form_class = forms.TimerForm
|
||||
success_url = '/timers'
|
||||
|
||||
def get_form_kwargs(self):
|
||||
|
@ -199,9 +210,9 @@ class TimerAdd(PermissionRequiredMixin, CreateView):
|
|||
|
||||
|
||||
class TimerUpdate(PermissionRequiredMixin, UpdateView):
|
||||
model = Timer
|
||||
model = models.Timer
|
||||
permission_required = ('core.change_timer',)
|
||||
form_class = TimerForm
|
||||
form_class = forms.TimerForm
|
||||
success_url = '/timers'
|
||||
|
||||
def get_form_kwargs(self):
|
||||
|
@ -218,7 +229,7 @@ class TimerAddQuick(PermissionRequiredMixin, RedirectView):
|
|||
permission_required = ('core.add_timer',)
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
instance = Timer.objects.create(user=request.user)
|
||||
instance = models.Timer.objects.create(user=request.user)
|
||||
instance.save()
|
||||
self.url = request.GET.get(
|
||||
'next', reverse('timer-detail', args={instance.id}))
|
||||
|
@ -229,7 +240,7 @@ class TimerRestart(PermissionRequiredMixin, RedirectView):
|
|||
permission_required = ('core.change_timer',)
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
instance = Timer.objects.get(id=kwargs['pk'])
|
||||
instance = models.Timer.objects.get(id=kwargs['pk'])
|
||||
instance.restart()
|
||||
return super(TimerRestart, self).get(request, *args, **kwargs)
|
||||
|
||||
|
@ -241,7 +252,7 @@ class TimerStop(PermissionRequiredMixin, RedirectView):
|
|||
permission_required = ('core.change_timer',)
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
instance = Timer.objects.get(id=kwargs['pk'])
|
||||
instance = models.Timer.objects.get(id=kwargs['pk'])
|
||||
instance.stop()
|
||||
return super(TimerStop, self).get(request, *args, **kwargs)
|
||||
|
||||
|
@ -250,13 +261,13 @@ class TimerStop(PermissionRequiredMixin, RedirectView):
|
|||
|
||||
|
||||
class TimerDelete(PermissionRequiredMixin, DeleteView):
|
||||
model = Timer
|
||||
model = models.Timer
|
||||
permission_required = ('core.delete_timer',)
|
||||
success_url = '/'
|
||||
|
||||
|
||||
class TummyTimeList(PermissionRequiredMixin, FilterView):
|
||||
model = TummyTime
|
||||
model = models.TummyTime
|
||||
template_name = 'core/tummytime_list.html'
|
||||
permission_required = ('core.view_tummytime',)
|
||||
paginate_by = 10
|
||||
|
@ -264,9 +275,9 @@ class TummyTimeList(PermissionRequiredMixin, FilterView):
|
|||
|
||||
|
||||
class TummyTimeAdd(PermissionRequiredMixin, CreateView):
|
||||
model = TummyTime
|
||||
model = models.TummyTime
|
||||
permission_required = ('core.add_tummytime',)
|
||||
form_class = TummyTimeForm
|
||||
form_class = forms.TummyTimeForm
|
||||
success_url = '/tummy-time'
|
||||
|
||||
def get_form_kwargs(self):
|
||||
|
@ -277,13 +288,13 @@ class TummyTimeAdd(PermissionRequiredMixin, CreateView):
|
|||
|
||||
|
||||
class TummyTimeUpdate(PermissionRequiredMixin, UpdateView):
|
||||
model = TummyTime
|
||||
model = models.TummyTime
|
||||
permission_required = ('core.change_tummytime',)
|
||||
form_class = TummyTimeForm
|
||||
form_class = forms.TummyTimeForm
|
||||
success_url = '/tummy-time'
|
||||
|
||||
|
||||
class TummyTimeDelete(PermissionRequiredMixin, DeleteView):
|
||||
model = TummyTime
|
||||
model = models.TummyTime
|
||||
permission_required = ('core.delete_tummytime',)
|
||||
success_url = '/tummy-time'
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
<a class="dropdown-item" href="{% url 'reports:report-diaperchange-lifetimes-child' object.slug %}">Diaper Lifetimes</a>
|
||||
<a class="dropdown-item" href="{% url 'reports:report-sleep-pattern-child' object.slug %}">Sleep Pattern</a>
|
||||
<a class="dropdown-item" href="{% url 'reports:report-sleep-totals-child' object.slug %}">Sleep Totals</a>
|
||||
<a class="dropdown-item" href="{% url 'reports:report-timeline-child' object.slug %}">Timeline</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -2,4 +2,3 @@ from .diaperchange_lifetimes import diaperchange_lifetimes # NOQA
|
|||
from .diaperchange_types import diaperchange_types # NOQA
|
||||
from .sleep_pattern import sleep_pattern # NOQA
|
||||
from .sleep_totals import sleep_totals # NOQA
|
||||
from .timeline import timeline # NOQA
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
{% extends 'reports/report_base.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}Timeline - {{ object }}{% endblock %}
|
||||
|
||||
{% block breadcrumbs %}
|
||||
{{ block.super }}
|
||||
<li class="breadcrumb-item active" aria-current="page">Timeline</li>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1 class="text-center">Timeline</h1>
|
||||
<h2 class="text-center text-muted">{{ object }}</h2>
|
||||
<h3 class="text-center">
|
||||
<a class="btn btn-sm btn-default" href="?date={{ date_previous|date:"Y-m-d" }}" aria-label="Previous">
|
||||
<i class="icon icon-chevron-left" aria-hidden="true"></i>
|
||||
<span class="sr-only">Previous</span>
|
||||
</a>
|
||||
{{ date|date }}
|
||||
<a class="btn btn-sm btn-default" href="?date={{ date_next|date:"Y-m-d" }}" aria-label="Next">
|
||||
<i class="icon icon-chevron-right" aria-hidden="true"></i>
|
||||
<span class="sr-only">Next</span>
|
||||
</a>
|
||||
</h3>
|
||||
<ul class="timeline m-auto">
|
||||
{% for object in 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 }}
|
||||
</div>
|
||||
<div class="card-footer text-muted">
|
||||
{{ object.time|timesince }} ago ({{ object.time|time }})
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endblock %}
|
|
@ -44,6 +44,3 @@ class ViewsTestCase(TestCase):
|
|||
self.assertEqual(page.status_code, 200)
|
||||
page = self.c.get('{}/sleep/totals/'.format(base_url))
|
||||
self.assertEqual(page.status_code, 200)
|
||||
|
||||
page = self.c.get('{}/timeline/'.format(base_url))
|
||||
self.assertEqual(page.status_code, 200)
|
||||
|
|
|
@ -19,8 +19,4 @@ urlpatterns = [
|
|||
url(r'^children/(?P<slug>[^/.]+)/reports/sleep/totals/$',
|
||||
views.SleepTotalsChildReport.as_view(),
|
||||
name='report-sleep-totals-child'),
|
||||
|
||||
url(r'^children/(?P<slug>[^/.]+)/reports/timeline/$',
|
||||
views.TimelineChildReport.as_view(),
|
||||
name='report-timeline-child'),
|
||||
]
|
||||
|
|
|
@ -3,12 +3,11 @@ from __future__ import unicode_literals
|
|||
|
||||
from django.contrib.auth.mixins import PermissionRequiredMixin
|
||||
from django.views.generic.detail import DetailView
|
||||
from django.utils import timezone
|
||||
|
||||
from core.models import Child, DiaperChange, Sleep
|
||||
|
||||
from .graphs import (diaperchange_types, diaperchange_lifetimes, sleep_pattern,
|
||||
sleep_totals, timeline)
|
||||
sleep_totals)
|
||||
|
||||
|
||||
class DiaperChangeLifetimesChildReport(PermissionRequiredMixin, DetailView):
|
||||
|
@ -89,22 +88,3 @@ class SleepTotalsChildReport(PermissionRequiredMixin, DetailView):
|
|||
if instances:
|
||||
context['html'], context['javascript'] = sleep_totals(instances)
|
||||
return context
|
||||
|
||||
|
||||
class TimelineChildReport(PermissionRequiredMixin, DetailView):
|
||||
"""Chronological daily view of events (non-graph).
|
||||
"""
|
||||
model = Child
|
||||
permission_required = ('core.view_child',)
|
||||
template_name = 'reports/timeline.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(TimelineChildReport, self).get_context_data(**kwargs)
|
||||
date = self.request.GET.get('date', str(timezone.localdate()))
|
||||
date = timezone.datetime.strptime(date, '%Y-%m-%d')
|
||||
date = timezone.localtime(timezone.make_aware(date))
|
||||
context['objects'] = timeline(self.object, date)
|
||||
context['date'] = date
|
||||
context['date_previous'] = date - timezone.timedelta(days=1)
|
||||
context['date_next'] = date + timezone.timedelta(days=1)
|
||||
return context
|
||||
|
|
Loading…
Reference in New Issue