diff --git a/api/filters.py b/api/filters.py
index b95c15e8..f1b1bcf1 100644
--- a/api/filters.py
+++ b/api/filters.py
@@ -99,7 +99,7 @@ class TemperatureFilter(TimeFieldFilter, TagsFieldFilter):
class TimerFilter(StartEndFieldFilter):
class Meta(StartEndFieldFilter.Meta):
model = models.Timer
- fields = sorted(StartEndFieldFilter.Meta.fields + ["active", "user"])
+ fields = sorted(StartEndFieldFilter.Meta.fields + ["user"])
class TummyTimeFilter(StartEndFieldFilter, TagsFieldFilter):
diff --git a/api/serializers.py b/api/serializers.py
index 23c46fe6..af3982b6 100644
--- a/api/serializers.py
+++ b/api/serializers.py
@@ -77,16 +77,12 @@ class CoreModelWithDurationSerializer(CoreModelSerializer):
timer = attrs["timer"]
attrs.pop("timer")
- if timer.end:
- end = timer.end
- else:
- end = timezone.now()
if timer.child:
attrs["child"] = timer.child
# Overwrites values provided directly!
attrs["start"] = timer.start
- attrs["end"] = end
+ attrs["end"] = timezone.now()
# The "child", "start", and "end" field should all be set at this
# point. If one is not, model validation will fail because they are
@@ -103,7 +99,7 @@ class CoreModelWithDurationSerializer(CoreModelSerializer):
# Only actually stop the timer if all validation passed.
if timer:
- timer.stop(attrs["end"])
+ timer.stop()
return attrs
@@ -232,10 +228,11 @@ class TimerSerializer(CoreModelSerializer):
queryset=get_user_model().objects.all(),
required=False,
)
+ duration = serializers.DurationField(read_only=True, required=False)
class Meta:
model = models.Timer
- fields = ("id", "child", "name", "start", "end", "duration", "active", "user")
+ fields = ("id", "child", "name", "start", "duration", "user")
def validate(self, attrs):
attrs = super(TimerSerializer, self).validate(attrs)
diff --git a/api/tests.py b/api/tests.py
index ed5f8505..535d1efa 100644
--- a/api/tests.py
+++ b/api/tests.py
@@ -47,7 +47,6 @@ class TestBase:
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
timer.refresh_from_db()
- self.assertTrue(timer.active)
child = models.Child.objects.first()
self.timer_test_data["child"] = child.id
@@ -55,11 +54,9 @@ class TestBase:
self.endpoint, self.timer_test_data, format="json"
)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
- timer.refresh_from_db()
- self.assertFalse(timer.active)
obj = self.model.objects.get(pk=response.data["id"])
self.assertEqual(obj.start, start)
- self.assertEqual(obj.end, timer.end)
+ self.assertIsNotNone(obj.end)
def test_post_with_timer_with_child(self):
if not self.timer_test_data:
@@ -73,12 +70,10 @@ class TestBase:
self.endpoint, self.timer_test_data, format="json"
)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
- timer.refresh_from_db()
- self.assertFalse(timer.active)
obj = self.model.objects.get(pk=response.data["id"])
- self.assertEqual(obj.child, timer.child)
+ self.assertIsNotNone(obj.child)
self.assertEqual(obj.start, start)
- self.assertEqual(obj.end, timer.end)
+ self.assertIsNotNone(obj.end)
class BMIAPITestCase(TestBase.BabyBuddyAPITestCaseBase):
@@ -703,19 +698,7 @@ class TimerAPITestCase(TestBase.BabyBuddyAPITestCaseBase):
def test_get(self):
response = self.client.get(self.endpoint)
self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertEqual(
- response.data["results"][0],
- {
- "id": 1,
- "child": None,
- "name": "Fake timer",
- "start": "2017-11-17T23:30:00-05:00",
- "end": "2017-11-18T00:30:00-05:00",
- "duration": "01:00:00",
- "active": False,
- "user": 1,
- },
- )
+ self.assertEqual(response.data["results"][0]["id"], 1)
def test_post(self):
data = {"name": "New fake timer", "user": 1}
@@ -743,31 +726,19 @@ class TimerAPITestCase(TestBase.BabyBuddyAPITestCaseBase):
},
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertEqual(response.data, entry)
+ self.assertEqual(response.data["name"], entry["name"])
- def test_start_stop_timer(self):
+ def test_start_restart_timer(self):
endpoint = "{}{}/".format(self.endpoint, 1)
response = self.client.get(endpoint)
self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertFalse(response.data["active"])
response = self.client.patch(f"{endpoint}restart/")
self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertTrue(response.data["active"])
# Restart twice is allowed
response = self.client.patch(f"{endpoint}restart/")
self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertTrue(response.data["active"])
-
- response = self.client.patch(f"{endpoint}stop/")
- self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertFalse(response.data["active"])
-
- # Stopping twice is allowed, too
- response = self.client.patch(f"{endpoint}stop/")
- self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertFalse(response.data["active"])
class TummyTimeAPITestCase(TestBase.BabyBuddyAPITestCaseBase):
diff --git a/api/views.py b/api/views.py
index edad1ed6..7f513e92 100644
--- a/api/views.py
+++ b/api/views.py
@@ -118,12 +118,6 @@ class TimerViewSet(viewsets.ModelViewSet):
ordering_fields = ("duration", "end", "start")
ordering = "-start"
- @action(detail=True, methods=["patch"])
- def stop(self, request, pk=None):
- timer = self.get_object()
- timer.stop()
- return Response(self.serializer_class(timer).data)
-
@action(detail=True, methods=["patch"])
def restart(self, request, pk=None):
timer = self.get_object()
diff --git a/babybuddy/fixtures/tests.json b/babybuddy/fixtures/tests.json
index 1eed6953..711e9aa0 100644
--- a/babybuddy/fixtures/tests.json
+++ b/babybuddy/fixtures/tests.json
@@ -393,9 +393,6 @@
{
"name": "Fake timer",
"start": "2017-11-18T04:30:00Z",
- "end": "2017-11-18T05:30:00Z",
- "duration": "01:00:00",
- "active": false,
"user": 1
}
},
diff --git a/core/admin.py b/core/admin.py
index 7f2d8757..69550068 100644
--- a/core/admin.py
+++ b/core/admin.py
@@ -216,8 +216,8 @@ class TemperatureAdmin(ImportExportMixin, ExportActionMixin, admin.ModelAdmin):
@admin.register(models.Timer)
class TimerAdmin(admin.ModelAdmin):
- list_display = ("name", "child", "start", "end", "duration", "active", "user")
- list_filter = ("child", "active", "user")
+ list_display = ("name", "child", "start", "duration", "user")
+ list_filter = ("child", "user")
search_fields = ("child__first_name", "child__last_name", "name", "user")
diff --git a/core/forms.py b/core/forms.py
index 22c8e65d..06b49602 100644
--- a/core/forms.py
+++ b/core/forms.py
@@ -44,7 +44,7 @@ def set_initial_values(kwargs, form_type):
if timer_id:
timer = models.Timer.objects.get(id=timer_id)
kwargs["initial"].update(
- {"timer": timer, "start": timer.start, "end": timer.end or timezone.now()}
+ {"timer": timer, "start": timer.start, "end": timezone.now()}
)
# Set type and method values for Feeding instance based on last feed.
@@ -83,7 +83,7 @@ class CoreModelForm(forms.ModelForm):
instance = super(CoreModelForm, self).save(commit=False)
if self.timer_id:
timer = models.Timer.objects.get(id=self.timer_id)
- timer.stop(instance.end)
+ timer.stop()
if commit:
instance.save()
self.save_m2m()
diff --git a/core/migrations/0027_alter_timer_options_remove_timer_duration_and_more.py b/core/migrations/0027_alter_timer_options_remove_timer_duration_and_more.py
new file mode 100644
index 00000000..bed2cb2e
--- /dev/null
+++ b/core/migrations/0027_alter_timer_options_remove_timer_duration_and_more.py
@@ -0,0 +1,38 @@
+from django.db import migrations
+
+
+def delete_inactive_timers(apps, schema_editor):
+ from core import models
+
+ for timer in models.Timer.objects.filter(active=False):
+ timer.delete()
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ("core", "0026_alter_feeding_end_alter_feeding_start_and_more"),
+ ]
+
+ operations = [
+ migrations.AlterModelOptions(
+ name="timer",
+ options={
+ "default_permissions": ("view", "add", "change", "delete"),
+ "ordering": ["-start"],
+ "verbose_name": "Timer",
+ "verbose_name_plural": "Timers",
+ },
+ ),
+ migrations.RemoveField(
+ model_name="timer",
+ name="duration",
+ ),
+ migrations.RemoveField(
+ model_name="timer",
+ name="end",
+ ),
+ migrations.RunPython(
+ delete_inactive_timers, reverse_code=migrations.RunPython.noop
+ ),
+ ]
diff --git a/core/models.py b/core/models.py
index 0a5658c8..e6b77c25 100644
--- a/core/models.py
+++ b/core/models.py
@@ -559,12 +559,6 @@ class Timer(models.Model):
start = models.DateTimeField(
default=timezone.now, blank=False, verbose_name=_("Start time")
)
- end = models.DateTimeField(
- blank=True, editable=False, null=True, verbose_name=_("End time")
- )
- duration = models.DurationField(
- editable=False, null=True, verbose_name=_("Duration")
- )
active = models.BooleanField(default=True, editable=False, verbose_name=_("Active"))
user = models.ForeignKey(
"auth.User",
@@ -577,7 +571,7 @@ class Timer(models.Model):
class Meta:
default_permissions = ("view", "add", "change", "delete")
- ordering = ["-active", "-start", "-end"]
+ ordering = ["-start"]
verbose_name = _("Timer")
verbose_name_plural = _("Timers")
@@ -600,42 +594,24 @@ class Timer(models.Model):
return self.user.get_full_name()
return self.user.get_username()
- @classmethod
- def from_db(cls, db, field_names, values):
- instance = super(Timer, cls).from_db(db, field_names, values)
- if not instance.duration:
- instance.duration = timezone.now() - instance.start
- return instance
+ def duration(self):
+ return timezone.now() - self.start
def restart(self):
"""Restart the timer."""
self.start = timezone.now()
- self.end = None
- self.duration = None
- self.active = True
self.save()
- def stop(self, end=None):
- """Stop the timer."""
- if not end:
- end = timezone.now()
- self.end = end
- self.save()
+ def stop(self):
+ """Stop (delete) the timer."""
+ self.delete()
def save(self, *args, **kwargs):
- self.active = self.end is None
self.name = self.name or None
- if self.start and self.end:
- self.duration = self.end - self.start
- else:
- self.duration = None
super(Timer, self).save(*args, **kwargs)
def clean(self):
validate_time(self.start, "start")
- if self.end:
- validate_time(self.end, "end")
- validate_duration(self)
class TummyTime(models.Model):
diff --git a/core/static_src/js/timer.js b/core/static_src/js/timer.js
index bb594ca0..d3b8f598 100644
--- a/core/static_src/js/timer.js
+++ b/core/static_src/js/timer.js
@@ -93,13 +93,7 @@ BabyBuddy.Timer = function ($) {
timerElement.find('.timer-minutes').text(parseInt(duration[1]));
timerElement.find('.timer-seconds').text(parseInt(duration[2]));
lastUpdate = new Date()
-
- if (data['active']) {
- runIntervalId = setInterval(Timer.tick, 1000);
- }
- else {
- timerElement.addClass('timer-stopped');
- }
+ runIntervalId = setInterval(Timer.tick, 1000);
}
});
}
diff --git a/core/templates/core/timer_confirm_delete_inactive.html b/core/templates/core/timer_confirm_delete_inactive.html
deleted file mode 100644
index ad6bc4ba..00000000
--- a/core/templates/core/timer_confirm_delete_inactive.html
+++ /dev/null
@@ -1,26 +0,0 @@
-{% extends 'babybuddy/page.html' %}
-{% load humanize i18n widget_tweaks %}
-
-{% block title %}
- {% blocktrans %}Delete All Inactive Timers{% endblocktrans %}
-{% endblock %}
-
-{% block breadcrumbs %}
-
{% trans "Timers" %}
- {% trans "Delete Inactive" %}
-{% endblock %}
-
-{% block content %}
-
-{% endblock %}
\ No newline at end of file
diff --git a/core/templates/core/timer_detail.html b/core/templates/core/timer_detail.html
index fa373b50..e091d7f9 100644
--- a/core/templates/core/timer_detail.html
+++ b/core/templates/core/timer_detail.html
@@ -13,7 +13,7 @@
+ class="display-1">
{{ object.duration|hours }}h
{{ object.duration|minutes }}m
{{ object.duration|seconds }}s
@@ -27,9 +27,6 @@
{% trans "Started" %} {{ object.start }}
- {% if not object.active %}
- / {% trans "Stopped" %} {{ object.end }}
- {% endif %}
{% blocktrans trimmed with user=object.user_username %}
@@ -80,14 +77,6 @@
-
- {% if object.active %}
-
- {% endif %}
{% endif %}
@@ -95,9 +84,7 @@
{% endblock %}
{% block javascript %}
- {% if object.active %}
-
- {% endif %}
+
{% endblock %}
\ No newline at end of file
diff --git a/core/templates/core/timer_list.html b/core/templates/core/timer_list.html
index df383838..c9eb433e 100644
--- a/core/templates/core/timer_list.html
+++ b/core/templates/core/timer_list.html
@@ -62,11 +62,4 @@
{% include 'babybuddy/paginator.html' %}
-
- {% if object_list and perms.core.delete_timer %}
-
- {% trans "Delete Inactive Timers" %}
-
- {% endif %}
-
{% endblock %}
\ No newline at end of file
diff --git a/core/templates/core/timer_nav.html b/core/templates/core/timer_nav.html
index d439fd8d..ae8109a6 100644
--- a/core/templates/core/timer_nav.html
+++ b/core/templates/core/timer_nav.html
@@ -49,7 +49,7 @@
{% endif %}
{% if timers %}
-
+
{% for timer in timers %}
{{ timer.title_with_child }}
diff --git a/core/templatetags/timers.py b/core/templatetags/timers.py
index 15b48962..2dd48c35 100644
--- a/core/templatetags/timers.py
+++ b/core/templatetags/timers.py
@@ -8,15 +8,14 @@ register = template.Library()
@register.inclusion_tag("core/timer_nav.html", takes_context=True)
-def timer_nav(context, active=True):
+def timer_nav(context):
"""
- Get a list of active Timer instances to include in the nav menu.
+ Get a list of Timer instances to include in the nav menu.
:param context: Django's context data.
- :param active: the state of Timers to filter.
:returns: a dictionary with timers data.
"""
request = context["request"] or None
- timers = Timer.objects.filter(active=active)
+ timers = Timer.objects.filter()
children = Child.objects.all()
perms = context["perms"] or None
# The 'next' parameter is currently not used.
diff --git a/core/tests/tests_forms.py b/core/tests/tests_forms.py
index 1fed7a34..9466c588 100644
--- a/core/tests/tests_forms.py
+++ b/core/tests/tests_forms.py
@@ -122,27 +122,23 @@ class InitialValuesTestCase(FormsTestCaseBase):
self.assertEqual(page.context["form"].initial["type"], f_three.type)
self.assertEqual(page.context["form"].initial["method"], f_three.method)
- def test_timer_set(self):
+ def test_timer_form_field_set(self):
self.timer.stop()
page = self.c.get("/sleep/add/")
self.assertTrue("start" not in page.context["form"].initial)
self.assertTrue("end" not in page.context["form"].initial)
- page = self.c.get("/sleep/add/?timer={}".format(self.timer.id))
- self.assertEqual(page.context["form"].initial["start"], self.timer.start)
- self.assertEqual(page.context["form"].initial["end"], self.timer.end)
-
def test_timer_stop_on_save(self):
- end = timezone.localtime()
+ timer = models.Timer.objects.create(
+ user=self.user, start=timezone.localtime() - timezone.timedelta(minutes=30)
+ )
params = {
"child": self.child.id,
"start": self.localtime_string(self.timer.start),
- "end": self.localtime_string(end),
+ "end": self.localtime_string(),
}
- page = self.c.post(
- "/sleep/add/?timer={}".format(self.timer.id), params, follow=True
- )
+ page = self.c.post("/sleep/add/?timer={}".format(timer.id), params, follow=True)
self.assertEqual(page.status_code, 200)
self.timer.refresh_from_db()
self.assertFalse(self.timer.active)
diff --git a/core/tests/tests_models.py b/core/tests/tests_models.py
index fd55f308..d413847a 100644
--- a/core/tests/tests_models.py
+++ b/core/tests/tests_models.py
@@ -270,11 +270,9 @@ class TimerTestCase(TestCase):
)
self.user = get_user_model().objects.first()
self.named = models.Timer.objects.create(
- name="Named", end=timezone.localtime(), user=self.user, child=child
- )
- self.unnamed = models.Timer.objects.create(
- end=timezone.localtime(), user=self.user
+ name="Named", user=self.user, child=child
)
+ self.unnamed = models.Timer.objects.create(user=self.user)
def test_timer_create(self):
self.assertEqual(self.named, models.Timer.objects.get(name="Named"))
@@ -302,19 +300,7 @@ class TimerTestCase(TestCase):
def test_timer_restart(self):
self.named.restart()
- self.assertIsNone(self.named.end)
- self.assertIsNone(self.named.duration)
- self.assertTrue(self.named.active)
-
- def test_timer_stop(self):
- stop_time = timezone.localtime()
- self.unnamed.stop(end=stop_time)
- self.assertEqual(self.unnamed.end, stop_time)
- self.assertEqual(
- self.unnamed.duration.seconds,
- (self.unnamed.end - self.unnamed.start).seconds,
- )
- self.assertFalse(self.unnamed.active)
+ self.assertGreaterEqual(timezone.localtime(), self.named.start)
def test_timer_duration(self):
timer = models.Timer.objects.create(user=get_user_model().objects.first())
@@ -322,9 +308,9 @@ class TimerTestCase(TestCase):
timer.save()
timer.refresh_from_db()
- self.assertEqual(timer.duration.seconds, timezone.timedelta(minutes=30).seconds)
- timer.stop()
- self.assertEqual(timer.duration.seconds, timezone.timedelta(minutes=30).seconds)
+ self.assertEqual(
+ timer.duration().seconds, timezone.timedelta(minutes=30).seconds
+ )
class TummyTimeTestCase(TestCase):
diff --git a/core/tests/tests_views.py b/core/tests/tests_views.py
index 3a0ea1a0..53ad6bef 100644
--- a/core/tests/tests_views.py
+++ b/core/tests/tests_views.py
@@ -178,28 +178,11 @@ class ViewsTestCase(TestCase):
page = self.c.get("/timers/{}/delete/".format(entry.id))
self.assertEqual(page.status_code, 200)
- page = self.c.get("/timers/{}/stop/".format(entry.id))
- self.assertEqual(page.status_code, 405)
- page = self.c.post("/timers/{}/stop/".format(entry.id), follow=True)
- self.assertEqual(page.status_code, 200)
-
page = self.c.get("/timers/{}/restart/".format(entry.id))
self.assertEqual(page.status_code, 405)
page = self.c.post("/timers/{}/restart/".format(entry.id), follow=True)
self.assertEqual(page.status_code, 200)
- page = self.c.get("/timers/delete-inactive/", follow=True)
- self.assertEqual(page.status_code, 200)
- messages = list(page.context["messages"])
- self.assertEqual(len(messages), 1)
- self.assertEqual(str(messages[0]), "No inactive timers exist.")
-
- entry = models.Timer.objects.first()
- entry.stop()
- page = self.c.get("/timers/delete-inactive/")
- self.assertEqual(page.status_code, 200)
- self.assertEqual(page.context["timer_count"], 1)
-
def test_timeline_views(self):
child = models.Child.objects.first()
response = self.c.get("/timeline/")
diff --git a/core/urls.py b/core/urls.py
index c660942c..efbce751 100644
--- a/core/urls.py
+++ b/core/urls.py
@@ -72,15 +72,9 @@ urlpatterns = [
path("timers//", views.TimerDetail.as_view(), name="timer-detail"),
path("timers//edit/", views.TimerUpdate.as_view(), name="timer-update"),
path("timers//delete/", views.TimerDelete.as_view(), name="timer-delete"),
- path("timers//stop/", views.TimerStop.as_view(), name="timer-stop"),
path(
"timers//restart/", views.TimerRestart.as_view(), name="timer-restart"
),
- path(
- "timers/delete-inactive/",
- views.TimerDeleteInactive.as_view(),
- name="timer-delete-inactive",
- ),
path("tummy-time/", views.TummyTimeList.as_view(), name="tummytime-list"),
path("tummy-time/add/", views.TummyTimeAdd.as_view(), name="tummytime-add"),
path(
diff --git a/core/views.py b/core/views.py
index 436fd8a1..9bac66d0 100644
--- a/core/views.py
+++ b/core/views.py
@@ -404,7 +404,7 @@ class TimerList(PermissionRequiredMixin, BabyBuddyFilterView):
template_name = "core/timer_list.html"
permission_required = ("core.view_timer",)
paginate_by = 10
- filterset_fields = ("active", "user")
+ filterset_fields = ("user",)
class TimerDetail(PermissionRequiredMixin, DetailView):
@@ -477,55 +477,12 @@ class TimerRestart(PermissionRequiredMixin, RedirectView):
return reverse("core:timer-detail", kwargs={"pk": kwargs["pk"]})
-class TimerStop(PermissionRequiredMixin, SuccessMessageMixin, RedirectView):
- http_method_names = ["post"]
- permission_required = ("core.change_timer",)
- success_message = _("%(timer)s stopped.")
-
- def post(self, request, *args, **kwargs):
- instance = models.Timer.objects.get(id=kwargs["pk"])
- instance.stop()
- messages.success(request, "{} stopped.".format(instance))
- return super(TimerStop, self).get(request, *args, **kwargs)
-
- def get_redirect_url(self, *args, **kwargs):
- return reverse("core:timer-detail", kwargs={"pk": kwargs["pk"]})
-
-
class TimerDelete(CoreDeleteView):
model = models.Timer
permission_required = ("core.delete_timer",)
success_url = reverse_lazy("core:timer-list")
-class TimerDeleteInactive(PermissionRequiredMixin, SuccessMessageMixin, FormView):
- permission_required = ("core.delete_timer",)
- form_class = Form
- template_name = "core/timer_confirm_delete_inactive.html"
- success_url = reverse_lazy("core:timer-list")
- success_message = _("All inactive timers deleted.")
-
- def get_context_data(self, **kwargs):
- kwargs = super().get_context_data(**kwargs)
- kwargs["timer_count"] = self.get_instances().count()
- return kwargs
-
- def get(self, request, *args, **kwargs):
- # Redirect back to list if there are no inactive timers.
- if self.get_instances().count() == 0:
- messages.warning(request, _("No inactive timers exist."))
- return HttpResponseRedirect(self.success_url)
- return super().get(request, *args, **kwargs)
-
- def form_valid(self, form):
- self.get_instances().delete()
- return super().form_valid(form)
-
- @staticmethod
- def get_instances():
- return models.Timer.objects.filter(active=False)
-
-
class TummyTimeList(PermissionRequiredMixin, BabyBuddyFilterView):
model = models.TummyTime
template_name = "core/tummytime_list.html"
diff --git a/dashboard/templates/cards/timer_list.html b/dashboard/templates/cards/timer_list.html
index 2fc3567d..eb6f8e63 100644
--- a/dashboard/templates/cards/timer_list.html
+++ b/dashboard/templates/cards/timer_list.html
@@ -3,16 +3,16 @@
{% block header %}
- {% trans "Active Timers" %}
+ {% trans "Timers" %}
{% endblock %}
{% block title %}
{% with instances|length as count %}
{% blocktrans trimmed count counter=count %}
- {{ count }} active timer
+ {{ count }} timer
{% plural %}
- {{ count }} active timers
+ {{ count }} timers
{% endblocktrans %}
{% endwith %}
{% endblock %}
diff --git a/dashboard/templatetags/cards.py b/dashboard/templatetags/cards.py
index 17ef75ac..11e9f14b 100644
--- a/dashboard/templatetags/cards.py
+++ b/dashboard/templatetags/cards.py
@@ -707,10 +707,10 @@ def card_timer_list(context, child=None):
if child:
# Get active instances for the selected child _or_ None (no child).
instances = models.Timer.objects.filter(
- Q(active=True), Q(child=child) | Q(child=None)
+ Q(child=child) | Q(child=None)
).order_by("-start")
else:
- instances = models.Timer.objects.filter(active=True).order_by("-start")
+ instances = models.Timer.objects.order_by("-start")
empty = len(instances) == 0
return {
diff --git a/dashboard/tests/tests_templatetags.py b/dashboard/tests/tests_templatetags.py
index 1c515b30..f26e96c6 100644
--- a/dashboard/tests/tests_templatetags.py
+++ b/dashboard/tests/tests_templatetags.py
@@ -323,7 +323,7 @@ class TemplateTagsTestCase(TestCase):
data = cards.card_timer_list(self.context)
self.assertIsInstance(data["instances"][0], models.Timer)
- self.assertEqual(len(data["instances"]), 3)
+ self.assertEqual(len(data["instances"]), 4)
data = cards.card_timer_list(self.context, child)
self.assertIsInstance(data["instances"][0], models.Timer)
diff --git a/docs/api.md b/docs/api.md
index 26c0c142..266aafff 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -222,9 +222,7 @@ Note the timer `id` in the response:
"child": 1,
"name": null,
"start": "2022-05-28T19:59:40.013914Z",
- "end": null,
"duration": null,
- "active": true,
"user": 1
}
```
@@ -252,25 +250,7 @@ Note that `child` and `start` match the timer values (and `end` is auto-populate
}
```
-Also note that the timer has been stopped:
-
-```shell
-curl --location --request GET '[...]/api/timers/5' \
---header 'Authorization: Token [...]'
-```
-
-```json
-{
- "id": 5,
- "child": 1,
- "name": null,
- "start": "2022-05-28T19:59:40.013914Z",
- "end": "2022-05-28T20:01:13.549099Z",
- "duration": "00:01:33.535185",
- "active": false,
- "user": 1
-}
-```
+Also note that the timer has been deleted.
### Response
diff --git a/docs/user-guide/getting-started.md b/docs/user-guide/getting-started.md
index 4d0aebad..f113c317 100644
--- a/docs/user-guide/getting-started.md
+++ b/docs/user-guide/getting-started.md
@@ -19,7 +19,7 @@ an overview of all elements related to your child, including:
- Last sleep is the start of the last nap/sleep, and includes the nap duration below.
- Last feeding method is a quick view of how the baby was last fed. This is particularly useful for nursing mothers to remember which breast they started with on the previous feed.
- Today's Feeding is a snapshot of the total numbers of daily feeds.
-- Active timers let you know if you have a timer running.
+- Timers let you know if you have a timer running.
- Statistics is a snapshot of various statistics – these can be scrolled through or select Statistics from the menu bar to see more.
- Today's Sleep lists the total number of hours slept for the day.
- Today's Naps lists the number of naps taken that day in bold and the total nap time below.
diff --git a/static/babybuddy/js/app.d20500d757a5.js b/static/babybuddy/js/app.d20500d757a5.js
new file mode 100644
index 00000000..9c004f47
--- /dev/null
+++ b/static/babybuddy/js/app.d20500d757a5.js
@@ -0,0 +1,206 @@
+if (typeof jQuery === 'undefined') {
+ throw new Error('Baby Buddy requires jQuery.')
+}
+
+/**
+ * Baby Buddy Namespace
+ *
+ * Default namespace for the Baby Buddy app.
+ *
+ * @type {{}}
+ */
+var BabyBuddy = function () {
+ return {};
+}();
+
+/**
+ * Pull to refresh.
+ *
+ * @type {{init: BabyBuddy.PullToRefresh.init, onRefresh: BabyBuddy.PullToRefresh.onRefresh}}
+ */
+BabyBuddy.PullToRefresh = function(ptr) {
+ return {
+ init: function () {
+ ptr.init({
+ mainElement: 'body',
+ onRefresh: this.onRefresh
+ });
+ },
+
+ onRefresh: function() {
+ window.location.reload();
+ }
+ };
+}(PullToRefresh);
+
+/**
+ * Fix for duplicate form submission from double pressing submit
+ */
+function preventDoubleSubmit() {
+ return false;
+}
+$('form').off("submit", preventDoubleSubmit);
+$("form").on("submit", function() {
+ $(this).on("submit", preventDoubleSubmit);
+});
+
+/* Baby Buddy Timer
+ *
+ * Uses a supplied ID to run a timer. The element using the ID must have
+ * three children with the following classes:
+ * * timer-seconds
+ * * timer-minutes
+ * * timer-hours
+ */
+BabyBuddy.Timer = function ($) {
+ var runIntervalId = null;
+ var timerId = null;
+ var timerElement = null;
+ var lastUpdate = new Date();
+ var hidden = null;
+
+ var Timer = {
+ run: function(timer_id, element_id) {
+ timerId = timer_id;
+ timerElement = $('#' + element_id);
+
+ if (timerElement.length === 0) {
+ console.error('BBTimer: Timer element not found.');
+ return false;
+ }
+
+ if (timerElement.find('.timer-seconds').length === 0
+ || timerElement.find('.timer-minutes').length === 0
+ || timerElement.find('.timer-hours').length === 0) {
+ console.error('BBTimer: Element does not contain expected children.');
+ return false;
+ }
+
+ runIntervalId = setInterval(this.tick, 1000);
+
+ // If the page just came in to view, update the timer data with the
+ // current actual duration. This will (potentially) help mobile
+ // phones that lock with the timer page open.
+ if (typeof document.hidden !== "undefined") {
+ hidden = "hidden";
+ }
+ else if (typeof document.msHidden !== "undefined") {
+ hidden = "msHidden";
+ }
+ else if (typeof document.webkitHidden !== "undefined") {
+ hidden = "webkitHidden";
+ }
+ window.addEventListener('focus', Timer.handleVisibilityChange, false);
+ },
+
+ handleVisibilityChange: function() {
+ if (!document[hidden] && (new Date()) - lastUpdate > 1) {
+ Timer.update();
+ }
+ },
+
+ tick: function() {
+ var s = timerElement.find('.timer-seconds');
+ var seconds = Number(s.text());
+ if (seconds < 59) {
+ s.text(seconds + 1);
+ return;
+ }
+ else {
+ s.text(0);
+ }
+
+ var m = timerElement.find('.timer-minutes');
+ var minutes = Number(m.text());
+ if (minutes < 59) {
+ m.text(minutes + 1);
+ return;
+ }
+ else {
+ m.text(0);
+ }
+
+ var h = timerElement.find('.timer-hours');
+ var hours = Number(h.text());
+ h.text(hours + 1);
+ },
+
+ update: function() {
+ $.get('/api/timers/' + timerId + '/', function(data) {
+ if (data && 'duration' in data) {
+ clearInterval(runIntervalId);
+ var duration = data.duration.split(/[\s:.]/)
+ if (duration.length === 5) {
+ duration[0] = parseInt(duration[0]) * 24 + parseInt(duration[1]);
+ duration[1] = duration[2];
+ duration[2] = duration[3];
+ }
+ timerElement.find('.timer-hours').text(parseInt(duration[0]));
+ timerElement.find('.timer-minutes').text(parseInt(duration[1]));
+ timerElement.find('.timer-seconds').text(parseInt(duration[2]));
+ lastUpdate = new Date()
+ runIntervalId = setInterval(Timer.tick, 1000);
+ }
+ });
+ }
+ };
+
+ return Timer;
+}(jQuery);
+
+/* Baby Buddy Dashboard
+ *
+ * Provides a "watch" function to update the dashboard at one minute intervals
+ * and/or on visibility state changes.
+ */
+BabyBuddy.Dashboard = function ($) {
+ var runIntervalId = null;
+ var dashboardElement = null;
+ var hidden = null;
+
+ var Dashboard = {
+ watch: function(element_id, refresh_rate) {
+ dashboardElement = $('#' + element_id);
+
+ if (dashboardElement.length == 0) {
+ console.error('Baby Buddy: Dashboard element not found.');
+ return false;
+ }
+
+ if (typeof document.hidden !== "undefined") {
+ hidden = "hidden";
+ }
+ else if (typeof document.msHidden !== "undefined") {
+ hidden = "msHidden";
+ }
+ else if (typeof document.webkitHidden !== "undefined") {
+ hidden = "webkitHidden";
+ }
+
+ if (typeof window.addEventListener === "undefined" || typeof document.hidden === "undefined") {
+ if (refresh_rate) {
+ runIntervalId = setInterval(this.update, refresh_rate);
+ }
+ }
+ else {
+ window.addEventListener('focus', Dashboard.handleVisibilityChange, false);
+ if (refresh_rate) {
+ runIntervalId = setInterval(Dashboard.handleVisibilityChange, refresh_rate);
+ }
+ }
+ },
+
+ handleVisibilityChange: function() {
+ if (!document[hidden]) {
+ Dashboard.update();
+ }
+ },
+
+ update: function() {
+ // TODO: Someday maybe update in place?
+ location.reload();
+ }
+ };
+
+ return Dashboard;
+}(jQuery);
diff --git a/static/babybuddy/js/app.d20500d757a5.js.gz b/static/babybuddy/js/app.d20500d757a5.js.gz
new file mode 100644
index 00000000..3f61214c
Binary files /dev/null and b/static/babybuddy/js/app.d20500d757a5.js.gz differ
diff --git a/static/babybuddy/js/app.js b/static/babybuddy/js/app.js
index 23143cbc..9c004f47 100644
--- a/static/babybuddy/js/app.js
+++ b/static/babybuddy/js/app.js
@@ -1 +1,206 @@
-if("undefined"==typeof jQuery)throw new Error("Baby Buddy requires jQuery.");var BabyBuddy={};function preventDoubleSubmit(){return!1}BabyBuddy.PullToRefresh=function(e){return{init:function(){e.init({mainElement:"body",onRefresh:this.onRefresh})},onRefresh:function(){window.location.reload()}}}(PullToRefresh),$("form").off("submit",preventDoubleSubmit),$("form").on("submit",function(){$(this).on("submit",preventDoubleSubmit)}),BabyBuddy.Timer=function(e){var n=null,t=null,i=null,d=new Date,r=null,o={run:function(d,u){return t=d,0===(i=e("#"+u)).length?(console.error("BBTimer: Timer element not found."),!1):0===i.find(".timer-seconds").length||0===i.find(".timer-minutes").length||0===i.find(".timer-hours").length?(console.error("BBTimer: Element does not contain expected children."),!1):(n=setInterval(this.tick,1e3),void 0!==document.hidden?r="hidden":void 0!==document.msHidden?r="msHidden":void 0!==document.webkitHidden&&(r="webkitHidden"),void window.addEventListener("focus",o.handleVisibilityChange,!1))},handleVisibilityChange:function(){!document[r]&&new Date-d>1&&o.update()},tick:function(){var e=i.find(".timer-seconds"),n=Number(e.text());if(n<59)e.text(n+1);else{e.text(0);var t=i.find(".timer-minutes"),d=Number(t.text());if(d<59)t.text(d+1);else{t.text(0);var r=i.find(".timer-hours"),o=Number(r.text());r.text(o+1)}}},update:function(){e.get("/api/timers/"+t+"/",function(e){if(e&&"duration"in e){clearInterval(n);var t=e.duration.split(/[\s:.]/);5===t.length&&(t[0]=24*parseInt(t[0])+parseInt(t[1]),t[1]=t[2],t[2]=t[3]),i.find(".timer-hours").text(parseInt(t[0])),i.find(".timer-minutes").text(parseInt(t[1])),i.find(".timer-seconds").text(parseInt(t[2])),d=new Date,e.active?n=setInterval(o.tick,1e3):i.addClass("timer-stopped")}})}};return o}(jQuery),BabyBuddy.Dashboard=function(e){var n=null,t={watch:function(i,d){if(0==e("#"+i).length)return console.error("Baby Buddy: Dashboard element not found."),!1;void 0!==document.hidden?n="hidden":void 0!==document.msHidden?n="msHidden":void 0!==document.webkitHidden&&(n="webkitHidden"),void 0===window.addEventListener||void 0===document.hidden?d&&setInterval(this.update,d):(window.addEventListener("focus",t.handleVisibilityChange,!1),d&&setInterval(t.handleVisibilityChange,d))},handleVisibilityChange:function(){document[n]||t.update()},update:function(){location.reload()}};return t}(jQuery);
\ No newline at end of file
+if (typeof jQuery === 'undefined') {
+ throw new Error('Baby Buddy requires jQuery.')
+}
+
+/**
+ * Baby Buddy Namespace
+ *
+ * Default namespace for the Baby Buddy app.
+ *
+ * @type {{}}
+ */
+var BabyBuddy = function () {
+ return {};
+}();
+
+/**
+ * Pull to refresh.
+ *
+ * @type {{init: BabyBuddy.PullToRefresh.init, onRefresh: BabyBuddy.PullToRefresh.onRefresh}}
+ */
+BabyBuddy.PullToRefresh = function(ptr) {
+ return {
+ init: function () {
+ ptr.init({
+ mainElement: 'body',
+ onRefresh: this.onRefresh
+ });
+ },
+
+ onRefresh: function() {
+ window.location.reload();
+ }
+ };
+}(PullToRefresh);
+
+/**
+ * Fix for duplicate form submission from double pressing submit
+ */
+function preventDoubleSubmit() {
+ return false;
+}
+$('form').off("submit", preventDoubleSubmit);
+$("form").on("submit", function() {
+ $(this).on("submit", preventDoubleSubmit);
+});
+
+/* Baby Buddy Timer
+ *
+ * Uses a supplied ID to run a timer. The element using the ID must have
+ * three children with the following classes:
+ * * timer-seconds
+ * * timer-minutes
+ * * timer-hours
+ */
+BabyBuddy.Timer = function ($) {
+ var runIntervalId = null;
+ var timerId = null;
+ var timerElement = null;
+ var lastUpdate = new Date();
+ var hidden = null;
+
+ var Timer = {
+ run: function(timer_id, element_id) {
+ timerId = timer_id;
+ timerElement = $('#' + element_id);
+
+ if (timerElement.length === 0) {
+ console.error('BBTimer: Timer element not found.');
+ return false;
+ }
+
+ if (timerElement.find('.timer-seconds').length === 0
+ || timerElement.find('.timer-minutes').length === 0
+ || timerElement.find('.timer-hours').length === 0) {
+ console.error('BBTimer: Element does not contain expected children.');
+ return false;
+ }
+
+ runIntervalId = setInterval(this.tick, 1000);
+
+ // If the page just came in to view, update the timer data with the
+ // current actual duration. This will (potentially) help mobile
+ // phones that lock with the timer page open.
+ if (typeof document.hidden !== "undefined") {
+ hidden = "hidden";
+ }
+ else if (typeof document.msHidden !== "undefined") {
+ hidden = "msHidden";
+ }
+ else if (typeof document.webkitHidden !== "undefined") {
+ hidden = "webkitHidden";
+ }
+ window.addEventListener('focus', Timer.handleVisibilityChange, false);
+ },
+
+ handleVisibilityChange: function() {
+ if (!document[hidden] && (new Date()) - lastUpdate > 1) {
+ Timer.update();
+ }
+ },
+
+ tick: function() {
+ var s = timerElement.find('.timer-seconds');
+ var seconds = Number(s.text());
+ if (seconds < 59) {
+ s.text(seconds + 1);
+ return;
+ }
+ else {
+ s.text(0);
+ }
+
+ var m = timerElement.find('.timer-minutes');
+ var minutes = Number(m.text());
+ if (minutes < 59) {
+ m.text(minutes + 1);
+ return;
+ }
+ else {
+ m.text(0);
+ }
+
+ var h = timerElement.find('.timer-hours');
+ var hours = Number(h.text());
+ h.text(hours + 1);
+ },
+
+ update: function() {
+ $.get('/api/timers/' + timerId + '/', function(data) {
+ if (data && 'duration' in data) {
+ clearInterval(runIntervalId);
+ var duration = data.duration.split(/[\s:.]/)
+ if (duration.length === 5) {
+ duration[0] = parseInt(duration[0]) * 24 + parseInt(duration[1]);
+ duration[1] = duration[2];
+ duration[2] = duration[3];
+ }
+ timerElement.find('.timer-hours').text(parseInt(duration[0]));
+ timerElement.find('.timer-minutes').text(parseInt(duration[1]));
+ timerElement.find('.timer-seconds').text(parseInt(duration[2]));
+ lastUpdate = new Date()
+ runIntervalId = setInterval(Timer.tick, 1000);
+ }
+ });
+ }
+ };
+
+ return Timer;
+}(jQuery);
+
+/* Baby Buddy Dashboard
+ *
+ * Provides a "watch" function to update the dashboard at one minute intervals
+ * and/or on visibility state changes.
+ */
+BabyBuddy.Dashboard = function ($) {
+ var runIntervalId = null;
+ var dashboardElement = null;
+ var hidden = null;
+
+ var Dashboard = {
+ watch: function(element_id, refresh_rate) {
+ dashboardElement = $('#' + element_id);
+
+ if (dashboardElement.length == 0) {
+ console.error('Baby Buddy: Dashboard element not found.');
+ return false;
+ }
+
+ if (typeof document.hidden !== "undefined") {
+ hidden = "hidden";
+ }
+ else if (typeof document.msHidden !== "undefined") {
+ hidden = "msHidden";
+ }
+ else if (typeof document.webkitHidden !== "undefined") {
+ hidden = "webkitHidden";
+ }
+
+ if (typeof window.addEventListener === "undefined" || typeof document.hidden === "undefined") {
+ if (refresh_rate) {
+ runIntervalId = setInterval(this.update, refresh_rate);
+ }
+ }
+ else {
+ window.addEventListener('focus', Dashboard.handleVisibilityChange, false);
+ if (refresh_rate) {
+ runIntervalId = setInterval(Dashboard.handleVisibilityChange, refresh_rate);
+ }
+ }
+ },
+
+ handleVisibilityChange: function() {
+ if (!document[hidden]) {
+ Dashboard.update();
+ }
+ },
+
+ update: function() {
+ // TODO: Someday maybe update in place?
+ location.reload();
+ }
+ };
+
+ return Dashboard;
+}(jQuery);
diff --git a/static/babybuddy/js/app.js.gz b/static/babybuddy/js/app.js.gz
index a26b6700..3f61214c 100644
Binary files a/static/babybuddy/js/app.js.gz and b/static/babybuddy/js/app.js.gz differ
diff --git a/static/staticfiles.json b/static/staticfiles.json
index fa4647f1..93a51e7f 100644
--- a/static/staticfiles.json
+++ b/static/staticfiles.json
@@ -1 +1 @@
-{"paths": {"admin/js/vendor/select2/i18n/cs.js": "admin/js/vendor/select2/i18n/cs.4f43e8e7d33a.js", "admin/js/vendor/select2/i18n/en.js": "admin/js/vendor/select2/i18n/en.cf932ba09a98.js", "admin/js/vendor/select2/i18n/ko.js": "admin/js/vendor/select2/i18n/ko.e7be6c20e673.js", "admin/js/vendor/select2/i18n/dsb.js": "admin/js/vendor/select2/i18n/dsb.56372c92d2f1.js", "admin/js/vendor/select2/i18n/lv.js": "admin/js/vendor/select2/i18n/lv.08e62128eac1.js", "admin/js/vendor/select2/i18n/hsb.js": "admin/js/vendor/select2/i18n/hsb.fa3b55265efe.js", "admin/js/vendor/select2/i18n/km.js": "admin/js/vendor/select2/i18n/km.c23089cb06ca.js", "admin/js/vendor/select2/i18n/pl.js": "admin/js/vendor/select2/i18n/pl.6031b4f16452.js", "admin/js/vendor/select2/i18n/de.js": "admin/js/vendor/select2/i18n/de.8a1c222b0204.js", "admin/js/vendor/select2/i18n/sr.js": "admin/js/vendor/select2/i18n/sr.5ed85a48f483.js", "admin/js/vendor/select2/i18n/ru.js": "admin/js/vendor/select2/i18n/ru.934aa95f5b5f.js", "admin/js/vendor/select2/i18n/he.js": "admin/js/vendor/select2/i18n/he.e420ff6cd3ed.js", "admin/js/vendor/select2/i18n/el.js": "admin/js/vendor/select2/i18n/el.27097f071856.js", "admin/js/vendor/select2/i18n/pt-BR.js": "admin/js/vendor/select2/i18n/pt-BR.e1b294433e7f.js", "admin/js/vendor/select2/i18n/uk.js": "admin/js/vendor/select2/i18n/uk.8cede7f4803c.js", "admin/js/vendor/select2/i18n/nl.js": "admin/js/vendor/select2/i18n/nl.997868a37ed8.js", "admin/js/vendor/select2/i18n/sv.js": "admin/js/vendor/select2/i18n/sv.7a9c2f71e777.js", "admin/js/vendor/select2/i18n/mk.js": "admin/js/vendor/select2/i18n/mk.dabbb9087130.js", "admin/js/vendor/select2/i18n/bg.js": "admin/js/vendor/select2/i18n/bg.39b8be30d4f0.js", "admin/js/vendor/select2/i18n/zh-CN.js": "admin/js/vendor/select2/i18n/zh-CN.2cff662ec5f9.js", "admin/js/vendor/select2/i18n/vi.js": "admin/js/vendor/select2/i18n/vi.097a5b75b3e1.js", "admin/js/vendor/select2/i18n/tr.js": "admin/js/vendor/select2/i18n/tr.b5a0643d1545.js", "admin/js/vendor/select2/i18n/tk.js": "admin/js/vendor/select2/i18n/tk.7c572a68c78f.js", "admin/js/vendor/select2/i18n/fr.js": "admin/js/vendor/select2/i18n/fr.05e0542fcfe6.js", "admin/js/vendor/select2/i18n/gl.js": "admin/js/vendor/select2/i18n/gl.d99b1fedaa86.js", "admin/js/vendor/select2/i18n/ps.js": "admin/js/vendor/select2/i18n/ps.38dfa47af9e0.js", "admin/js/vendor/select2/i18n/hr.js": "admin/js/vendor/select2/i18n/hr.a2b092cc1147.js", "admin/js/vendor/select2/i18n/eu.js": "admin/js/vendor/select2/i18n/eu.adfe5c97b72c.js", "admin/js/vendor/select2/i18n/ar.js": "admin/js/vendor/select2/i18n/ar.65aa8e36bf5d.js", "admin/js/vendor/select2/i18n/af.js": "admin/js/vendor/select2/i18n/af.4f6fcd73488c.js", "admin/js/vendor/select2/i18n/zh-TW.js": "admin/js/vendor/select2/i18n/zh-TW.04554a227c2b.js", "admin/js/vendor/select2/i18n/ka.js": "admin/js/vendor/select2/i18n/ka.2083264a54f0.js", "admin/js/vendor/select2/i18n/th.js": "admin/js/vendor/select2/i18n/th.f38c20b0221b.js", "admin/js/vendor/select2/i18n/sr-Cyrl.js": "admin/js/vendor/select2/i18n/sr-Cyrl.f254bb8c4c7c.js", "admin/js/vendor/select2/i18n/lt.js": "admin/js/vendor/select2/i18n/lt.23c7ce903300.js", "admin/js/vendor/select2/i18n/hy.js": "admin/js/vendor/select2/i18n/hy.c7babaeef5a6.js", "admin/js/vendor/select2/i18n/sq.js": "admin/js/vendor/select2/i18n/sq.5636b60d29c9.js", "admin/js/vendor/select2/i18n/et.js": "admin/js/vendor/select2/i18n/et.2b96fd98289d.js", "admin/js/vendor/select2/i18n/sl.js": "admin/js/vendor/select2/i18n/sl.131a78bc0752.js", "admin/js/vendor/select2/i18n/bs.js": "admin/js/vendor/select2/i18n/bs.91624382358e.js", "admin/js/vendor/select2/i18n/fi.js": "admin/js/vendor/select2/i18n/fi.614ec42aa9ba.js", "admin/js/vendor/select2/i18n/hi.js": "admin/js/vendor/select2/i18n/hi.70640d41628f.js", "admin/js/vendor/select2/i18n/ja.js": "admin/js/vendor/select2/i18n/ja.170ae885d74f.js", "admin/js/vendor/select2/i18n/ne.js": "admin/js/vendor/select2/i18n/ne.3d79fd3f08db.js", "admin/js/vendor/select2/i18n/sk.js": "admin/js/vendor/select2/i18n/sk.33d02cef8d11.js", "admin/js/vendor/select2/i18n/da.js": "admin/js/vendor/select2/i18n/da.766346afe4dd.js", "admin/js/vendor/select2/i18n/id.js": "admin/js/vendor/select2/i18n/id.04debded514d.js", "admin/js/vendor/select2/i18n/es.js": "admin/js/vendor/select2/i18n/es.66dbc2652fb1.js", "admin/js/vendor/select2/i18n/it.js": "admin/js/vendor/select2/i18n/it.be4fe8d365b5.js", "admin/js/vendor/select2/i18n/hu.js": "admin/js/vendor/select2/i18n/hu.6ec6039cb8a3.js", "admin/js/vendor/select2/i18n/bn.js": "admin/js/vendor/select2/i18n/bn.6d42b4dd5665.js", "admin/js/vendor/select2/i18n/ms.js": "admin/js/vendor/select2/i18n/ms.4ba82c9a51ce.js", "admin/js/vendor/select2/i18n/fa.js": "admin/js/vendor/select2/i18n/fa.3b5bd1961cfd.js", "admin/js/vendor/select2/i18n/nb.js": "admin/js/vendor/select2/i18n/nb.da2fce143f27.js", "admin/js/vendor/select2/i18n/az.js": "admin/js/vendor/select2/i18n/az.270c257daf81.js", "admin/js/vendor/select2/i18n/ca.js": "admin/js/vendor/select2/i18n/ca.a166b745933a.js", "admin/js/vendor/select2/i18n/ro.js": "admin/js/vendor/select2/i18n/ro.f75cb460ec3b.js", "admin/js/vendor/select2/i18n/pt.js": "admin/js/vendor/select2/i18n/pt.33b4a3b44d43.js", "admin/js/vendor/select2/i18n/is.js": "admin/js/vendor/select2/i18n/is.3ddd9a6a97e9.js", "admin/js/vendor/xregexp/LICENSE.txt": "admin/js/vendor/xregexp/LICENSE.bf79e414957a.txt", "admin/js/vendor/xregexp/xregexp.min.js": "admin/js/vendor/xregexp/xregexp.min.b0439563a5d3.js", "admin/js/vendor/xregexp/xregexp.js": "admin/js/vendor/xregexp/xregexp.efda034b9537.js", "admin/js/vendor/select2/LICENSE.md": "admin/js/vendor/select2/LICENSE.f94142512c91.md", "admin/js/vendor/select2/select2.full.js": "admin/js/vendor/select2/select2.full.c2afdeda3058.js", "admin/js/vendor/select2/select2.full.min.js": "admin/js/vendor/select2/select2.full.min.fcd7500d8e13.js", "admin/js/vendor/jquery/jquery.js": "admin/js/vendor/jquery/jquery.2849239b95f5.js", "admin/js/vendor/jquery/LICENSE.txt": "admin/js/vendor/jquery/LICENSE.de877aa6d744.txt", "admin/js/vendor/jquery/jquery.min.js": "admin/js/vendor/jquery/jquery.min.8fb8fee4fcc3.js", "admin/css/vendor/select2/select2.min.css": "admin/css/vendor/select2/select2.min.9f54e6414f87.css", "admin/css/vendor/select2/select2.css": "admin/css/vendor/select2/select2.a2194c262648.css", "admin/css/vendor/select2/LICENSE-SELECT2.md": "admin/css/vendor/select2/LICENSE-SELECT2.f94142512c91.md", "babybuddy/img/core/child-placeholder.png": "babybuddy/img/core/child-placeholder.7c0a81f0d7f0.png", "rest_framework/docs/js/jquery.json-view.min.js": "rest_framework/docs/js/jquery.json-view.min.b7c2d6981377.js", "rest_framework/docs/js/highlight.pack.js": "rest_framework/docs/js/highlight.pack.479b5f21dcba.js", "rest_framework/docs/js/api.js": "rest_framework/docs/js/api.18a5ba8a1bd8.js", "rest_framework/docs/css/base.css": "rest_framework/docs/css/base.e630f8f4990e.css", "rest_framework/docs/css/jquery.json-view.min.css": "rest_framework/docs/css/jquery.json-view.min.a2e6beeb6710.css", "rest_framework/docs/css/highlight.css": "rest_framework/docs/css/highlight.e0e4d973c6d7.css", "rest_framework/docs/img/grid.png": "rest_framework/docs/img/grid.a4b938cf382b.png", "rest_framework/docs/img/favicon.ico": "rest_framework/docs/img/favicon.5195b4d0f3eb.ico", "admin/js/admin/DateTimeShortcuts.js": "admin/js/admin/DateTimeShortcuts.300591891b2b.js", "admin/js/admin/RelatedObjectLookups.js": "admin/js/admin/RelatedObjectLookups.de5309ac06dd.js", "admin/img/gis/move_vertex_on.svg": "admin/img/gis/move_vertex_on.0047eba25b67.svg", "admin/img/gis/move_vertex_off.svg": "admin/img/gis/move_vertex_off.7a23bf31ef8a.svg", "babybuddy/logo/icon-brand.png": "babybuddy/logo/icon-brand.32cbedf6aee3.png", "babybuddy/logo/logo-sad.png": "babybuddy/logo/logo-sad.47c3d5c2d397.png", "babybuddy/logo/logo.png": "babybuddy/logo/logo.62870041cc83.png", "babybuddy/logo/icon.png": "babybuddy/logo/icon.df80640f0465.png", "babybuddy/js/app.js": "babybuddy/js/app.124d3a444540.js", "babybuddy/js/graph.js": "babybuddy/js/graph.ad87e6353f28.js", "babybuddy/js/tags_editor.js": "babybuddy/js/tags_editor.cf5018f5a70a.js", "babybuddy/css/app.css": "babybuddy/css/app.a8ba77c4f554.css", "babybuddy/root/site.webmanifest": "babybuddy/root/site.c6c4158e40df.webmanifest", "babybuddy/root/favicon.ico": "babybuddy/root/favicon.ee5ebcd40fb9.ico", "babybuddy/root/apple-touch-startup-image.png": "babybuddy/root/apple-touch-startup-image.749726217484.png", "babybuddy/root/favicon.svg": "babybuddy/root/favicon.12fe726d0bac.svg", "babybuddy/root/android-chrome-512x512.png": "babybuddy/root/android-chrome-512x512.e1fd38ad828c.png", "babybuddy/root/mstile-150x150.png": "babybuddy/root/mstile-150x150.08524a406cf2.png", "babybuddy/root/apple-touch-icon.png": "babybuddy/root/apple-touch-icon.bdc75cec89fa.png", "babybuddy/root/safari-pinned-tab.svg": "babybuddy/root/safari-pinned-tab.e8c8ac2f55f5.svg", "babybuddy/root/android-chrome-192x192.png": "babybuddy/root/android-chrome-192x192.ac7d2baba4df.png", "babybuddy/root/browserconfig.xml": "babybuddy/root/browserconfig.84708aade0e5.xml", "babybuddy/font/babybuddy.ttf": "babybuddy/font/babybuddy.b6a356bd9752.ttf", "babybuddy/font/babybuddy.eot": "babybuddy/font/babybuddy.ae7049e685dd.eot", "babybuddy/font/babybuddy.svg": "babybuddy/font/babybuddy.ef799e0dd5c7.svg", "babybuddy/font/babybuddy.woff2": "babybuddy/font/babybuddy.998e9bc52faf.woff2", "babybuddy/font/babybuddy.woff": "babybuddy/font/babybuddy.1913791605fb.woff", "rest_framework/js/prettify-min.js": "rest_framework/js/prettify-min.709bfcc456c6.js", "rest_framework/js/default.js": "rest_framework/js/default.5b08897dbdc3.js", "rest_framework/js/jquery-3.5.1.min.js": "rest_framework/js/jquery-3.5.1.min.dc5e7f18c8d3.js", "rest_framework/js/csrf.js": "rest_framework/js/csrf.969930007329.js", "rest_framework/js/bootstrap.min.js": "rest_framework/js/bootstrap.min.2f34b630ffe3.js", "rest_framework/js/ajax-form.js": "rest_framework/js/ajax-form.0ea6e6052ab5.js", "rest_framework/js/coreapi-0.1.1.js": "rest_framework/js/coreapi-0.1.1.e580e3854595.js", "rest_framework/css/prettify.css": "rest_framework/css/prettify.a987f72342ee.css", "rest_framework/css/font-awesome-4.0.3.css": "rest_framework/css/font-awesome-4.0.3.c1e1ea213abf.css", "rest_framework/css/bootstrap-tweaks.css": "rest_framework/css/bootstrap-tweaks.46ed116b0edd.css", "rest_framework/css/default.css": "rest_framework/css/default.789dfb5732d7.css", "rest_framework/css/bootstrap-theme.min.css": "rest_framework/css/bootstrap-theme.min.1d4b05b397c3.css", "rest_framework/css/bootstrap.min.css.map": "rest_framework/css/bootstrap.min.css.cafbda9c0e9e.map", "rest_framework/css/bootstrap.min.css": "rest_framework/css/bootstrap.min.f17d4516b026.css", "rest_framework/css/bootstrap-theme.min.css.map": "rest_framework/css/bootstrap-theme.min.css.51806092cc05.map", "rest_framework/fonts/fontawesome-webfont.eot": "rest_framework/fonts/fontawesome-webfont.8b27bc96115c.eot", "rest_framework/fonts/glyphicons-halflings-regular.svg": "rest_framework/fonts/glyphicons-halflings-regular.08eda92397ae.svg", "rest_framework/fonts/glyphicons-halflings-regular.woff2": "rest_framework/fonts/glyphicons-halflings-regular.448c34a56d69.woff2", "rest_framework/fonts/glyphicons-halflings-regular.ttf": "rest_framework/fonts/glyphicons-halflings-regular.e18bbf611f2a.ttf", "rest_framework/fonts/fontawesome-webfont.ttf": "rest_framework/fonts/fontawesome-webfont.dcb26c7239d8.ttf", "rest_framework/fonts/fontawesome-webfont.svg": "rest_framework/fonts/fontawesome-webfont.83e37a11f9d7.svg", "rest_framework/fonts/glyphicons-halflings-regular.eot": "rest_framework/fonts/glyphicons-halflings-regular.f4769f9bdb74.eot", "rest_framework/fonts/glyphicons-halflings-regular.woff": "rest_framework/fonts/glyphicons-halflings-regular.fa2772327f55.woff", "rest_framework/fonts/fontawesome-webfont.woff": "rest_framework/fonts/fontawesome-webfont.3293616ec0c6.woff", "rest_framework/img/grid.png": "rest_framework/img/grid.a4b938cf382b.png", "rest_framework/img/glyphicons-halflings.png": "rest_framework/img/glyphicons-halflings.90233c9067e9.png", "rest_framework/img/glyphicons-halflings-white.png": "rest_framework/img/glyphicons-halflings-white.9bbc6e960299.png", "admin/js/calendar.js": "admin/js/calendar.f8a5d055eb33.js", "admin/js/SelectBox.js": "admin/js/SelectBox.8161741c7647.js", "admin/js/urlify.js": "admin/js/urlify.25cc3eac8123.js", "admin/js/popup_response.js": "admin/js/popup_response.c6cc78ea5551.js", "admin/js/autocomplete.js": "admin/js/autocomplete.01591ab27be7.js", "admin/js/collapse.js": "admin/js/collapse.f84e7410290f.js", "admin/js/change_form.js": "admin/js/change_form.9d8ca4f96b75.js", "admin/js/jquery.init.js": "admin/js/jquery.init.b7781a0897fc.js", "admin/js/actions.js": "admin/js/actions.eac7e3441574.js", "admin/js/prepopulate_init.js": "admin/js/prepopulate_init.6cac7f3105b8.js", "admin/js/inlines.js": "admin/js/inlines.22d4d93c00b4.js", "admin/js/prepopulate.js": "admin/js/prepopulate.bd2361dfd64d.js", "admin/js/filters.js": "admin/js/filters.295a9d3d8b6a.js", "admin/js/cancel.js": "admin/js/cancel.ecc4c5ca7b32.js", "admin/js/SelectFilter2.js": "admin/js/SelectFilter2.3f53e33c88d6.js", "admin/js/nav_sidebar.js": "admin/js/nav_sidebar.36a64ecb39ed.js", "admin/js/core.js": "admin/js/core.5d6b384a08b5.js", "admin/css/changelists.css": "admin/css/changelists.ae46354f4e80.css", "admin/css/login.css": "admin/css/login.586129c60a93.css", "admin/css/fonts.css": "admin/css/fonts.168bab448fee.css", "admin/css/responsive.css": "admin/css/responsive.02281633b5f1.css", "admin/css/nav_sidebar.css": "admin/css/nav_sidebar.30423191f399.css", "admin/css/autocomplete.css": "admin/css/autocomplete.4a81fc4242d0.css", "admin/css/base.css": "admin/css/base.01580fff1759.css", "admin/css/widgets.css": "admin/css/widgets.00318bc424d3.css", "admin/css/forms.css": "admin/css/forms.c192d1ec6902.css", "admin/css/dashboard.css": "admin/css/dashboard.be83f13e4369.css", "admin/css/rtl.css": "admin/css/rtl.8473f45bd49b.css", "admin/css/dark_mode.css": "admin/css/dark_mode.4e3d1504ca81.css", "admin/css/responsive_rtl.css": "admin/css/responsive_rtl.e13ae754cceb.css", "admin/fonts/LICENSE.txt": "admin/fonts/LICENSE.d273d63619c9.txt", "admin/fonts/Roboto-Bold-webfont.woff": "admin/fonts/Roboto-Bold-webfont.50d75e48e0a3.woff", "admin/fonts/Roboto-Light-webfont.woff": "admin/fonts/Roboto-Light-webfont.c73eb1ceba33.woff", "admin/fonts/README.txt": "admin/fonts/README.ab99e6b541ea.txt", "admin/fonts/Roboto-Regular-webfont.woff": "admin/fonts/Roboto-Regular-webfont.35b07eb2f871.woff", "admin/img/icon-changelink.svg": "admin/img/icon-changelink.18d2fd706348.svg", "admin/img/icon-no.svg": "admin/img/icon-no.439e821418cd.svg", "admin/img/selector-icons.svg": "admin/img/selector-icons.b4555096cea2.svg", "admin/img/search.svg": "admin/img/search.7cf54ff789c6.svg", "admin/img/icon-yes.svg": "admin/img/icon-yes.d2f9f035226a.svg", "admin/img/icon-addlink.svg": "admin/img/icon-addlink.d519b3bab011.svg", "admin/img/icon-clock.svg": "admin/img/icon-clock.e1d4dfac3f2b.svg", "admin/img/LICENSE": "admin/img/LICENSE.2c54f4e1ca1c", "admin/img/icon-viewlink.svg": "admin/img/icon-viewlink.41eb31f7826e.svg", "admin/img/icon-unknown.svg": "admin/img/icon-unknown.a18cb4398978.svg", "admin/img/README.txt": "admin/img/README.a70711a38d87.txt", "admin/img/icon-calendar.svg": "admin/img/icon-calendar.ac7aea671bea.svg", "admin/img/sorting-icons.svg": "admin/img/sorting-icons.3a097b59f104.svg", "admin/img/tooltag-arrowright.svg": "admin/img/tooltag-arrowright.bbfb788a849e.svg", "admin/img/icon-deletelink.svg": "admin/img/icon-deletelink.564ef9dc3854.svg", "admin/img/tooltag-add.svg": "admin/img/tooltag-add.e59d620a9742.svg", "admin/img/inline-delete.svg": "admin/img/inline-delete.fec1b761f254.svg", "admin/img/icon-alert.svg": "admin/img/icon-alert.034cc7d8a67f.svg", "admin/img/icon-unknown-alt.svg": "admin/img/icon-unknown-alt.81536e128bb6.svg", "admin/img/calendar-icons.svg": "admin/img/calendar-icons.39b290681a8b.svg", "import_export/guess_format.js": "import_export/guess_format.1e929842623e.js", "import_export/action_formats.js": "import_export/action_formats.11c3e817b80a.js", "import_export/import.css": "import_export/import.87299a479910.css"}, "version": "1.0"}
\ No newline at end of file
+{"paths": {"admin/js/vendor/select2/i18n/id.js": "admin/js/vendor/select2/i18n/id.04debded514d.js", "admin/js/vendor/select2/i18n/dsb.js": "admin/js/vendor/select2/i18n/dsb.56372c92d2f1.js", "admin/js/vendor/select2/i18n/mk.js": "admin/js/vendor/select2/i18n/mk.dabbb9087130.js", "admin/js/vendor/select2/i18n/fr.js": "admin/js/vendor/select2/i18n/fr.05e0542fcfe6.js", "admin/js/vendor/select2/i18n/fi.js": "admin/js/vendor/select2/i18n/fi.614ec42aa9ba.js", "admin/js/vendor/select2/i18n/az.js": "admin/js/vendor/select2/i18n/az.270c257daf81.js", "admin/js/vendor/select2/i18n/af.js": "admin/js/vendor/select2/i18n/af.4f6fcd73488c.js", "admin/js/vendor/select2/i18n/sv.js": "admin/js/vendor/select2/i18n/sv.7a9c2f71e777.js", "admin/js/vendor/select2/i18n/ps.js": "admin/js/vendor/select2/i18n/ps.38dfa47af9e0.js", "admin/js/vendor/select2/i18n/ja.js": "admin/js/vendor/select2/i18n/ja.170ae885d74f.js", "admin/js/vendor/select2/i18n/bn.js": "admin/js/vendor/select2/i18n/bn.6d42b4dd5665.js", "admin/js/vendor/select2/i18n/km.js": "admin/js/vendor/select2/i18n/km.c23089cb06ca.js", "admin/js/vendor/select2/i18n/gl.js": "admin/js/vendor/select2/i18n/gl.d99b1fedaa86.js", "admin/js/vendor/select2/i18n/ne.js": "admin/js/vendor/select2/i18n/ne.3d79fd3f08db.js", "admin/js/vendor/select2/i18n/hi.js": "admin/js/vendor/select2/i18n/hi.70640d41628f.js", "admin/js/vendor/select2/i18n/el.js": "admin/js/vendor/select2/i18n/el.27097f071856.js", "admin/js/vendor/select2/i18n/eu.js": "admin/js/vendor/select2/i18n/eu.adfe5c97b72c.js", "admin/js/vendor/select2/i18n/hsb.js": "admin/js/vendor/select2/i18n/hsb.fa3b55265efe.js", "admin/js/vendor/select2/i18n/sq.js": "admin/js/vendor/select2/i18n/sq.5636b60d29c9.js", "admin/js/vendor/select2/i18n/pt-BR.js": "admin/js/vendor/select2/i18n/pt-BR.e1b294433e7f.js", "admin/js/vendor/select2/i18n/tr.js": "admin/js/vendor/select2/i18n/tr.b5a0643d1545.js", "admin/js/vendor/select2/i18n/zh-CN.js": "admin/js/vendor/select2/i18n/zh-CN.2cff662ec5f9.js", "admin/js/vendor/select2/i18n/ko.js": "admin/js/vendor/select2/i18n/ko.e7be6c20e673.js", "admin/js/vendor/select2/i18n/is.js": "admin/js/vendor/select2/i18n/is.3ddd9a6a97e9.js", "admin/js/vendor/select2/i18n/hr.js": "admin/js/vendor/select2/i18n/hr.a2b092cc1147.js", "admin/js/vendor/select2/i18n/vi.js": "admin/js/vendor/select2/i18n/vi.097a5b75b3e1.js", "admin/js/vendor/select2/i18n/lt.js": "admin/js/vendor/select2/i18n/lt.23c7ce903300.js", "admin/js/vendor/select2/i18n/de.js": "admin/js/vendor/select2/i18n/de.8a1c222b0204.js", "admin/js/vendor/select2/i18n/ka.js": "admin/js/vendor/select2/i18n/ka.2083264a54f0.js", "admin/js/vendor/select2/i18n/et.js": "admin/js/vendor/select2/i18n/et.2b96fd98289d.js", "admin/js/vendor/select2/i18n/zh-TW.js": "admin/js/vendor/select2/i18n/zh-TW.04554a227c2b.js", "admin/js/vendor/select2/i18n/lv.js": "admin/js/vendor/select2/i18n/lv.08e62128eac1.js", "admin/js/vendor/select2/i18n/nb.js": "admin/js/vendor/select2/i18n/nb.da2fce143f27.js", "admin/js/vendor/select2/i18n/hy.js": "admin/js/vendor/select2/i18n/hy.c7babaeef5a6.js", "admin/js/vendor/select2/i18n/th.js": "admin/js/vendor/select2/i18n/th.f38c20b0221b.js", "admin/js/vendor/select2/i18n/ms.js": "admin/js/vendor/select2/i18n/ms.4ba82c9a51ce.js", "admin/js/vendor/select2/i18n/bs.js": "admin/js/vendor/select2/i18n/bs.91624382358e.js", "admin/js/vendor/select2/i18n/he.js": "admin/js/vendor/select2/i18n/he.e420ff6cd3ed.js", "admin/js/vendor/select2/i18n/hu.js": "admin/js/vendor/select2/i18n/hu.6ec6039cb8a3.js", "admin/js/vendor/select2/i18n/bg.js": "admin/js/vendor/select2/i18n/bg.39b8be30d4f0.js", "admin/js/vendor/select2/i18n/en.js": "admin/js/vendor/select2/i18n/en.cf932ba09a98.js", "admin/js/vendor/select2/i18n/ar.js": "admin/js/vendor/select2/i18n/ar.65aa8e36bf5d.js", "admin/js/vendor/select2/i18n/pt.js": "admin/js/vendor/select2/i18n/pt.33b4a3b44d43.js", "admin/js/vendor/select2/i18n/cs.js": "admin/js/vendor/select2/i18n/cs.4f43e8e7d33a.js", "admin/js/vendor/select2/i18n/sk.js": "admin/js/vendor/select2/i18n/sk.33d02cef8d11.js", "admin/js/vendor/select2/i18n/tk.js": "admin/js/vendor/select2/i18n/tk.7c572a68c78f.js", "admin/js/vendor/select2/i18n/da.js": "admin/js/vendor/select2/i18n/da.766346afe4dd.js", "admin/js/vendor/select2/i18n/sl.js": "admin/js/vendor/select2/i18n/sl.131a78bc0752.js", "admin/js/vendor/select2/i18n/ro.js": "admin/js/vendor/select2/i18n/ro.f75cb460ec3b.js", "admin/js/vendor/select2/i18n/ru.js": "admin/js/vendor/select2/i18n/ru.934aa95f5b5f.js", "admin/js/vendor/select2/i18n/fa.js": "admin/js/vendor/select2/i18n/fa.3b5bd1961cfd.js", "admin/js/vendor/select2/i18n/es.js": "admin/js/vendor/select2/i18n/es.66dbc2652fb1.js", "admin/js/vendor/select2/i18n/pl.js": "admin/js/vendor/select2/i18n/pl.6031b4f16452.js", "admin/js/vendor/select2/i18n/it.js": "admin/js/vendor/select2/i18n/it.be4fe8d365b5.js", "admin/js/vendor/select2/i18n/uk.js": "admin/js/vendor/select2/i18n/uk.8cede7f4803c.js", "admin/js/vendor/select2/i18n/sr.js": "admin/js/vendor/select2/i18n/sr.5ed85a48f483.js", "admin/js/vendor/select2/i18n/nl.js": "admin/js/vendor/select2/i18n/nl.997868a37ed8.js", "admin/js/vendor/select2/i18n/ca.js": "admin/js/vendor/select2/i18n/ca.a166b745933a.js", "admin/js/vendor/select2/i18n/sr-Cyrl.js": "admin/js/vendor/select2/i18n/sr-Cyrl.f254bb8c4c7c.js", "admin/js/vendor/jquery/jquery.min.js": "admin/js/vendor/jquery/jquery.min.8fb8fee4fcc3.js", "admin/js/vendor/jquery/LICENSE.txt": "admin/js/vendor/jquery/LICENSE.de877aa6d744.txt", "admin/js/vendor/jquery/jquery.js": "admin/js/vendor/jquery/jquery.2849239b95f5.js", "admin/js/vendor/select2/LICENSE.md": "admin/js/vendor/select2/LICENSE.f94142512c91.md", "admin/js/vendor/select2/select2.full.js": "admin/js/vendor/select2/select2.full.c2afdeda3058.js", "admin/js/vendor/select2/select2.full.min.js": "admin/js/vendor/select2/select2.full.min.fcd7500d8e13.js", "admin/js/vendor/xregexp/xregexp.js": "admin/js/vendor/xregexp/xregexp.efda034b9537.js", "admin/js/vendor/xregexp/LICENSE.txt": "admin/js/vendor/xregexp/LICENSE.bf79e414957a.txt", "admin/js/vendor/xregexp/xregexp.min.js": "admin/js/vendor/xregexp/xregexp.min.b0439563a5d3.js", "admin/css/vendor/select2/select2.css": "admin/css/vendor/select2/select2.a2194c262648.css", "admin/css/vendor/select2/select2.min.css": "admin/css/vendor/select2/select2.min.9f54e6414f87.css", "admin/css/vendor/select2/LICENSE-SELECT2.md": "admin/css/vendor/select2/LICENSE-SELECT2.f94142512c91.md", "babybuddy/img/core/child-placeholder.png": "babybuddy/img/core/child-placeholder.7c0a81f0d7f0.png", "rest_framework/docs/js/jquery.json-view.min.js": "rest_framework/docs/js/jquery.json-view.min.b7c2d6981377.js", "rest_framework/docs/js/highlight.pack.js": "rest_framework/docs/js/highlight.pack.479b5f21dcba.js", "rest_framework/docs/js/api.js": "rest_framework/docs/js/api.c9743eab7a4f.js", "rest_framework/docs/img/favicon.ico": "rest_framework/docs/img/favicon.5195b4d0f3eb.ico", "rest_framework/docs/img/grid.png": "rest_framework/docs/img/grid.a4b938cf382b.png", "rest_framework/docs/css/jquery.json-view.min.css": "rest_framework/docs/css/jquery.json-view.min.a2e6beeb6710.css", "rest_framework/docs/css/highlight.css": "rest_framework/docs/css/highlight.e0e4d973c6d7.css", "rest_framework/docs/css/base.css": "rest_framework/docs/css/base.e630f8f4990e.css", "admin/js/admin/RelatedObjectLookups.js": "admin/js/admin/RelatedObjectLookups.b4d76b6aaf0b.js", "admin/js/admin/DateTimeShortcuts.js": "admin/js/admin/DateTimeShortcuts.5548f99471bf.js", "admin/img/gis/move_vertex_on.svg": "admin/img/gis/move_vertex_on.0047eba25b67.svg", "admin/img/gis/move_vertex_off.svg": "admin/img/gis/move_vertex_off.7a23bf31ef8a.svg", "babybuddy/font/babybuddy.ttf": "babybuddy/font/babybuddy.b6a356bd9752.ttf", "babybuddy/font/babybuddy.eot": "babybuddy/font/babybuddy.ae7049e685dd.eot", "babybuddy/font/babybuddy.woff2": "babybuddy/font/babybuddy.998e9bc52faf.woff2", "babybuddy/font/babybuddy.woff": "babybuddy/font/babybuddy.1913791605fb.woff", "babybuddy/font/babybuddy.svg": "babybuddy/font/babybuddy.ef799e0dd5c7.svg", "babybuddy/logo/icon.png": "babybuddy/logo/icon.df80640f0465.png", "babybuddy/logo/icon-brand.png": "babybuddy/logo/icon-brand.32cbedf6aee3.png", "babybuddy/logo/logo.png": "babybuddy/logo/logo.62870041cc83.png", "babybuddy/logo/logo-sad.png": "babybuddy/logo/logo-sad.47c3d5c2d397.png", "babybuddy/root/favicon.ico": "babybuddy/root/favicon.ee5ebcd40fb9.ico", "babybuddy/root/mstile-150x150.png": "babybuddy/root/mstile-150x150.08524a406cf2.png", "babybuddy/root/favicon.svg": "babybuddy/root/favicon.12fe726d0bac.svg", "babybuddy/root/site.webmanifest": "babybuddy/root/site.c6c4158e40df.webmanifest", "babybuddy/root/browserconfig.xml": "babybuddy/root/browserconfig.84708aade0e5.xml", "babybuddy/root/safari-pinned-tab.svg": "babybuddy/root/safari-pinned-tab.e8c8ac2f55f5.svg", "babybuddy/root/apple-touch-startup-image.png": "babybuddy/root/apple-touch-startup-image.749726217484.png", "babybuddy/root/android-chrome-192x192.png": "babybuddy/root/android-chrome-192x192.ac7d2baba4df.png", "babybuddy/root/android-chrome-512x512.png": "babybuddy/root/android-chrome-512x512.e1fd38ad828c.png", "babybuddy/root/apple-touch-icon.png": "babybuddy/root/apple-touch-icon.bdc75cec89fa.png", "babybuddy/js/vendor.js": "babybuddy/js/vendor.d42b27d2c123.js", "babybuddy/js/tags_editor.js": "babybuddy/js/tags_editor.07f7b7a22846.js", "babybuddy/js/graph.js": "babybuddy/js/graph.5f9828355633.js", "babybuddy/js/app.js": "babybuddy/js/app.d20500d757a5.js", "babybuddy/css/app.css": "babybuddy/css/app.531dd9e6521f.css", "rest_framework/js/jquery-3.5.1.min.js": "rest_framework/js/jquery-3.5.1.min.dc5e7f18c8d3.js", "rest_framework/js/bootstrap.min.js": "rest_framework/js/bootstrap.min.2f34b630ffe3.js", "rest_framework/js/default.js": "rest_framework/js/default.5b08897dbdc3.js", "rest_framework/js/prettify-min.js": "rest_framework/js/prettify-min.709bfcc456c6.js", "rest_framework/js/coreapi-0.1.1.js": "rest_framework/js/coreapi-0.1.1.e580e3854595.js", "rest_framework/js/ajax-form.js": "rest_framework/js/ajax-form.0ea6e6052ab5.js", "rest_framework/js/csrf.js": "rest_framework/js/csrf.969930007329.js", "rest_framework/fonts/glyphicons-halflings-regular.svg": "rest_framework/fonts/glyphicons-halflings-regular.08eda92397ae.svg", "rest_framework/fonts/fontawesome-webfont.eot": "rest_framework/fonts/fontawesome-webfont.8b27bc96115c.eot", "rest_framework/fonts/glyphicons-halflings-regular.woff": "rest_framework/fonts/glyphicons-halflings-regular.fa2772327f55.woff", "rest_framework/fonts/fontawesome-webfont.ttf": "rest_framework/fonts/fontawesome-webfont.dcb26c7239d8.ttf", "rest_framework/fonts/fontawesome-webfont.svg": "rest_framework/fonts/fontawesome-webfont.83e37a11f9d7.svg", "rest_framework/fonts/glyphicons-halflings-regular.ttf": "rest_framework/fonts/glyphicons-halflings-regular.e18bbf611f2a.ttf", "rest_framework/fonts/glyphicons-halflings-regular.woff2": "rest_framework/fonts/glyphicons-halflings-regular.448c34a56d69.woff2", "rest_framework/fonts/glyphicons-halflings-regular.eot": "rest_framework/fonts/glyphicons-halflings-regular.f4769f9bdb74.eot", "rest_framework/fonts/fontawesome-webfont.woff": "rest_framework/fonts/fontawesome-webfont.3293616ec0c6.woff", "rest_framework/img/glyphicons-halflings-white.png": "rest_framework/img/glyphicons-halflings-white.9bbc6e960299.png", "rest_framework/img/glyphicons-halflings.png": "rest_framework/img/glyphicons-halflings.90233c9067e9.png", "rest_framework/img/grid.png": "rest_framework/img/grid.a4b938cf382b.png", "rest_framework/css/bootstrap-theme.min.css": "rest_framework/css/bootstrap-theme.min.66b84a04375e.css", "rest_framework/css/bootstrap.min.css": "rest_framework/css/bootstrap.min.77017a69879a.css", "rest_framework/css/default.css": "rest_framework/css/default.789dfb5732d7.css", "rest_framework/css/font-awesome-4.0.3.css": "rest_framework/css/font-awesome-4.0.3.c1e1ea213abf.css", "rest_framework/css/prettify.css": "rest_framework/css/prettify.a987f72342ee.css", "rest_framework/css/bootstrap-tweaks.css": "rest_framework/css/bootstrap-tweaks.46ed116b0edd.css", "admin/js/calendar.js": "admin/js/calendar.f8a5d055eb33.js", "admin/js/cancel.js": "admin/js/cancel.ecc4c5ca7b32.js", "admin/js/SelectBox.js": "admin/js/SelectBox.8161741c7647.js", "admin/js/change_form.js": "admin/js/change_form.9d8ca4f96b75.js", "admin/js/prepopulate_init.js": "admin/js/prepopulate_init.e056047b7a7e.js", "admin/js/jquery.init.js": "admin/js/jquery.init.b7781a0897fc.js", "admin/js/autocomplete.js": "admin/js/autocomplete.c508b167ab61.js", "admin/js/collapse.js": "admin/js/collapse.f84e7410290f.js", "admin/js/prepopulate.js": "admin/js/prepopulate.bd2361dfd64d.js", "admin/js/inlines.js": "admin/js/inlines.fb1617228dbe.js", "admin/js/nav_sidebar.js": "admin/js/nav_sidebar.36a64ecb39ed.js", "admin/js/popup_response.js": "admin/js/popup_response.c6cc78ea5551.js", "admin/js/SelectFilter2.js": "admin/js/SelectFilter2.d250dcb52a9a.js", "admin/js/actions.js": "admin/js/actions.eac7e3441574.js", "admin/js/core.js": "admin/js/core.5d6b384a08b5.js", "admin/js/urlify.js": "admin/js/urlify.25cc3eac8123.js", "admin/fonts/Roboto-Regular-webfont.woff": "admin/fonts/Roboto-Regular-webfont.35b07eb2f871.woff", "admin/fonts/Roboto-Bold-webfont.woff": "admin/fonts/Roboto-Bold-webfont.50d75e48e0a3.woff", "admin/fonts/LICENSE.txt": "admin/fonts/LICENSE.d273d63619c9.txt", "admin/fonts/Roboto-Light-webfont.woff": "admin/fonts/Roboto-Light-webfont.c73eb1ceba33.woff", "admin/fonts/README.txt": "admin/fonts/README.ab99e6b541ea.txt", "admin/img/icon-unknown.svg": "admin/img/icon-unknown.a18cb4398978.svg", "admin/img/icon-no.svg": "admin/img/icon-no.439e821418cd.svg", "admin/img/selector-icons.svg": "admin/img/selector-icons.b4555096cea2.svg", "admin/img/icon-addlink.svg": "admin/img/icon-addlink.d519b3bab011.svg", "admin/img/icon-changelink.svg": "admin/img/icon-changelink.18d2fd706348.svg", "admin/img/search.svg": "admin/img/search.7cf54ff789c6.svg", "admin/img/icon-alert.svg": "admin/img/icon-alert.034cc7d8a67f.svg", "admin/img/icon-yes.svg": "admin/img/icon-yes.d2f9f035226a.svg", "admin/img/icon-calendar.svg": "admin/img/icon-calendar.ac7aea671bea.svg", "admin/img/calendar-icons.svg": "admin/img/calendar-icons.39b290681a8b.svg", "admin/img/LICENSE": "admin/img/LICENSE.2c54f4e1ca1c", "admin/img/sorting-icons.svg": "admin/img/sorting-icons.3a097b59f104.svg", "admin/img/icon-deletelink.svg": "admin/img/icon-deletelink.564ef9dc3854.svg", "admin/img/icon-clock.svg": "admin/img/icon-clock.e1d4dfac3f2b.svg", "admin/img/tooltag-add.svg": "admin/img/tooltag-add.e59d620a9742.svg", "admin/img/inline-delete.svg": "admin/img/inline-delete.fec1b761f254.svg", "admin/img/icon-unknown-alt.svg": "admin/img/icon-unknown-alt.81536e128bb6.svg", "admin/img/README.txt": "admin/img/README.a70711a38d87.txt", "admin/img/icon-viewlink.svg": "admin/img/icon-viewlink.41eb31f7826e.svg", "admin/img/tooltag-arrowright.svg": "admin/img/tooltag-arrowright.bbfb788a849e.svg", "admin/css/nav_sidebar.css": "admin/css/nav_sidebar.e32d345464bd.css", "admin/css/widgets.css": "admin/css/widgets.694d845b2cb1.css", "admin/css/fonts.css": "admin/css/fonts.168bab448fee.css", "admin/css/forms.css": "admin/css/forms.332ab41432e2.css", "admin/css/dashboard.css": "admin/css/dashboard.be83f13e4369.css", "admin/css/responsive.css": "admin/css/responsive.b9e1565b3609.css", "admin/css/autocomplete.css": "admin/css/autocomplete.4a81fc4242d0.css", "admin/css/responsive_rtl.css": "admin/css/responsive_rtl.e13ae754cceb.css", "admin/css/changelists.css": "admin/css/changelists.cd4dd90ae1a1.css", "admin/css/base.css": "admin/css/base.1f418065fc2c.css", "admin/css/login.css": "admin/css/login.8b76a9f7cbf6.css", "admin/css/rtl.css": "admin/css/rtl.4bc23eb90919.css", "import_export/import.css": "import_export/import.87299a479910.css", "import_export/action_formats.js": "import_export/action_formats.11c3e817b80a.js"}, "version": "1.0"}
\ No newline at end of file