mirror of https://github.com/snachodog/mybuddy.git
Format code with black
This commit is contained in:
parent
bf9bd4dd8a
commit
cd946280cb
|
@ -6,64 +6,59 @@ from core import models
|
|||
class ChildFieldFilter(filters.FilterSet):
|
||||
class Meta:
|
||||
abstract = True
|
||||
fields = ['child']
|
||||
fields = ["child"]
|
||||
|
||||
|
||||
class TimeFieldFilter(ChildFieldFilter):
|
||||
date = filters.DateFilter(field_name='time__date', label='Date')
|
||||
date_max = filters.DateFilter(field_name='time__date', label='Max. Date',
|
||||
lookup_expr='lte')
|
||||
date_min = filters.DateFilter(field_name='time__date', label='Min. Date',
|
||||
lookup_expr='gte')
|
||||
date = filters.DateFilter(field_name="time__date", label="Date")
|
||||
date_max = filters.DateFilter(
|
||||
field_name="time__date", label="Max. Date", lookup_expr="lte"
|
||||
)
|
||||
date_min = filters.DateFilter(
|
||||
field_name="time__date", label="Min. Date", lookup_expr="gte"
|
||||
)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
fields = sorted(ChildFieldFilter.Meta.fields + [
|
||||
'date',
|
||||
'date_max',
|
||||
'date_min'
|
||||
])
|
||||
fields = sorted(ChildFieldFilter.Meta.fields + ["date", "date_max", "date_min"])
|
||||
|
||||
|
||||
class StartEndFieldFilter(ChildFieldFilter):
|
||||
end = filters.DateFilter(field_name='end__date', label='End Date')
|
||||
end_max = filters.DateFilter(field_name='end__date', label='Max. End Date',
|
||||
lookup_expr='lte')
|
||||
end_min = filters.DateFilter(field_name='end__date', label='Min. End Date',
|
||||
lookup_expr='gte')
|
||||
start = filters.DateFilter(field_name='start__date', label='Start Date')
|
||||
start_max = filters.DateFilter(field_name='start__date', lookup_expr='lte',
|
||||
label='Max. End Date')
|
||||
start_min = filters.DateFilter(field_name='start__date', lookup_expr='gte',
|
||||
label='Min. Start Date')
|
||||
end = filters.DateFilter(field_name="end__date", label="End Date")
|
||||
end_max = filters.DateFilter(
|
||||
field_name="end__date", label="Max. End Date", lookup_expr="lte"
|
||||
)
|
||||
end_min = filters.DateFilter(
|
||||
field_name="end__date", label="Min. End Date", lookup_expr="gte"
|
||||
)
|
||||
start = filters.DateFilter(field_name="start__date", label="Start Date")
|
||||
start_max = filters.DateFilter(
|
||||
field_name="start__date", lookup_expr="lte", label="Max. End Date"
|
||||
)
|
||||
start_min = filters.DateFilter(
|
||||
field_name="start__date", lookup_expr="gte", label="Min. Start Date"
|
||||
)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
fields = sorted(ChildFieldFilter.Meta.fields + [
|
||||
'end',
|
||||
'end_max',
|
||||
'end_min',
|
||||
'start',
|
||||
'start_max',
|
||||
'start_min'
|
||||
])
|
||||
fields = sorted(
|
||||
ChildFieldFilter.Meta.fields
|
||||
+ ["end", "end_max", "end_min", "start", "start_max", "start_min"]
|
||||
)
|
||||
|
||||
|
||||
class DiaperChangeFilter(TimeFieldFilter):
|
||||
class Meta(TimeFieldFilter.Meta):
|
||||
model = models.DiaperChange
|
||||
fields = sorted(TimeFieldFilter.Meta.fields + [
|
||||
'wet',
|
||||
'solid',
|
||||
'color',
|
||||
'amount'
|
||||
])
|
||||
fields = sorted(
|
||||
TimeFieldFilter.Meta.fields + ["wet", "solid", "color", "amount"]
|
||||
)
|
||||
|
||||
|
||||
class FeedingFilter(StartEndFieldFilter):
|
||||
class Meta(StartEndFieldFilter.Meta):
|
||||
model = models.Feeding
|
||||
fields = sorted(StartEndFieldFilter.Meta.fields + ['type', 'method'])
|
||||
fields = sorted(StartEndFieldFilter.Meta.fields + ["type", "method"])
|
||||
|
||||
|
||||
class NoteFilter(TimeFieldFilter):
|
||||
|
@ -84,7 +79,7 @@ class TemperatureFilter(TimeFieldFilter):
|
|||
class TimerFilter(StartEndFieldFilter):
|
||||
class Meta(StartEndFieldFilter.Meta):
|
||||
model = models.Timer
|
||||
fields = sorted(StartEndFieldFilter.Meta.fields + ['active', 'user'])
|
||||
fields = sorted(StartEndFieldFilter.Meta.fields + ["active", "user"])
|
||||
|
||||
|
||||
class TummyTimeFilter(StartEndFieldFilter):
|
||||
|
|
|
@ -6,11 +6,12 @@ class APIMetadata(metadata.SimpleMetadata):
|
|||
"""
|
||||
Custom metadata class for OPTIONS responses.
|
||||
"""
|
||||
|
||||
def determine_metadata(self, request, view):
|
||||
data = super(APIMetadata, self).determine_metadata(request, view)
|
||||
data.pop('description')
|
||||
if hasattr(view, 'filterset_fields'):
|
||||
data.update({'filters': view.filterset_fields})
|
||||
elif hasattr(view, 'filterset_class'):
|
||||
data.update({'filters': view.filterset_class.Meta.fields})
|
||||
data.pop("description")
|
||||
if hasattr(view, "filterset_fields"):
|
||||
data.update({"filters": view.filterset_fields})
|
||||
elif hasattr(view, "filterset_class"):
|
||||
data.update({"filters": view.filterset_class.Meta.fields})
|
||||
return data
|
||||
|
|
|
@ -11,17 +11,19 @@ class TimerFieldSupportMixin:
|
|||
"""
|
||||
meta = self.metadata_class()
|
||||
data = meta.determine_metadata(request, self)
|
||||
post = data.get('actions').get('POST') # type: OrderedDict
|
||||
post['timer'] = OrderedDict({
|
||||
post = data.get("actions").get("POST") # type: OrderedDict
|
||||
post["timer"] = OrderedDict(
|
||||
{
|
||||
"type": "integer",
|
||||
"required": False,
|
||||
"read_only": False,
|
||||
"label": "Timer",
|
||||
"details": "ID for an existing Timer, may be used in place of the "
|
||||
"`start`, `end`, and/or `child` fields. "
|
||||
})
|
||||
"`start`, `end`, and/or `child` fields. ",
|
||||
}
|
||||
)
|
||||
details = "Required unless a value is provided in the `timer` field."
|
||||
post['child']['details'] = details
|
||||
post['start']['details'] = details
|
||||
post['end']['details'] = details
|
||||
post["child"]["details"] = details
|
||||
post["start"]["details"] = details
|
||||
post["end"]["details"] = details
|
||||
return Response(data)
|
||||
|
|
|
@ -4,11 +4,11 @@ from rest_framework.permissions import DjangoModelPermissions
|
|||
|
||||
class BabyBuddyDjangoModelPermissions(DjangoModelPermissions):
|
||||
perms_map = {
|
||||
'GET': ['%(app_label)s.view_%(model_name)s'],
|
||||
'OPTIONS': ['%(app_label)s.add_%(model_name)s'],
|
||||
'HEAD': [],
|
||||
'POST': ['%(app_label)s.add_%(model_name)s'],
|
||||
"GET": ["%(app_label)s.view_%(model_name)s"],
|
||||
"OPTIONS": ["%(app_label)s.add_%(model_name)s"],
|
||||
"HEAD": [],
|
||||
"POST": ["%(app_label)s.add_%(model_name)s"],
|
||||
# 'PUT': ['%(app_label)s.change_%(model_name)s'],
|
||||
'PATCH': ['%(app_label)s.change_%(model_name)s'],
|
||||
'DELETE': ['%(app_label)s.delete_%(model_name)s'],
|
||||
"PATCH": ["%(app_label)s.change_%(model_name)s"],
|
||||
"DELETE": ["%(app_label)s.delete_%(model_name)s"],
|
||||
}
|
||||
|
|
|
@ -14,8 +14,8 @@ class CoreModelSerializer(serializers.HyperlinkedModelSerializer):
|
|||
Provide the child link (used by most core models) and run model clean()
|
||||
methods during POST operations.
|
||||
"""
|
||||
child = serializers.PrimaryKeyRelatedField(
|
||||
queryset=models.Child.objects.all())
|
||||
|
||||
child = serializers.PrimaryKeyRelatedField(queryset=models.Child.objects.all())
|
||||
|
||||
def validate(self, attrs):
|
||||
# Ensure that all instance data is available for partial updates to
|
||||
|
@ -34,15 +34,19 @@ class CoreModelWithDurationSerializer(CoreModelSerializer):
|
|||
"""
|
||||
Specific serializer base for models with a "start" and "end" field.
|
||||
"""
|
||||
|
||||
child = serializers.PrimaryKeyRelatedField(
|
||||
allow_null=True, allow_empty=True, queryset=models.Child.objects.all(),
|
||||
required=False)
|
||||
allow_null=True,
|
||||
allow_empty=True,
|
||||
queryset=models.Child.objects.all(),
|
||||
required=False,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
extra_kwargs = {
|
||||
'start': {'required': False},
|
||||
'end': {'required': False},
|
||||
"start": {"required": False},
|
||||
"end": {"required": False},
|
||||
}
|
||||
|
||||
def validate(self, attrs):
|
||||
|
@ -50,30 +54,30 @@ class CoreModelWithDurationSerializer(CoreModelSerializer):
|
|||
# of "start" and "end" fields as well as "child" if it is set on the
|
||||
# Timer entry.
|
||||
timer = None
|
||||
if 'timer' in self.initial_data:
|
||||
if "timer" in self.initial_data:
|
||||
try:
|
||||
timer = models.Timer.objects.get(pk=self.initial_data['timer'])
|
||||
timer = models.Timer.objects.get(pk=self.initial_data["timer"])
|
||||
except models.Timer.DoesNotExist:
|
||||
raise ValidationError({'timer': ['Timer does not exist.']})
|
||||
raise ValidationError({"timer": ["Timer does not exist."]})
|
||||
if timer.end:
|
||||
end = timer.end
|
||||
else:
|
||||
end = timezone.now()
|
||||
if timer.child:
|
||||
attrs['child'] = timer.child
|
||||
attrs["child"] = timer.child
|
||||
|
||||
# Overwrites values provided directly!
|
||||
attrs['start'] = timer.start
|
||||
attrs['end'] = end
|
||||
attrs["start"] = timer.start
|
||||
attrs["end"] = end
|
||||
|
||||
# The "child", "start", and "end" field should all be set at this
|
||||
# point. If one is not, model validation will fail because they are
|
||||
# required fields at the model level.
|
||||
if not self.partial:
|
||||
errors = {}
|
||||
for field in ['child', 'start', 'end']:
|
||||
for field in ["child", "start", "end"]:
|
||||
if field not in attrs or not attrs[field]:
|
||||
errors[field] = 'This field is required.'
|
||||
errors[field] = "This field is required."
|
||||
if len(errors) > 0:
|
||||
raise ValidationError(errors)
|
||||
|
||||
|
@ -81,7 +85,7 @@ class CoreModelWithDurationSerializer(CoreModelSerializer):
|
|||
|
||||
# Only actually stop the timer if all validation passed.
|
||||
if timer:
|
||||
timer.stop(attrs['end'])
|
||||
timer.stop(attrs["end"])
|
||||
|
||||
return attrs
|
||||
|
||||
|
@ -89,76 +93,77 @@ class CoreModelWithDurationSerializer(CoreModelSerializer):
|
|||
class UserSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ('id', 'username')
|
||||
fields = ("id", "username")
|
||||
|
||||
|
||||
class ChildSerializer(serializers.HyperlinkedModelSerializer):
|
||||
class Meta:
|
||||
model = models.Child
|
||||
fields = ('id', 'first_name', 'last_name', 'birth_date', 'slug',
|
||||
'picture')
|
||||
lookup_field = 'slug'
|
||||
fields = ("id", "first_name", "last_name", "birth_date", "slug", "picture")
|
||||
lookup_field = "slug"
|
||||
|
||||
|
||||
class DiaperChangeSerializer(CoreModelSerializer):
|
||||
class Meta:
|
||||
model = models.DiaperChange
|
||||
fields = (
|
||||
'id',
|
||||
'child',
|
||||
'time',
|
||||
'wet',
|
||||
'solid',
|
||||
'color',
|
||||
'amount',
|
||||
'notes'
|
||||
)
|
||||
fields = ("id", "child", "time", "wet", "solid", "color", "amount", "notes")
|
||||
|
||||
|
||||
class FeedingSerializer(CoreModelWithDurationSerializer):
|
||||
class Meta(CoreModelWithDurationSerializer.Meta):
|
||||
model = models.Feeding
|
||||
fields = ('id', 'child', 'start', 'end', 'duration', 'type', 'method',
|
||||
'amount', 'notes')
|
||||
fields = (
|
||||
"id",
|
||||
"child",
|
||||
"start",
|
||||
"end",
|
||||
"duration",
|
||||
"type",
|
||||
"method",
|
||||
"amount",
|
||||
"notes",
|
||||
)
|
||||
|
||||
|
||||
class NoteSerializer(CoreModelSerializer):
|
||||
class Meta:
|
||||
model = models.Note
|
||||
fields = ('id', 'child', 'note', 'time')
|
||||
fields = ("id", "child", "note", "time")
|
||||
|
||||
|
||||
class SleepSerializer(CoreModelWithDurationSerializer):
|
||||
class Meta(CoreModelWithDurationSerializer.Meta):
|
||||
model = models.Sleep
|
||||
fields = ('id', 'child', 'start', 'end', 'duration', 'nap', 'notes')
|
||||
fields = ("id", "child", "start", "end", "duration", "nap", "notes")
|
||||
|
||||
|
||||
class TemperatureSerializer(CoreModelSerializer):
|
||||
class Meta:
|
||||
model = models.Temperature
|
||||
fields = ('id', 'child', 'temperature', 'time', 'notes')
|
||||
fields = ("id", "child", "temperature", "time", "notes")
|
||||
|
||||
|
||||
class TimerSerializer(CoreModelSerializer):
|
||||
child = serializers.PrimaryKeyRelatedField(
|
||||
allow_null=True, allow_empty=True, queryset=models.Child.objects.all(),
|
||||
required=False)
|
||||
allow_null=True,
|
||||
allow_empty=True,
|
||||
queryset=models.Child.objects.all(),
|
||||
required=False,
|
||||
)
|
||||
user = serializers.PrimaryKeyRelatedField(
|
||||
allow_null=True, allow_empty=True, queryset=User.objects.all(),
|
||||
required=False)
|
||||
allow_null=True, allow_empty=True, queryset=User.objects.all(), required=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = models.Timer
|
||||
fields = ('id', 'child', 'name', 'start', 'end', 'duration', 'active',
|
||||
'user')
|
||||
fields = ("id", "child", "name", "start", "end", "duration", "active", "user")
|
||||
|
||||
def validate(self, attrs):
|
||||
attrs = super(TimerSerializer, self).validate(attrs)
|
||||
|
||||
# Set user to current user if no value is provided.
|
||||
if 'user' not in attrs or attrs['user'] is None:
|
||||
attrs['user'] = self.context['request'].user
|
||||
if "user" not in attrs or attrs["user"] is None:
|
||||
attrs["user"] = self.context["request"].user
|
||||
|
||||
return attrs
|
||||
|
||||
|
@ -166,28 +171,28 @@ class TimerSerializer(CoreModelSerializer):
|
|||
class TummyTimeSerializer(CoreModelWithDurationSerializer):
|
||||
class Meta(CoreModelWithDurationSerializer.Meta):
|
||||
model = models.TummyTime
|
||||
fields = ('id', 'child', 'start', 'end', 'duration', 'milestone')
|
||||
fields = ("id", "child", "start", "end", "duration", "milestone")
|
||||
|
||||
|
||||
class WeightSerializer(CoreModelSerializer):
|
||||
class Meta:
|
||||
model = models.Weight
|
||||
fields = ('id', 'child', 'weight', 'date', 'notes')
|
||||
fields = ("id", "child", "weight", "date", "notes")
|
||||
|
||||
|
||||
class HeightSerializer(CoreModelSerializer):
|
||||
class Meta:
|
||||
model = models.Height
|
||||
fields = ('id', 'child', 'height', 'date', 'notes')
|
||||
fields = ("id", "child", "height", "date", "notes")
|
||||
|
||||
|
||||
class HeadCircumferenceSerializer(CoreModelSerializer):
|
||||
class Meta:
|
||||
model = models.HeadCircumference
|
||||
fields = ('id', 'child', 'head_circumference', 'date', 'notes')
|
||||
fields = ("id", "child", "head_circumference", "date", "notes")
|
||||
|
||||
|
||||
class BMISerializer(CoreModelSerializer):
|
||||
class Meta:
|
||||
model = models.BMI
|
||||
fields = ('id', 'child', 'bmi', 'date', 'notes')
|
||||
fields = ("id", "child", "bmi", "date", "notes")
|
||||
|
|
527
api/tests.py
527
api/tests.py
|
@ -10,25 +10,25 @@ from core import models
|
|||
|
||||
|
||||
class TestBase:
|
||||
|
||||
class BabyBuddyAPITestCaseBase(APITestCase):
|
||||
fixtures = ['tests.json']
|
||||
fixtures = ["tests.json"]
|
||||
model = None
|
||||
endpoint = None
|
||||
delete_id = 1
|
||||
timer_test_data = {}
|
||||
|
||||
def setUp(self):
|
||||
self.client.login(username='admin', password='admin')
|
||||
self.client.login(username="admin", password="admin")
|
||||
|
||||
def test_options(self):
|
||||
response = self.client.options(self.endpoint)
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(response.data['name'], '{} List'.format(
|
||||
self.model._meta.verbose_name))
|
||||
self.assertEqual(
|
||||
response.data["name"], "{} List".format(self.model._meta.verbose_name)
|
||||
)
|
||||
|
||||
def test_delete(self):
|
||||
endpoint = '{}{}/'.format(self.endpoint, self.delete_id)
|
||||
endpoint = "{}{}/".format(self.endpoint, self.delete_id)
|
||||
response = self.client.get(endpoint)
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
response = self.client.delete(endpoint)
|
||||
|
@ -40,24 +40,26 @@ class TestBase:
|
|||
user = User.objects.first()
|
||||
start = timezone.now() - timezone.timedelta(minutes=10)
|
||||
timer = models.Timer.objects.create(user=user, start=start)
|
||||
self.timer_test_data['timer'] = timer.id
|
||||
self.timer_test_data["timer"] = timer.id
|
||||
|
||||
if 'child' in self.timer_test_data:
|
||||
del self.timer_test_data['child']
|
||||
if "child" in self.timer_test_data:
|
||||
del self.timer_test_data["child"]
|
||||
response = self.client.post(
|
||||
self.endpoint, self.timer_test_data, format='json')
|
||||
self.endpoint, self.timer_test_data, format="json"
|
||||
)
|
||||
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
|
||||
self.timer_test_data["child"] = child.id
|
||||
response = self.client.post(
|
||||
self.endpoint, self.timer_test_data, format='json')
|
||||
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'])
|
||||
obj = self.model.objects.get(pk=response.data["id"])
|
||||
self.assertEqual(obj.start, start)
|
||||
self.assertEqual(obj.end, timer.end)
|
||||
|
||||
|
@ -67,339 +69,374 @@ class TestBase:
|
|||
user = User.objects.first()
|
||||
child = models.Child.objects.first()
|
||||
start = timezone.now() - timezone.timedelta(minutes=10)
|
||||
timer = models.Timer.objects.create(
|
||||
user=user, child=child, start=start)
|
||||
self.timer_test_data['timer'] = timer.id
|
||||
timer = models.Timer.objects.create(user=user, child=child, start=start)
|
||||
self.timer_test_data["timer"] = timer.id
|
||||
response = self.client.post(
|
||||
self.endpoint, self.timer_test_data, format='json')
|
||||
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'])
|
||||
obj = self.model.objects.get(pk=response.data["id"])
|
||||
self.assertEqual(obj.child, timer.child)
|
||||
self.assertEqual(obj.start, start)
|
||||
self.assertEqual(obj.end, timer.end)
|
||||
|
||||
|
||||
class ChildAPITestCase(TestBase.BabyBuddyAPITestCaseBase):
|
||||
endpoint = reverse('api:child-list')
|
||||
endpoint = reverse("api:child-list")
|
||||
model = models.Child
|
||||
delete_id = 'fake-child'
|
||||
delete_id = "fake-child"
|
||||
|
||||
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,
|
||||
'first_name': 'Fake',
|
||||
'last_name': 'Child',
|
||||
'birth_date': '2017-11-11',
|
||||
'slug': 'fake-child',
|
||||
'picture': None
|
||||
})
|
||||
self.assertEqual(
|
||||
response.data["results"][0],
|
||||
{
|
||||
"id": 1,
|
||||
"first_name": "Fake",
|
||||
"last_name": "Child",
|
||||
"birth_date": "2017-11-11",
|
||||
"slug": "fake-child",
|
||||
"picture": None,
|
||||
},
|
||||
)
|
||||
|
||||
def test_post(self):
|
||||
data = {
|
||||
'first_name': 'Test',
|
||||
'last_name': 'Child',
|
||||
'birth_date': '2017-11-12'
|
||||
}
|
||||
response = self.client.post(self.endpoint, data, format='json')
|
||||
data = {"first_name": "Test", "last_name": "Child", "birth_date": "2017-11-12"}
|
||||
response = self.client.post(self.endpoint, data, format="json")
|
||||
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
||||
obj = models.Child.objects.get(pk=response.data['id'])
|
||||
self.assertEqual(obj.first_name, data['first_name'])
|
||||
obj = models.Child.objects.get(pk=response.data["id"])
|
||||
self.assertEqual(obj.first_name, data["first_name"])
|
||||
|
||||
def test_patch(self):
|
||||
endpoint = '{}{}/'.format(self.endpoint, 'fake-child')
|
||||
endpoint = "{}{}/".format(self.endpoint, "fake-child")
|
||||
response = self.client.get(endpoint)
|
||||
entry = response.data
|
||||
entry['first_name'] = 'New'
|
||||
entry['last_name'] = 'Name'
|
||||
response = self.client.patch(endpoint, {
|
||||
'first_name': entry['first_name'],
|
||||
'last_name': entry['last_name'],
|
||||
})
|
||||
entry["first_name"] = "New"
|
||||
entry["last_name"] = "Name"
|
||||
response = self.client.patch(
|
||||
endpoint,
|
||||
{
|
||||
"first_name": entry["first_name"],
|
||||
"last_name": entry["last_name"],
|
||||
},
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
# The slug we be updated by the name change.
|
||||
entry['slug'] = 'new-name'
|
||||
entry["slug"] = "new-name"
|
||||
self.assertEqual(response.data, entry)
|
||||
|
||||
|
||||
class DiaperChangeAPITestCase(TestBase.BabyBuddyAPITestCaseBase):
|
||||
endpoint = reverse('api:diaperchange-list')
|
||||
endpoint = reverse("api:diaperchange-list")
|
||||
model = models.DiaperChange
|
||||
delete_id = 3
|
||||
|
||||
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': 3,
|
||||
'child': 1,
|
||||
'time': '2017-11-18T14:00:00-05:00',
|
||||
'wet': True,
|
||||
'solid': False,
|
||||
'color': '',
|
||||
'amount': 2.25,
|
||||
'notes': 'stinky'
|
||||
})
|
||||
self.assertEqual(
|
||||
response.data["results"][0],
|
||||
{
|
||||
"id": 3,
|
||||
"child": 1,
|
||||
"time": "2017-11-18T14:00:00-05:00",
|
||||
"wet": True,
|
||||
"solid": False,
|
||||
"color": "",
|
||||
"amount": 2.25,
|
||||
"notes": "stinky",
|
||||
},
|
||||
)
|
||||
|
||||
def test_post(self):
|
||||
data = {
|
||||
'child': 1,
|
||||
'time': '2017-11-18T12:00:00-05:00',
|
||||
'wet': True,
|
||||
'solid': True,
|
||||
'color': 'brown',
|
||||
'amount': 1.25,
|
||||
'notes': 'seedy'
|
||||
"child": 1,
|
||||
"time": "2017-11-18T12:00:00-05:00",
|
||||
"wet": True,
|
||||
"solid": True,
|
||||
"color": "brown",
|
||||
"amount": 1.25,
|
||||
"notes": "seedy",
|
||||
}
|
||||
response = self.client.post(self.endpoint, data, format='json')
|
||||
response = self.client.post(self.endpoint, data, format="json")
|
||||
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
||||
obj = models.DiaperChange.objects.get(pk=response.data['id'])
|
||||
obj = models.DiaperChange.objects.get(pk=response.data["id"])
|
||||
self.assertTrue(obj.wet)
|
||||
self.assertTrue(obj.solid)
|
||||
self.assertEqual(obj.color, data['color'])
|
||||
self.assertEqual(obj.amount, data['amount'])
|
||||
self.assertEqual(obj.notes, data['notes'])
|
||||
self.assertEqual(obj.color, data["color"])
|
||||
self.assertEqual(obj.amount, data["amount"])
|
||||
self.assertEqual(obj.notes, data["notes"])
|
||||
|
||||
def test_patch(self):
|
||||
endpoint = '{}{}/'.format(self.endpoint, 3)
|
||||
endpoint = "{}{}/".format(self.endpoint, 3)
|
||||
response = self.client.get(endpoint)
|
||||
entry = response.data
|
||||
entry['wet'] = False
|
||||
entry['solid'] = True
|
||||
response = self.client.patch(endpoint, {
|
||||
'wet': entry['wet'],
|
||||
'solid': entry['solid'],
|
||||
})
|
||||
entry["wet"] = False
|
||||
entry["solid"] = True
|
||||
response = self.client.patch(
|
||||
endpoint,
|
||||
{
|
||||
"wet": entry["wet"],
|
||||
"solid": entry["solid"],
|
||||
},
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(response.data, entry)
|
||||
|
||||
|
||||
class FeedingAPITestCase(TestBase.BabyBuddyAPITestCaseBase):
|
||||
endpoint = reverse('api:feeding-list')
|
||||
endpoint = reverse("api:feeding-list")
|
||||
model = models.Feeding
|
||||
timer_test_data = {'type': 'breast milk', 'method': 'left breast'}
|
||||
timer_test_data = {"type": "breast milk", "method": "left breast"}
|
||||
|
||||
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': 3,
|
||||
'child': 1,
|
||||
'start': '2017-11-18T14:00:00-05:00',
|
||||
'end': '2017-11-18T14:15:00-05:00',
|
||||
'duration': '00:15:00',
|
||||
'type': 'formula',
|
||||
'method': 'bottle',
|
||||
'amount': 2.5,
|
||||
'notes': 'forgot vitamins :('
|
||||
})
|
||||
self.assertEqual(
|
||||
response.data["results"][0],
|
||||
{
|
||||
"id": 3,
|
||||
"child": 1,
|
||||
"start": "2017-11-18T14:00:00-05:00",
|
||||
"end": "2017-11-18T14:15:00-05:00",
|
||||
"duration": "00:15:00",
|
||||
"type": "formula",
|
||||
"method": "bottle",
|
||||
"amount": 2.5,
|
||||
"notes": "forgot vitamins :(",
|
||||
},
|
||||
)
|
||||
|
||||
def test_post(self):
|
||||
data = {
|
||||
'child': 1,
|
||||
'start': '2017-11-19T14:00:00-05:00',
|
||||
'end': '2017-11-19T14:15:00-05:00',
|
||||
'type': 'breast milk',
|
||||
'method': 'left breast',
|
||||
'notes': 'with vitamins'
|
||||
"child": 1,
|
||||
"start": "2017-11-19T14:00:00-05:00",
|
||||
"end": "2017-11-19T14:15:00-05:00",
|
||||
"type": "breast milk",
|
||||
"method": "left breast",
|
||||
"notes": "with vitamins",
|
||||
}
|
||||
response = self.client.post(self.endpoint, data, format='json')
|
||||
response = self.client.post(self.endpoint, data, format="json")
|
||||
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
||||
obj = models.Feeding.objects.get(pk=response.data['id'])
|
||||
self.assertEqual(obj.type, data['type'])
|
||||
self.assertEqual(obj.notes, data['notes'])
|
||||
obj = models.Feeding.objects.get(pk=response.data["id"])
|
||||
self.assertEqual(obj.type, data["type"])
|
||||
self.assertEqual(obj.notes, data["notes"])
|
||||
|
||||
def test_patch(self):
|
||||
endpoint = '{}{}/'.format(self.endpoint, 3)
|
||||
endpoint = "{}{}/".format(self.endpoint, 3)
|
||||
response = self.client.get(endpoint)
|
||||
entry = response.data
|
||||
entry['type'] = 'breast milk'
|
||||
entry['method'] = 'left breast'
|
||||
entry['amount'] = 0
|
||||
response = self.client.patch(endpoint, {
|
||||
'type': entry['type'],
|
||||
'method': entry['method'],
|
||||
'amount': entry['amount'],
|
||||
})
|
||||
entry["type"] = "breast milk"
|
||||
entry["method"] = "left breast"
|
||||
entry["amount"] = 0
|
||||
response = self.client.patch(
|
||||
endpoint,
|
||||
{
|
||||
"type": entry["type"],
|
||||
"method": entry["method"],
|
||||
"amount": entry["amount"],
|
||||
},
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(response.data, entry)
|
||||
|
||||
|
||||
class NoteAPITestCase(TestBase.BabyBuddyAPITestCaseBase):
|
||||
endpoint = reverse('api:note-list')
|
||||
endpoint = reverse("api:note-list")
|
||||
model = models.Note
|
||||
|
||||
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': 1,
|
||||
'note': 'Fake note.',
|
||||
'time': '2017-11-17T22:45:00-05:00'
|
||||
})
|
||||
self.assertEqual(
|
||||
response.data["results"][0],
|
||||
{
|
||||
"id": 1,
|
||||
"child": 1,
|
||||
"note": "Fake note.",
|
||||
"time": "2017-11-17T22:45:00-05:00",
|
||||
},
|
||||
)
|
||||
|
||||
def test_post(self):
|
||||
data = {
|
||||
'child': 1,
|
||||
'note': 'New fake note.',
|
||||
'time': '2017-11-18T22:45:00-05:00'
|
||||
"child": 1,
|
||||
"note": "New fake note.",
|
||||
"time": "2017-11-18T22:45:00-05:00",
|
||||
}
|
||||
response = self.client.post(self.endpoint, data, format='json')
|
||||
response = self.client.post(self.endpoint, data, format="json")
|
||||
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
||||
obj = models.Note.objects.get(pk=response.data['id'])
|
||||
self.assertEqual(obj.note, data['note'])
|
||||
obj = models.Note.objects.get(pk=response.data["id"])
|
||||
self.assertEqual(obj.note, data["note"])
|
||||
|
||||
def test_patch(self):
|
||||
endpoint = '{}{}/'.format(self.endpoint, 1)
|
||||
endpoint = "{}{}/".format(self.endpoint, 1)
|
||||
response = self.client.get(endpoint)
|
||||
entry = response.data
|
||||
entry['note'] = 'Updated note text.'
|
||||
response = self.client.patch(endpoint, {
|
||||
'note': entry['note'],
|
||||
})
|
||||
entry["note"] = "Updated note text."
|
||||
response = self.client.patch(
|
||||
endpoint,
|
||||
{
|
||||
"note": entry["note"],
|
||||
},
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
# The time of entry will always update automatically, so only check the
|
||||
# new value.
|
||||
self.assertEqual(response.data['note'], entry['note'])
|
||||
self.assertEqual(response.data["note"], entry["note"])
|
||||
|
||||
|
||||
class SleepAPITestCase(TestBase.BabyBuddyAPITestCaseBase):
|
||||
endpoint = reverse('api:sleep-list')
|
||||
endpoint = reverse("api:sleep-list")
|
||||
model = models.Sleep
|
||||
timer_test_data = {'child': 1}
|
||||
timer_test_data = {"child": 1}
|
||||
|
||||
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': 4,
|
||||
'child': 1,
|
||||
'start': '2017-11-18T19:00:00-05:00',
|
||||
'end': '2017-11-18T23:00:00-05:00',
|
||||
'duration': '04:00:00',
|
||||
'nap': False,
|
||||
'notes': 'lots of squirming'
|
||||
})
|
||||
self.assertEqual(
|
||||
response.data["results"][0],
|
||||
{
|
||||
"id": 4,
|
||||
"child": 1,
|
||||
"start": "2017-11-18T19:00:00-05:00",
|
||||
"end": "2017-11-18T23:00:00-05:00",
|
||||
"duration": "04:00:00",
|
||||
"nap": False,
|
||||
"notes": "lots of squirming",
|
||||
},
|
||||
)
|
||||
|
||||
def test_post(self):
|
||||
data = {
|
||||
'child': 1,
|
||||
'start': '2017-11-21T19:30:00-05:00',
|
||||
'end': '2017-11-21T23:00:00-05:00',
|
||||
'notes': 'used new swaddle'
|
||||
"child": 1,
|
||||
"start": "2017-11-21T19:30:00-05:00",
|
||||
"end": "2017-11-21T23:00:00-05:00",
|
||||
"notes": "used new swaddle",
|
||||
}
|
||||
response = self.client.post(self.endpoint, data, format='json')
|
||||
response = self.client.post(self.endpoint, data, format="json")
|
||||
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
||||
obj = models.Sleep.objects.get(pk=response.data['id'])
|
||||
self.assertEqual(str(obj.duration), '3:30:00')
|
||||
self.assertEqual(obj.notes, data['notes'])
|
||||
obj = models.Sleep.objects.get(pk=response.data["id"])
|
||||
self.assertEqual(str(obj.duration), "3:30:00")
|
||||
self.assertEqual(obj.notes, data["notes"])
|
||||
|
||||
def test_patch(self):
|
||||
endpoint = '{}{}/'.format(self.endpoint, 4)
|
||||
endpoint = "{}{}/".format(self.endpoint, 4)
|
||||
response = self.client.get(endpoint)
|
||||
entry = response.data
|
||||
entry['end'] = '2017-11-18T23:30:00-05:00'
|
||||
response = self.client.patch(endpoint, {
|
||||
'end': entry['end'],
|
||||
})
|
||||
entry["end"] = "2017-11-18T23:30:00-05:00"
|
||||
response = self.client.patch(
|
||||
endpoint,
|
||||
{
|
||||
"end": entry["end"],
|
||||
},
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
# The duration of entry will always update automatically, so only check
|
||||
# the new value.
|
||||
self.assertEqual(response.data['end'], entry['end'])
|
||||
self.assertEqual(response.data["end"], entry["end"])
|
||||
|
||||
|
||||
class TemperatureAPITestCase(TestBase.BabyBuddyAPITestCaseBase):
|
||||
endpoint = reverse('api:temperature-list')
|
||||
endpoint = reverse("api:temperature-list")
|
||||
model = models.Temperature
|
||||
|
||||
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': 1,
|
||||
'temperature': 98.6,
|
||||
'time': '2017-11-17T12:52:00-05:00',
|
||||
'notes': 'tympanic'
|
||||
})
|
||||
self.assertEqual(
|
||||
response.data["results"][0],
|
||||
{
|
||||
"id": 1,
|
||||
"child": 1,
|
||||
"temperature": 98.6,
|
||||
"time": "2017-11-17T12:52:00-05:00",
|
||||
"notes": "tympanic",
|
||||
},
|
||||
)
|
||||
|
||||
def test_post(self):
|
||||
data = {
|
||||
'child': 1,
|
||||
'temperature': '100.1',
|
||||
'time': '2017-11-20T22:52:00-05:00',
|
||||
'notes': 'rectal'
|
||||
"child": 1,
|
||||
"temperature": "100.1",
|
||||
"time": "2017-11-20T22:52:00-05:00",
|
||||
"notes": "rectal",
|
||||
}
|
||||
response = self.client.post(self.endpoint, data, format='json')
|
||||
response = self.client.post(self.endpoint, data, format="json")
|
||||
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
||||
obj = models.Temperature.objects.get(pk=response.data['id'])
|
||||
self.assertEqual(str(obj.temperature), data['temperature'])
|
||||
self.assertEqual(obj.notes, data['notes'])
|
||||
obj = models.Temperature.objects.get(pk=response.data["id"])
|
||||
self.assertEqual(str(obj.temperature), data["temperature"])
|
||||
self.assertEqual(obj.notes, data["notes"])
|
||||
|
||||
def test_patch(self):
|
||||
endpoint = '{}{}/'.format(self.endpoint, 1)
|
||||
endpoint = "{}{}/".format(self.endpoint, 1)
|
||||
response = self.client.get(endpoint)
|
||||
entry = response.data
|
||||
entry['temperature'] = 99
|
||||
response = self.client.patch(endpoint, {
|
||||
'temperature': entry['temperature'],
|
||||
})
|
||||
entry["temperature"] = 99
|
||||
response = self.client.patch(
|
||||
endpoint,
|
||||
{
|
||||
"temperature": entry["temperature"],
|
||||
},
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(response.data, entry)
|
||||
|
||||
|
||||
class TimerAPITestCase(TestBase.BabyBuddyAPITestCaseBase):
|
||||
endpoint = reverse('api:timer-list')
|
||||
endpoint = reverse("api:timer-list")
|
||||
model = models.Timer
|
||||
|
||||
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,
|
||||
"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,
|
||||
},
|
||||
)
|
||||
|
||||
def test_post(self):
|
||||
data = {
|
||||
'name': 'New fake timer',
|
||||
'user': 1
|
||||
}
|
||||
response = self.client.post(self.endpoint, data, format='json')
|
||||
data = {"name": "New fake timer", "user": 1}
|
||||
response = self.client.post(self.endpoint, data, format="json")
|
||||
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
||||
obj = models.Timer.objects.get(pk=response.data['id'])
|
||||
self.assertEqual(obj.name, data['name'])
|
||||
obj = models.Timer.objects.get(pk=response.data["id"])
|
||||
self.assertEqual(obj.name, data["name"])
|
||||
|
||||
def test_post_default_user(self):
|
||||
user = User.objects.first()
|
||||
response = self.client.post(self.endpoint)
|
||||
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
||||
obj = models.Timer.objects.get(pk=response.data['id'])
|
||||
obj = models.Timer.objects.get(pk=response.data["id"])
|
||||
self.assertEqual(obj.user, user)
|
||||
|
||||
def test_patch(self):
|
||||
endpoint = '{}{}/'.format(self.endpoint, 1)
|
||||
endpoint = "{}{}/".format(self.endpoint, 1)
|
||||
response = self.client.get(endpoint)
|
||||
entry = response.data
|
||||
entry['name'] = 'New Timer Name'
|
||||
response = self.client.patch(endpoint, {
|
||||
'name': entry['name'],
|
||||
})
|
||||
entry["name"] = "New Timer Name"
|
||||
response = self.client.patch(
|
||||
endpoint,
|
||||
{
|
||||
"name": entry["name"],
|
||||
},
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(response.data, entry)
|
||||
|
||||
def test_start_stop_timer(self):
|
||||
endpoint = '{}{}/'.format(self.endpoint, 1)
|
||||
endpoint = "{}{}/".format(self.endpoint, 1)
|
||||
response = self.client.get(endpoint)
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertFalse(response.data["active"])
|
||||
|
@ -424,81 +461,93 @@ class TimerAPITestCase(TestBase.BabyBuddyAPITestCaseBase):
|
|||
|
||||
|
||||
class TummyTimeAPITestCase(TestBase.BabyBuddyAPITestCaseBase):
|
||||
endpoint = reverse('api:tummytime-list')
|
||||
endpoint = reverse("api:tummytime-list")
|
||||
model = models.TummyTime
|
||||
timer_test_data = {'milestone': 'Timer test'}
|
||||
timer_test_data = {"milestone": "Timer test"}
|
||||
|
||||
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': 3,
|
||||
'child': 1,
|
||||
'start': '2017-11-18T15:30:00-05:00',
|
||||
'end': '2017-11-18T15:30:45-05:00',
|
||||
'duration': '00:00:45',
|
||||
'milestone': ''
|
||||
})
|
||||
self.assertEqual(
|
||||
response.data["results"][0],
|
||||
{
|
||||
"id": 3,
|
||||
"child": 1,
|
||||
"start": "2017-11-18T15:30:00-05:00",
|
||||
"end": "2017-11-18T15:30:45-05:00",
|
||||
"duration": "00:00:45",
|
||||
"milestone": "",
|
||||
},
|
||||
)
|
||||
|
||||
def test_post(self):
|
||||
data = {
|
||||
'child': 1,
|
||||
'start': '2017-11-18T12:30:00-05:00',
|
||||
'end': '2017-11-18T12:35:30-05:00',
|
||||
'milestone': 'Rolled over.'
|
||||
"child": 1,
|
||||
"start": "2017-11-18T12:30:00-05:00",
|
||||
"end": "2017-11-18T12:35:30-05:00",
|
||||
"milestone": "Rolled over.",
|
||||
}
|
||||
response = self.client.post(self.endpoint, data, format='json')
|
||||
response = self.client.post(self.endpoint, data, format="json")
|
||||
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
||||
obj = models.TummyTime.objects.get(pk=response.data['id'])
|
||||
self.assertEqual(str(obj.duration), '0:05:30')
|
||||
obj = models.TummyTime.objects.get(pk=response.data["id"])
|
||||
self.assertEqual(str(obj.duration), "0:05:30")
|
||||
|
||||
def test_patch(self):
|
||||
endpoint = '{}{}/'.format(self.endpoint, 3)
|
||||
endpoint = "{}{}/".format(self.endpoint, 3)
|
||||
response = self.client.get(endpoint)
|
||||
entry = response.data
|
||||
entry['milestone'] = 'Switched sides!'
|
||||
response = self.client.patch(endpoint, {
|
||||
'milestone': entry['milestone'],
|
||||
})
|
||||
entry["milestone"] = "Switched sides!"
|
||||
response = self.client.patch(
|
||||
endpoint,
|
||||
{
|
||||
"milestone": entry["milestone"],
|
||||
},
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(response.data, entry)
|
||||
|
||||
|
||||
class WeightAPITestCase(TestBase.BabyBuddyAPITestCaseBase):
|
||||
endpoint = reverse('api:weight-list')
|
||||
endpoint = reverse("api:weight-list")
|
||||
model = models.Weight
|
||||
|
||||
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': 2,
|
||||
'child': 1,
|
||||
'weight': 9.5,
|
||||
'date': '2017-11-18',
|
||||
'notes': 'before feed'
|
||||
})
|
||||
self.assertEqual(
|
||||
response.data["results"][0],
|
||||
{
|
||||
"id": 2,
|
||||
"child": 1,
|
||||
"weight": 9.5,
|
||||
"date": "2017-11-18",
|
||||
"notes": "before feed",
|
||||
},
|
||||
)
|
||||
|
||||
def test_post(self):
|
||||
data = {
|
||||
'child': 1,
|
||||
'weight': '9.75',
|
||||
'date': '2017-11-20',
|
||||
'notes': 'after feed'
|
||||
"child": 1,
|
||||
"weight": "9.75",
|
||||
"date": "2017-11-20",
|
||||
"notes": "after feed",
|
||||
}
|
||||
response = self.client.post(self.endpoint, data, format='json')
|
||||
response = self.client.post(self.endpoint, data, format="json")
|
||||
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
||||
obj = models.Weight.objects.get(pk=response.data['id'])
|
||||
self.assertEqual(str(obj.weight), data['weight'])
|
||||
self.assertEqual(str(obj.notes), data['notes'])
|
||||
obj = models.Weight.objects.get(pk=response.data["id"])
|
||||
self.assertEqual(str(obj.weight), data["weight"])
|
||||
self.assertEqual(str(obj.notes), data["notes"])
|
||||
|
||||
def test_patch(self):
|
||||
endpoint = '{}{}/'.format(self.endpoint, 2)
|
||||
endpoint = "{}{}/".format(self.endpoint, 2)
|
||||
response = self.client.get(endpoint)
|
||||
entry = response.data
|
||||
entry['weight'] = 8.25
|
||||
response = self.client.patch(endpoint, {
|
||||
'weight': entry['weight'],
|
||||
})
|
||||
entry["weight"] = 8.25
|
||||
response = self.client.patch(
|
||||
endpoint,
|
||||
{
|
||||
"weight": entry["weight"],
|
||||
},
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(response.data, entry)
|
||||
|
|
45
api/urls.py
45
api/urls.py
|
@ -6,30 +6,31 @@ from rest_framework.schemas import get_schema_view
|
|||
from . import views
|
||||
|
||||
router = routers.DefaultRouter()
|
||||
router.register(r'children', views.ChildViewSet)
|
||||
router.register(r'changes', views.DiaperChangeViewSet)
|
||||
router.register(r'feedings', views.FeedingViewSet)
|
||||
router.register(r'notes', views.NoteViewSet)
|
||||
router.register(r'sleep', views.SleepViewSet)
|
||||
router.register(r'temperature', views.TemperatureViewSet)
|
||||
router.register(r'timers', views.TimerViewSet)
|
||||
router.register(r'tummy-times', views.TummyTimeViewSet)
|
||||
router.register(r'weight', views.WeightViewSet)
|
||||
router.register(r'height', views.HeightViewSet)
|
||||
router.register(r'head-circumference', views.HeadCircumferenceViewSet)
|
||||
router.register(r'bmi', views.BMIViewSet)
|
||||
router.register(r"children", views.ChildViewSet)
|
||||
router.register(r"changes", views.DiaperChangeViewSet)
|
||||
router.register(r"feedings", views.FeedingViewSet)
|
||||
router.register(r"notes", views.NoteViewSet)
|
||||
router.register(r"sleep", views.SleepViewSet)
|
||||
router.register(r"temperature", views.TemperatureViewSet)
|
||||
router.register(r"timers", views.TimerViewSet)
|
||||
router.register(r"tummy-times", views.TummyTimeViewSet)
|
||||
router.register(r"weight", views.WeightViewSet)
|
||||
router.register(r"height", views.HeightViewSet)
|
||||
router.register(r"head-circumference", views.HeadCircumferenceViewSet)
|
||||
router.register(r"bmi", views.BMIViewSet)
|
||||
|
||||
app_name = 'api'
|
||||
app_name = "api"
|
||||
|
||||
urlpatterns = [
|
||||
path('api/', include(router.urls)),
|
||||
path('api/auth/', include(
|
||||
'rest_framework.urls',
|
||||
namespace='rest_framework'
|
||||
)),
|
||||
path('api/schema', get_schema_view(
|
||||
title='Baby Buddy API',
|
||||
path("api/", include(router.urls)),
|
||||
path("api/auth/", include("rest_framework.urls", namespace="rest_framework")),
|
||||
path(
|
||||
"api/schema",
|
||||
get_schema_view(
|
||||
title="Baby Buddy API",
|
||||
version=1,
|
||||
description='API documentation for the Baby Buddy application'
|
||||
), name='openapi-schema'),
|
||||
description="API documentation for the Baby Buddy application",
|
||||
),
|
||||
name="openapi-schema",
|
||||
),
|
||||
]
|
||||
|
|
16
api/views.py
16
api/views.py
|
@ -12,8 +12,8 @@ from .mixins import TimerFieldSupportMixin
|
|||
class ChildViewSet(viewsets.ModelViewSet):
|
||||
queryset = models.Child.objects.all()
|
||||
serializer_class = serializers.ChildSerializer
|
||||
lookup_field = 'slug'
|
||||
filterset_fields = ('first_name', 'last_name', 'slug', 'birth_date')
|
||||
lookup_field = "slug"
|
||||
filterset_fields = ("first_name", "last_name", "slug", "birth_date")
|
||||
|
||||
|
||||
class DiaperChangeViewSet(viewsets.ModelViewSet):
|
||||
|
@ -51,13 +51,13 @@ class TimerViewSet(viewsets.ModelViewSet):
|
|||
serializer_class = serializers.TimerSerializer
|
||||
filterset_class = filters.TimerFilter
|
||||
|
||||
@action(detail=True, methods=['patch'])
|
||||
@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'])
|
||||
@action(detail=True, methods=["patch"])
|
||||
def restart(self, request, pk=None):
|
||||
timer = self.get_object()
|
||||
timer.restart()
|
||||
|
@ -73,22 +73,22 @@ class TummyTimeViewSet(TimerFieldSupportMixin, viewsets.ModelViewSet):
|
|||
class WeightViewSet(viewsets.ModelViewSet):
|
||||
queryset = models.Weight.objects.all()
|
||||
serializer_class = serializers.WeightSerializer
|
||||
filterset_fields = ('child', 'date')
|
||||
filterset_fields = ("child", "date")
|
||||
|
||||
|
||||
class HeightViewSet(viewsets.ModelViewSet):
|
||||
queryset = models.Height.objects.all()
|
||||
serializer_class = serializers.HeightSerializer
|
||||
filterset_fields = ('child', 'date')
|
||||
filterset_fields = ("child", "date")
|
||||
|
||||
|
||||
class HeadCircumferenceViewSet(viewsets.ModelViewSet):
|
||||
queryset = models.HeadCircumference.objects.all()
|
||||
serializer_class = serializers.HeadCircumferenceSerializer
|
||||
filterset_fields = ('child', 'date')
|
||||
filterset_fields = ("child", "date")
|
||||
|
||||
|
||||
class BMIViewSet(viewsets.ModelViewSet):
|
||||
queryset = models.BMI.objects.all()
|
||||
serializer_class = serializers.BMISerializer
|
||||
filterset_fields = ('child', 'date')
|
||||
filterset_fields = ("child", "date")
|
||||
|
|
|
@ -45,10 +45,10 @@
|
|||
'----------------' '----------------' '----------------' '----------------'
|
||||
""" # noqa
|
||||
|
||||
__title__ = 'Baby Buddy'
|
||||
__version__ = '1.9.3'
|
||||
__license__ = 'BSD 2-Clause'
|
||||
__title__ = "Baby Buddy"
|
||||
__version__ = "1.9.3"
|
||||
__license__ = "BSD 2-Clause"
|
||||
|
||||
VERSION = __version__
|
||||
|
||||
default_app_config = 'babybuddy.apps.BabyBuddyConfig'
|
||||
default_app_config = "babybuddy.apps.BabyBuddyConfig"
|
||||
|
|
|
@ -9,21 +9,25 @@ from babybuddy import models
|
|||
|
||||
class SettingsInline(admin.StackedInline):
|
||||
model = models.Settings
|
||||
verbose_name = _('Settings')
|
||||
verbose_name_plural = _('Settings')
|
||||
verbose_name = _("Settings")
|
||||
verbose_name_plural = _("Settings")
|
||||
can_delete = False
|
||||
fieldsets = (
|
||||
(_('Dashboard'), {
|
||||
'fields': (
|
||||
'dashboard_refresh_rate',
|
||||
'dashboard_hide_empty',
|
||||
'dashboard_hide_age')
|
||||
}),
|
||||
(
|
||||
_("Dashboard"),
|
||||
{
|
||||
"fields": (
|
||||
"dashboard_refresh_rate",
|
||||
"dashboard_hide_empty",
|
||||
"dashboard_hide_age",
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class UserAdmin(BaseUserAdmin):
|
||||
inlines = (SettingsInline, )
|
||||
inlines = (SettingsInline,)
|
||||
|
||||
|
||||
admin.site.unregister(User)
|
||||
|
|
|
@ -7,12 +7,12 @@ from babybuddy import VERSION
|
|||
|
||||
|
||||
class BabyBuddyConfig(AppConfig):
|
||||
name = 'babybuddy'
|
||||
verbose_name = 'Baby Buddy'
|
||||
name = "babybuddy"
|
||||
verbose_name = "Baby Buddy"
|
||||
version = VERSION
|
||||
version_string = VERSION
|
||||
|
||||
def ready(self):
|
||||
if os.path.isfile('.git/refs/heads/master'):
|
||||
commit = open('.git/refs/heads/master').read()
|
||||
self.version_string += ' ({})'.format(commit[0:7])
|
||||
if os.path.isfile(".git/refs/heads/master"):
|
||||
commit = open(".git/refs/heads/master").read()
|
||||
self.version_string += " ({})".format(commit[0:7])
|
||||
|
|
|
@ -9,8 +9,14 @@ from .models import Settings
|
|||
class UserAddForm(UserCreationForm):
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ['username', 'first_name', 'last_name', 'email',
|
||||
'is_staff', 'is_active']
|
||||
fields = [
|
||||
"username",
|
||||
"first_name",
|
||||
"last_name",
|
||||
"email",
|
||||
"is_staff",
|
||||
"is_active",
|
||||
]
|
||||
|
||||
def save(self, commit=True):
|
||||
user = super(UserAddForm, self).save(commit=False)
|
||||
|
@ -23,28 +29,34 @@ class UserAddForm(UserCreationForm):
|
|||
class UserUpdateForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ['username', 'first_name', 'last_name', 'email',
|
||||
'is_staff', 'is_active']
|
||||
fields = [
|
||||
"username",
|
||||
"first_name",
|
||||
"last_name",
|
||||
"email",
|
||||
"is_staff",
|
||||
"is_active",
|
||||
]
|
||||
|
||||
|
||||
class UserForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ['first_name', 'last_name', 'email']
|
||||
fields = ["first_name", "last_name", "email"]
|
||||
|
||||
|
||||
class UserPasswordForm(PasswordChangeForm):
|
||||
class Meta:
|
||||
fields = ['old_password', 'new_password1', 'new_password2']
|
||||
fields = ["old_password", "new_password1", "new_password2"]
|
||||
|
||||
|
||||
class UserSettingsForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Settings
|
||||
fields = [
|
||||
'dashboard_refresh_rate',
|
||||
'dashboard_hide_empty',
|
||||
'dashboard_hide_age',
|
||||
'language',
|
||||
'timezone'
|
||||
"dashboard_refresh_rate",
|
||||
"dashboard_hide_empty",
|
||||
"dashboard_hide_age",
|
||||
"language",
|
||||
"timezone",
|
||||
]
|
||||
|
|
|
@ -13,7 +13,7 @@ from core import models
|
|||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Generates fake children and related entries.'
|
||||
help = "Generates fake children and related entries."
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(Command, self).__init__(*args, **kwargs)
|
||||
|
@ -25,37 +25,35 @@ class Command(BaseCommand):
|
|||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
'--children',
|
||||
dest='children',
|
||||
"--children",
|
||||
dest="children",
|
||||
default=1,
|
||||
help='The number of fake children to create.'
|
||||
help="The number of fake children to create.",
|
||||
)
|
||||
parser.add_argument(
|
||||
'--days',
|
||||
dest='days',
|
||||
"--days",
|
||||
dest="days",
|
||||
default=31,
|
||||
help='How many days of fake entries to create.'
|
||||
help="How many days of fake entries to create.",
|
||||
)
|
||||
|
||||
def handle(self, *args, **kwargs):
|
||||
verbosity = int(kwargs['verbosity'])
|
||||
children = int(kwargs['children']) or 1
|
||||
days = int(kwargs['days']) or 31
|
||||
verbosity = int(kwargs["verbosity"])
|
||||
children = int(kwargs["children"]) or 1
|
||||
days = int(kwargs["days"]) or 31
|
||||
|
||||
birth_date = (timezone.localtime() - timedelta(days=days))
|
||||
birth_date = timezone.localtime() - timedelta(days=days)
|
||||
for i in range(0, children):
|
||||
self.child = models.Child.objects.create(
|
||||
first_name=self.faker.first_name(),
|
||||
last_name=self.faker.last_name(),
|
||||
birth_date=birth_date
|
||||
birth_date=birth_date,
|
||||
)
|
||||
self.child.save()
|
||||
self._add_child_data()
|
||||
|
||||
if verbosity > 0:
|
||||
self.stdout.write(
|
||||
self.style.SUCCESS('Successfully added fake data.')
|
||||
)
|
||||
self.stdout.write(self.style.SUCCESS("Successfully added fake data."))
|
||||
|
||||
@transaction.atomic
|
||||
def _add_child_data(self):
|
||||
|
@ -121,18 +119,17 @@ class Command(BaseCommand):
|
|||
"""
|
||||
solid = choice([True, False, False, False])
|
||||
wet = choice([True, False])
|
||||
color = ''
|
||||
color = ""
|
||||
if solid:
|
||||
color = choice(
|
||||
models.DiaperChange._meta.get_field('color').choices)[0]
|
||||
color = choice(models.DiaperChange._meta.get_field("color").choices)[0]
|
||||
if not wet and not solid:
|
||||
wet = True
|
||||
amount = Decimal('%d.%d' % (randint(0, 6), randint(1, 9)))
|
||||
amount = Decimal("%d.%d" % (randint(0, 6), randint(1, 9)))
|
||||
time = self.time + timedelta(minutes=randint(1, 60))
|
||||
|
||||
notes = ''
|
||||
notes = ""
|
||||
if choice([True, False, False, False]):
|
||||
notes = ' '.join(self.faker.sentences(randint(1, 5)))
|
||||
notes = " ".join(self.faker.sentences(randint(1, 5)))
|
||||
|
||||
if time < self.time_now:
|
||||
models.DiaperChange.objects.create(
|
||||
|
@ -142,7 +139,7 @@ class Command(BaseCommand):
|
|||
solid=solid,
|
||||
color=color,
|
||||
amount=amount,
|
||||
notes=notes
|
||||
notes=notes,
|
||||
).save()
|
||||
self.time = time
|
||||
|
||||
|
@ -152,26 +149,26 @@ class Command(BaseCommand):
|
|||
Add a Feeding entry and advance self.time.
|
||||
:returns:
|
||||
"""
|
||||
method = choice(models.Feeding._meta.get_field('method').choices)[0]
|
||||
method = choice(models.Feeding._meta.get_field("method").choices)[0]
|
||||
amount = None
|
||||
if method == 'bottle':
|
||||
amount = Decimal('%d.%d' % (randint(0, 6), randint(0, 9)))
|
||||
if method == "bottle":
|
||||
amount = Decimal("%d.%d" % (randint(0, 6), randint(0, 9)))
|
||||
start = self.time + timedelta(minutes=randint(1, 60))
|
||||
end = start + timedelta(minutes=randint(5, 20))
|
||||
|
||||
notes = ''
|
||||
notes = ""
|
||||
if choice([True, False, False, False]):
|
||||
notes = ' '.join(self.faker.sentences(randint(1, 5)))
|
||||
notes = " ".join(self.faker.sentences(randint(1, 5)))
|
||||
|
||||
if end < self.time_now:
|
||||
models.Feeding.objects.create(
|
||||
child=self.child,
|
||||
start=start,
|
||||
end=end,
|
||||
type=choice(models.Feeding._meta.get_field('type').choices)[0],
|
||||
type=choice(models.Feeding._meta.get_field("type").choices)[0],
|
||||
method=method,
|
||||
amount=amount,
|
||||
notes=notes
|
||||
notes=notes,
|
||||
).save()
|
||||
self.time = end
|
||||
|
||||
|
@ -197,16 +194,13 @@ class Command(BaseCommand):
|
|||
minutes = randint(30, 60 * 2)
|
||||
end = self.time + timedelta(minutes=minutes)
|
||||
|
||||
notes = ''
|
||||
notes = ""
|
||||
if choice([True, False, False, False]):
|
||||
notes = ' '.join(self.faker.sentences(randint(1, 5)))
|
||||
notes = " ".join(self.faker.sentences(randint(1, 5)))
|
||||
|
||||
if end < self.time_now:
|
||||
models.Sleep.objects.create(
|
||||
child=self.child,
|
||||
start=self.time,
|
||||
end=end,
|
||||
notes=notes
|
||||
child=self.child, start=self.time, end=end, notes=notes
|
||||
).save()
|
||||
self.time = end
|
||||
|
||||
|
@ -218,15 +212,12 @@ class Command(BaseCommand):
|
|||
"""
|
||||
self.temperature = round(uniform(95.0, 102.0), 2)
|
||||
|
||||
notes = ''
|
||||
notes = ""
|
||||
if choice([True, False, False, False]):
|
||||
notes = ' '.join(self.faker.sentences(randint(1, 5)))
|
||||
notes = " ".join(self.faker.sentences(randint(1, 5)))
|
||||
|
||||
models.Temperature.objects.create(
|
||||
child=self.child,
|
||||
temperature=self.temperature,
|
||||
time=self.time,
|
||||
notes=notes
|
||||
child=self.child, temperature=self.temperature, time=self.time, notes=notes
|
||||
).save()
|
||||
|
||||
@transaction.atomic
|
||||
|
@ -235,7 +226,7 @@ class Command(BaseCommand):
|
|||
Add a Tummy time entry and advance self.time.
|
||||
:returns:
|
||||
"""
|
||||
milestone = ''
|
||||
milestone = ""
|
||||
if choice([True, False]):
|
||||
milestone = self.faker.sentence()
|
||||
start = self.time + timedelta(minutes=randint(1, 60))
|
||||
|
@ -245,10 +236,7 @@ class Command(BaseCommand):
|
|||
|
||||
if end < self.time_now:
|
||||
models.TummyTime.objects.create(
|
||||
child=self.child,
|
||||
start=start,
|
||||
end=end,
|
||||
milestone=milestone
|
||||
child=self.child, start=start, end=end, milestone=milestone
|
||||
).save()
|
||||
self.time = end
|
||||
|
||||
|
@ -260,15 +248,15 @@ class Command(BaseCommand):
|
|||
"""
|
||||
self.weight += uniform(0.1, 0.3)
|
||||
|
||||
notes = ''
|
||||
notes = ""
|
||||
if choice([True, False, False, False]):
|
||||
notes = ' '.join(self.faker.sentences(randint(1, 5)))
|
||||
notes = " ".join(self.faker.sentences(randint(1, 5)))
|
||||
|
||||
models.Weight.objects.create(
|
||||
child=self.child,
|
||||
weight=round(self.weight, 2),
|
||||
date=self.time.date(),
|
||||
notes=notes
|
||||
notes=notes,
|
||||
).save()
|
||||
|
||||
@transaction.atomic
|
||||
|
@ -279,15 +267,15 @@ class Command(BaseCommand):
|
|||
"""
|
||||
self.height += uniform(0.1, 0.3)
|
||||
|
||||
notes = ''
|
||||
notes = ""
|
||||
if choice([True, False, False, False]):
|
||||
notes = ' '.join(self.faker.sentences(randint(1, 5)))
|
||||
notes = " ".join(self.faker.sentences(randint(1, 5)))
|
||||
|
||||
models.Height.objects.create(
|
||||
child=self.child,
|
||||
height=round(self.height, 2),
|
||||
date=self.time.date(),
|
||||
notes=notes
|
||||
notes=notes,
|
||||
).save()
|
||||
|
||||
@transaction.atomic
|
||||
|
@ -298,15 +286,15 @@ class Command(BaseCommand):
|
|||
"""
|
||||
self.head_circumference += uniform(0.1, 0.3)
|
||||
|
||||
notes = ''
|
||||
notes = ""
|
||||
if choice([True, False, False, False]):
|
||||
notes = ' '.join(self.faker.sentences(randint(1, 5)))
|
||||
notes = " ".join(self.faker.sentences(randint(1, 5)))
|
||||
|
||||
models.HeadCircumference.objects.create(
|
||||
child=self.child,
|
||||
head_circumference=round(self.head_circumference, 2),
|
||||
date=self.time.date(),
|
||||
notes=notes
|
||||
notes=notes,
|
||||
).save()
|
||||
|
||||
@transaction.atomic
|
||||
|
@ -317,13 +305,10 @@ class Command(BaseCommand):
|
|||
"""
|
||||
self.bmi += uniform(0.1, 0.3)
|
||||
|
||||
notes = ''
|
||||
notes = ""
|
||||
if choice([True, False, False, False]):
|
||||
notes = ' '.join(self.faker.sentences(randint(1, 5)))
|
||||
notes = " ".join(self.faker.sentences(randint(1, 5)))
|
||||
|
||||
models.BMI.objects.create(
|
||||
child=self.child,
|
||||
bmi=round(self.bmi, 2),
|
||||
date=self.time.date(),
|
||||
notes=notes
|
||||
child=self.child, bmi=round(self.bmi, 2), date=self.time.date(), notes=notes
|
||||
).save()
|
||||
|
|
|
@ -4,14 +4,14 @@ from django.core.management.commands import migrate
|
|||
|
||||
|
||||
class Command(migrate.Command):
|
||||
help = 'Creates an initial User (admin/admin) for Baby Buddy.'
|
||||
help = "Creates an initial User (admin/admin) for Baby Buddy."
|
||||
|
||||
def handle(self, *args, **kwargs):
|
||||
super(Command, self).handle(*args, **kwargs)
|
||||
|
||||
superusers = User.objects.filter(is_superuser=True)
|
||||
if len(superusers) == 0:
|
||||
default_user = User.objects.create_user('admin', password='admin')
|
||||
default_user = User.objects.create_user("admin", password="admin")
|
||||
default_user.is_superuser = True
|
||||
default_user.is_staff = True
|
||||
default_user.save()
|
||||
|
|
|
@ -12,13 +12,14 @@ from .migrate import Command as Migrate
|
|||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Reapplies core migrations and generates fake data.'
|
||||
help = "Reapplies core migrations and generates fake data."
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(Command, self).__init__(*args, **kwargs)
|
||||
self.UserModel = get_user_model()
|
||||
self.username_field = self.UserModel._meta.get_field(
|
||||
self.UserModel.USERNAME_FIELD)
|
||||
self.UserModel.USERNAME_FIELD
|
||||
)
|
||||
|
||||
# Disable system checks for reset.
|
||||
self.requires_system_checks = False
|
||||
|
@ -28,20 +29,20 @@ class Command(BaseCommand):
|
|||
Fake().add_arguments(parser)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
verbosity = options['verbosity']
|
||||
verbosity = options["verbosity"]
|
||||
|
||||
# Flush all existing database records.
|
||||
flush = Flush()
|
||||
flush.handle(**options)
|
||||
if verbosity > 0:
|
||||
self.stdout.write(self.style.SUCCESS('Database flushed.'))
|
||||
self.stdout.write(self.style.SUCCESS("Database flushed."))
|
||||
|
||||
# Run migrations for all Baby Buddy apps.
|
||||
for config in apps.app_configs.values():
|
||||
if path.split(path.split(config.path)[0])[1] == 'babybuddy':
|
||||
if path.split(path.split(config.path)[0])[1] == "babybuddy":
|
||||
migrate = Migrate()
|
||||
options['app_label'] = config.name
|
||||
options['migration_name'] = 'zero'
|
||||
options["app_label"] = config.name
|
||||
options["migration_name"] = "zero"
|
||||
|
||||
try:
|
||||
migrate.handle(*args, **options)
|
||||
|
@ -51,18 +52,18 @@ class Command(BaseCommand):
|
|||
|
||||
# Run other migrations.
|
||||
migrate = Migrate()
|
||||
options['app_label'] = None
|
||||
options['migration_name'] = None
|
||||
options["app_label"] = None
|
||||
options["migration_name"] = None
|
||||
migrate.handle(*args, **options)
|
||||
|
||||
# Clear cache.
|
||||
cache.clear()
|
||||
if verbosity > 0:
|
||||
self.stdout.write(self.style.SUCCESS('Cache cleared.'))
|
||||
self.stdout.write(self.style.SUCCESS("Cache cleared."))
|
||||
|
||||
# Populate database with fake data.
|
||||
fake = Fake()
|
||||
fake.handle(*args, **options)
|
||||
|
||||
if verbosity > 0:
|
||||
self.stdout.write(self.style.SUCCESS('Database reset complete.'))
|
||||
self.stdout.write(self.style.SUCCESS("Database reset complete."))
|
||||
|
|
|
@ -16,63 +16,66 @@ def update_en_us_date_formats():
|
|||
based on user settings.
|
||||
"""
|
||||
if settings.USE_24_HOUR_TIME_FORMAT:
|
||||
formats_en_us.DATETIME_FORMAT = 'N j, Y, H:i:s'
|
||||
formats_en_us.DATETIME_FORMAT = "N j, Y, H:i:s"
|
||||
custom_input_formats = [
|
||||
'%m/%d/%Y %H:%M:%S', # '10/25/2006 14:30:59'
|
||||
'%m/%d/%Y %H:%M', # '10/25/2006 14:30'
|
||||
"%m/%d/%Y %H:%M:%S", # '10/25/2006 14:30:59'
|
||||
"%m/%d/%Y %H:%M", # '10/25/2006 14:30'
|
||||
]
|
||||
formats_en_us.SHORT_DATETIME_FORMAT = 'm/d/Y G:i:s'
|
||||
formats_en_us.TIME_FORMAT = 'H:i:s'
|
||||
formats_en_us.SHORT_DATETIME_FORMAT = "m/d/Y G:i:s"
|
||||
formats_en_us.TIME_FORMAT = "H:i:s"
|
||||
else:
|
||||
# These formats are added to support the locale style of Baby Buddy's
|
||||
# frontend library, which uses momentjs.
|
||||
custom_input_formats = [
|
||||
'%m/%d/%Y %I:%M:%S %p', # '10/25/2006 2:30:59 PM'
|
||||
'%m/%d/%Y %I:%M %p', # '10/25/2006 2:30 PM'
|
||||
"%m/%d/%Y %I:%M:%S %p", # '10/25/2006 2:30:59 PM'
|
||||
"%m/%d/%Y %I:%M %p", # '10/25/2006 2:30 PM'
|
||||
]
|
||||
|
||||
# Add custom "short" version of `MONTH_DAY_FORMAT`.
|
||||
formats_en_us.SHORT_MONTH_DAY_FORMAT = 'M j'
|
||||
formats_en_us.SHORT_MONTH_DAY_FORMAT = "M j"
|
||||
|
||||
# Append all other input formats from the base locale.
|
||||
formats_en_us.DATETIME_INPUT_FORMATS = \
|
||||
formats_en_us.DATETIME_INPUT_FORMATS = (
|
||||
custom_input_formats + formats_en_us.DATETIME_INPUT_FORMATS
|
||||
)
|
||||
|
||||
|
||||
def update_en_gb_date_formats():
|
||||
if settings.USE_24_HOUR_TIME_FORMAT:
|
||||
# 25 October 2006 14:30:00
|
||||
formats_en_gb.DATETIME_FORMAT = 'j F Y H:i:s'
|
||||
formats_en_gb.DATETIME_FORMAT = "j F Y H:i:s"
|
||||
custom_input_formats = [
|
||||
'%d/%m/%Y %H:%M:%S', # '25/10/2006 14:30:59'
|
||||
'%d/%m/%Y %H:%M', # '25/10/2006 14:30'
|
||||
"%d/%m/%Y %H:%M:%S", # '25/10/2006 14:30:59'
|
||||
"%d/%m/%Y %H:%M", # '25/10/2006 14:30'
|
||||
]
|
||||
formats_en_gb.SHORT_DATETIME_FORMAT = 'd/m/Y H:i'
|
||||
formats_en_gb.TIME_FORMAT = 'H:i'
|
||||
formats_en_gb.SHORT_DATETIME_FORMAT = "d/m/Y H:i"
|
||||
formats_en_gb.TIME_FORMAT = "H:i"
|
||||
else:
|
||||
formats_en_gb.DATETIME_FORMAT = 'j F Y f a' # 25 October 2006 2:30 p.m
|
||||
formats_en_gb.DATETIME_FORMAT = "j F Y f a" # 25 October 2006 2:30 p.m
|
||||
# These formats are added to support the locale style of Baby Buddy's
|
||||
# frontend library, which uses momentjs.
|
||||
custom_input_formats = [
|
||||
'%d/%m/%Y %I:%M:%S %p', # '25/10/2006 2:30:59 PM'
|
||||
'%d/%m/%Y %I:%M %p', # '25/10/2006 2:30 PM'
|
||||
"%d/%m/%Y %I:%M:%S %p", # '25/10/2006 2:30:59 PM'
|
||||
"%d/%m/%Y %I:%M %p", # '25/10/2006 2:30 PM'
|
||||
]
|
||||
|
||||
# Append all other input formats from the base locale.
|
||||
formats_en_gb.DATETIME_INPUT_FORMATS = \
|
||||
formats_en_gb.DATETIME_INPUT_FORMATS = (
|
||||
custom_input_formats + formats_en_gb.DATETIME_INPUT_FORMATS
|
||||
)
|
||||
|
||||
|
||||
class UserLanguageMiddleware:
|
||||
"""
|
||||
Customizes settings based on user language setting.
|
||||
"""
|
||||
|
||||
def __init__(self, get_response):
|
||||
self.get_response = get_response
|
||||
|
||||
def __call__(self, request):
|
||||
user = request.user
|
||||
if hasattr(user, 'settings') and user.settings.language:
|
||||
if hasattr(user, "settings") and user.settings.language:
|
||||
language = user.settings.language
|
||||
elif request.LANGUAGE_CODE:
|
||||
language = request.LANGUAGE_CODE
|
||||
|
@ -80,9 +83,9 @@ class UserLanguageMiddleware:
|
|||
language = settings.LANGUAGE_CODE
|
||||
|
||||
if language:
|
||||
if language == 'en-US':
|
||||
if language == "en-US":
|
||||
update_en_us_date_formats()
|
||||
elif language == 'en-GB':
|
||||
elif language == "en-GB":
|
||||
update_en_gb_date_formats()
|
||||
|
||||
# Set the language before generating the response.
|
||||
|
@ -104,12 +107,13 @@ class UserTimezoneMiddleware:
|
|||
`django.contrib.auth.middleware.AuthenticationMiddleware` because it uses
|
||||
the request.user object.
|
||||
"""
|
||||
|
||||
def __init__(self, get_response):
|
||||
self.get_response = get_response
|
||||
|
||||
def __call__(self, request):
|
||||
user = request.user
|
||||
if hasattr(user, 'settings') and user.settings.timezone:
|
||||
if hasattr(user, "settings") and user.settings.timezone:
|
||||
try:
|
||||
timezone.activate(pytz.timezone(user.settings.timezone))
|
||||
except pytz.UnknownTimeZoneError:
|
||||
|
@ -121,20 +125,21 @@ class RollingSessionMiddleware:
|
|||
"""
|
||||
Periodically resets the session expiry for existing sessions.
|
||||
"""
|
||||
|
||||
def __init__(self, get_response):
|
||||
self.get_response = get_response
|
||||
|
||||
def __call__(self, request):
|
||||
if request.session.keys():
|
||||
session_refresh = request.session.get('session_refresh')
|
||||
session_refresh = request.session.get("session_refresh")
|
||||
if session_refresh:
|
||||
try:
|
||||
delta = int(time.time()) - session_refresh
|
||||
except (ValueError, TypeError):
|
||||
delta = settings.ROLLING_SESSION_REFRESH + 1
|
||||
if delta > settings.ROLLING_SESSION_REFRESH:
|
||||
request.session['session_refresh'] = int(time.time())
|
||||
request.session["session_refresh"] = int(time.time())
|
||||
request.session.set_expiry(settings.SESSION_COOKIE_AGE)
|
||||
else:
|
||||
request.session['session_refresh'] = int(time.time())
|
||||
request.session["session_refresh"] = int(time.time())
|
||||
return self.get_response(request)
|
||||
|
|
|
@ -16,11 +16,44 @@ class Migration(migrations.Migration):
|
|||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Settings',
|
||||
name="Settings",
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('dashboard_refresh_rate', models.DurationField(blank=True, choices=[(None, 'disabled'), (datetime.timedelta(0, 60), '1 min.'), (datetime.timedelta(0, 120), '2 min.'), (datetime.timedelta(0, 180), '3 min.'), (datetime.timedelta(0, 240), '4 min.'), (datetime.timedelta(0, 300), '5 min.'), (datetime.timedelta(0, 600), '10 min.'), (datetime.timedelta(0, 900), '15 min.'), (datetime.timedelta(0, 1800), '30 min.')], default=datetime.timedelta(0, 60), null=True, verbose_name='Refresh rate')),
|
||||
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
(
|
||||
"dashboard_refresh_rate",
|
||||
models.DurationField(
|
||||
blank=True,
|
||||
choices=[
|
||||
(None, "disabled"),
|
||||
(datetime.timedelta(0, 60), "1 min."),
|
||||
(datetime.timedelta(0, 120), "2 min."),
|
||||
(datetime.timedelta(0, 180), "3 min."),
|
||||
(datetime.timedelta(0, 240), "4 min."),
|
||||
(datetime.timedelta(0, 300), "5 min."),
|
||||
(datetime.timedelta(0, 600), "10 min."),
|
||||
(datetime.timedelta(0, 900), "15 min."),
|
||||
(datetime.timedelta(0, 1800), "30 min."),
|
||||
],
|
||||
default=datetime.timedelta(0, 60),
|
||||
null=True,
|
||||
verbose_name="Refresh rate",
|
||||
),
|
||||
),
|
||||
(
|
||||
"user",
|
||||
models.OneToOneField(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
]
|
||||
|
|
|
@ -3,8 +3,8 @@ from django.db import migrations
|
|||
|
||||
|
||||
def add_settings(apps, schema_editor):
|
||||
Settings = apps.get_model('babybuddy', 'Settings')
|
||||
User = apps.get_model('auth', 'User')
|
||||
Settings = apps.get_model("babybuddy", "Settings")
|
||||
User = apps.get_model("auth", "User")
|
||||
for user in User.objects.all():
|
||||
if Settings.objects.filter(user=user).count() == 0:
|
||||
settings = Settings.objects.create(user=user)
|
||||
|
@ -16,7 +16,7 @@ class Migration(migrations.Migration):
|
|||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('babybuddy', '0001_initial'),
|
||||
("babybuddy", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
|
|
|
@ -7,13 +7,30 @@ from django.db import migrations, models
|
|||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('babybuddy', '0002_add_settings'),
|
||||
("babybuddy", "0002_add_settings"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='settings',
|
||||
name='dashboard_refresh_rate',
|
||||
field=models.DurationField(blank=True, choices=[(None, 'disabled'), (datetime.timedelta(0, 60), '1 min.'), (datetime.timedelta(0, 120), '2 min.'), (datetime.timedelta(0, 180), '3 min.'), (datetime.timedelta(0, 240), '4 min.'), (datetime.timedelta(0, 300), '5 min.'), (datetime.timedelta(0, 600), '10 min.'), (datetime.timedelta(0, 900), '15 min.'), (datetime.timedelta(0, 1800), '30 min.')], default=datetime.timedelta(0, 60), help_text='This setting will only be used when a browser does not support refresh on focus.', null=True, verbose_name='Refresh rate'),
|
||||
model_name="settings",
|
||||
name="dashboard_refresh_rate",
|
||||
field=models.DurationField(
|
||||
blank=True,
|
||||
choices=[
|
||||
(None, "disabled"),
|
||||
(datetime.timedelta(0, 60), "1 min."),
|
||||
(datetime.timedelta(0, 120), "2 min."),
|
||||
(datetime.timedelta(0, 180), "3 min."),
|
||||
(datetime.timedelta(0, 240), "4 min."),
|
||||
(datetime.timedelta(0, 300), "5 min."),
|
||||
(datetime.timedelta(0, 600), "10 min."),
|
||||
(datetime.timedelta(0, 900), "15 min."),
|
||||
(datetime.timedelta(0, 1800), "30 min."),
|
||||
],
|
||||
default=datetime.timedelta(0, 60),
|
||||
help_text="This setting will only be used when a browser does not support refresh on focus.",
|
||||
null=True,
|
||||
verbose_name="Refresh rate",
|
||||
),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -6,13 +6,15 @@ from django.db import migrations, models
|
|||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('babybuddy', '0003_add_refresh_help_text'),
|
||||
("babybuddy", "0003_add_refresh_help_text"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='settings',
|
||||
name='language',
|
||||
field=models.CharField(choices=[], default='en', max_length=255, verbose_name='Language'),
|
||||
model_name="settings",
|
||||
name="language",
|
||||
field=models.CharField(
|
||||
choices=[], default="en", max_length=255, verbose_name="Language"
|
||||
),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -6,13 +6,18 @@ from django.db import migrations, models
|
|||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('babybuddy', '0004_settings_language'),
|
||||
("babybuddy", "0004_settings_language"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='settings',
|
||||
name='language',
|
||||
field=models.CharField(choices=[('en', 'English'), ('fr', 'French')], default='en', max_length=255, verbose_name='Language'),
|
||||
model_name="settings",
|
||||
name="language",
|
||||
field=models.CharField(
|
||||
choices=[("en", "English"), ("fr", "French")],
|
||||
default="en",
|
||||
max_length=255,
|
||||
verbose_name="Language",
|
||||
),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -6,13 +6,18 @@ from django.db import migrations, models
|
|||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('babybuddy', '0005_auto_20190502_1701'),
|
||||
("babybuddy", "0005_auto_20190502_1701"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='settings',
|
||||
name='language',
|
||||
field=models.CharField(choices=[('en', 'English'), ('fr', 'French'), ('sv', 'Swedish')], default='en', max_length=255, verbose_name='Language'),
|
||||
model_name="settings",
|
||||
name="language",
|
||||
field=models.CharField(
|
||||
choices=[("en", "English"), ("fr", "French"), ("sv", "Swedish")],
|
||||
default="en",
|
||||
max_length=255,
|
||||
verbose_name="Language",
|
||||
),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -6,13 +6,23 @@ from django.db import migrations, models
|
|||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('babybuddy', '0006_auto_20190502_1744'),
|
||||
("babybuddy", "0006_auto_20190502_1744"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='settings',
|
||||
name='language',
|
||||
field=models.CharField(choices=[('en', 'English'), ('fr', 'French'), ('de', 'German'), ('sv', 'Swedish')], default='en', max_length=255, verbose_name='Language'),
|
||||
model_name="settings",
|
||||
name="language",
|
||||
field=models.CharField(
|
||||
choices=[
|
||||
("en", "English"),
|
||||
("fr", "French"),
|
||||
("de", "German"),
|
||||
("sv", "Swedish"),
|
||||
],
|
||||
default="en",
|
||||
max_length=255,
|
||||
verbose_name="Language",
|
||||
),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -6,13 +6,25 @@ from django.db import migrations, models
|
|||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('babybuddy', '0007_auto_20190607_1422'),
|
||||
("babybuddy", "0007_auto_20190607_1422"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='settings',
|
||||
name='language',
|
||||
field=models.CharField(choices=[('en', 'English'), ('fr', 'French'), ('de', 'German'), ('es', 'Spanish'), ('sv', 'Swedish'), ('tr', 'Turkish')], default='en', max_length=255, verbose_name='Language'),
|
||||
model_name="settings",
|
||||
name="language",
|
||||
field=models.CharField(
|
||||
choices=[
|
||||
("en", "English"),
|
||||
("fr", "French"),
|
||||
("de", "German"),
|
||||
("es", "Spanish"),
|
||||
("sv", "Swedish"),
|
||||
("tr", "Turkish"),
|
||||
],
|
||||
default="en",
|
||||
max_length=255,
|
||||
verbose_name="Language",
|
||||
),
|
||||
),
|
||||
]
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -6,13 +6,15 @@ from django.db import migrations, models
|
|||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('babybuddy', '0013_auto_20210411_1241'),
|
||||
("babybuddy", "0013_auto_20210411_1241"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='settings',
|
||||
name='dashboard_hide_empty',
|
||||
field=models.BooleanField(default=False, verbose_name='Hide Empty Dashboard Cards'),
|
||||
model_name="settings",
|
||||
name="dashboard_hide_empty",
|
||||
field=models.BooleanField(
|
||||
default=False, verbose_name="Hide Empty Dashboard Cards"
|
||||
),
|
||||
),
|
||||
]
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -5,24 +5,25 @@ from django.utils import timezone
|
|||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('babybuddy', '0016_alter_settings_timezone'),
|
||||
("babybuddy", "0016_alter_settings_timezone"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='settings',
|
||||
name='dashboard_hide_age',
|
||||
model_name="settings",
|
||||
name="dashboard_hide_age",
|
||||
field=models.DurationField(
|
||||
choices=[
|
||||
(None, 'show all data'),
|
||||
(timezone.timedelta(days=1), '1 day'),
|
||||
(timezone.timedelta(days=2), '2 days'),
|
||||
(timezone.timedelta(days=3), '3 days'),
|
||||
(timezone.timedelta(weeks=1), '1 week'),
|
||||
(timezone.timedelta(weeks=4), '4 weeks')
|
||||
(None, "show all data"),
|
||||
(timezone.timedelta(days=1), "1 day"),
|
||||
(timezone.timedelta(days=2), "2 days"),
|
||||
(timezone.timedelta(days=3), "3 days"),
|
||||
(timezone.timedelta(weeks=1), "1 week"),
|
||||
(timezone.timedelta(weeks=4), "4 weeks"),
|
||||
],
|
||||
default=None,
|
||||
null=True,
|
||||
verbose_name='Hide data older than'),
|
||||
verbose_name="Hide data older than",
|
||||
),
|
||||
),
|
||||
]
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -4,27 +4,29 @@ from django.db import migrations
|
|||
|
||||
|
||||
def update_language_en_to_en_us(apps, schema_editor):
|
||||
Settings = apps.get_model('babybuddy', 'Settings')
|
||||
Settings = apps.get_model("babybuddy", "Settings")
|
||||
for settings in Settings.objects.all():
|
||||
if settings.language == 'en':
|
||||
settings.language = 'en-US'
|
||||
if settings.language == "en":
|
||||
settings.language = "en-US"
|
||||
settings.save()
|
||||
|
||||
|
||||
def update_language_en_us_to_en(apps, schema_editor):
|
||||
Settings = apps.get_model('babybuddy', 'Settings')
|
||||
Settings = apps.get_model("babybuddy", "Settings")
|
||||
for settings in Settings.objects.all():
|
||||
if settings.language == 'en-US':
|
||||
settings.language = 'en'
|
||||
if settings.language == "en-US":
|
||||
settings.language = "en"
|
||||
settings.save()
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('babybuddy', '0019_alter_settings_timezone'),
|
||||
("babybuddy", "0019_alter_settings_timezone"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(update_language_en_to_en_us, reverse_code=update_language_en_us_to_en),
|
||||
migrations.RunPython(
|
||||
update_language_en_to_en_us, reverse_code=update_language_en_us_to_en
|
||||
),
|
||||
]
|
||||
|
|
|
@ -1,26 +1,29 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from django.contrib.auth.mixins import AccessMixin, \
|
||||
LoginRequiredMixin as LoginRequiredMixInBase, \
|
||||
PermissionRequiredMixin as PermissionRequiredMixinBase
|
||||
from django.contrib.auth.mixins import (
|
||||
AccessMixin,
|
||||
LoginRequiredMixin as LoginRequiredMixInBase,
|
||||
PermissionRequiredMixin as PermissionRequiredMixinBase,
|
||||
)
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.views.decorators.cache import never_cache
|
||||
|
||||
|
||||
@method_decorator(never_cache, name='dispatch')
|
||||
@method_decorator(never_cache, name="dispatch")
|
||||
class LoginRequiredMixin(LoginRequiredMixInBase):
|
||||
pass
|
||||
|
||||
|
||||
@method_decorator(never_cache, name='dispatch')
|
||||
@method_decorator(never_cache, name="dispatch")
|
||||
class PermissionRequiredMixin(PermissionRequiredMixinBase):
|
||||
login_url = '/login'
|
||||
login_url = "/login"
|
||||
|
||||
|
||||
@method_decorator(never_cache, name='dispatch')
|
||||
@method_decorator(never_cache, name="dispatch")
|
||||
class StaffOnlyMixin(AccessMixin):
|
||||
"""
|
||||
Verify the current user is staff.
|
||||
"""
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
if not request.user.is_staff:
|
||||
return self.handle_no_permission()
|
||||
|
|
|
@ -16,58 +16,61 @@ from rest_framework.authtoken.models import Token
|
|||
class Settings(models.Model):
|
||||
user = models.OneToOneField(User, on_delete=models.CASCADE)
|
||||
dashboard_refresh_rate = models.DurationField(
|
||||
verbose_name=_('Refresh rate'),
|
||||
help_text=_('If supported by browser, the dashboard will only refresh '
|
||||
'when visible, and also when receiving focus.'),
|
||||
verbose_name=_("Refresh rate"),
|
||||
help_text=_(
|
||||
"If supported by browser, the dashboard will only refresh "
|
||||
"when visible, and also when receiving focus."
|
||||
),
|
||||
blank=True,
|
||||
null=True,
|
||||
default=timezone.timedelta(minutes=1),
|
||||
choices=[
|
||||
(None, _('disabled')),
|
||||
(timezone.timedelta(minutes=1), _('1 min.')),
|
||||
(timezone.timedelta(minutes=2), _('2 min.')),
|
||||
(timezone.timedelta(minutes=3), _('3 min.')),
|
||||
(timezone.timedelta(minutes=4), _('4 min.')),
|
||||
(timezone.timedelta(minutes=5), _('5 min.')),
|
||||
(timezone.timedelta(minutes=10), _('10 min.')),
|
||||
(timezone.timedelta(minutes=15), _('15 min.')),
|
||||
(timezone.timedelta(minutes=30), _('30 min.')),
|
||||
])
|
||||
(None, _("disabled")),
|
||||
(timezone.timedelta(minutes=1), _("1 min.")),
|
||||
(timezone.timedelta(minutes=2), _("2 min.")),
|
||||
(timezone.timedelta(minutes=3), _("3 min.")),
|
||||
(timezone.timedelta(minutes=4), _("4 min.")),
|
||||
(timezone.timedelta(minutes=5), _("5 min.")),
|
||||
(timezone.timedelta(minutes=10), _("10 min.")),
|
||||
(timezone.timedelta(minutes=15), _("15 min.")),
|
||||
(timezone.timedelta(minutes=30), _("30 min.")),
|
||||
],
|
||||
)
|
||||
dashboard_hide_empty = models.BooleanField(
|
||||
verbose_name=_('Hide Empty Dashboard Cards'),
|
||||
default=False,
|
||||
editable=True
|
||||
verbose_name=_("Hide Empty Dashboard Cards"), default=False, editable=True
|
||||
)
|
||||
dashboard_hide_age = models.DurationField(
|
||||
verbose_name=_('Hide data older than'),
|
||||
help_text=_('This setting controls which data will be shown '
|
||||
'in the dashboard.'),
|
||||
verbose_name=_("Hide data older than"),
|
||||
help_text=_(
|
||||
"This setting controls which data will be shown " "in the dashboard."
|
||||
),
|
||||
blank=True,
|
||||
null=True,
|
||||
default=None,
|
||||
choices=[
|
||||
(None, _('show all data')),
|
||||
(timezone.timedelta(days=1), _('1 day')),
|
||||
(timezone.timedelta(days=2), _('2 days')),
|
||||
(timezone.timedelta(days=3), _('3 days')),
|
||||
(timezone.timedelta(weeks=1), _('1 week')),
|
||||
(timezone.timedelta(weeks=4), _('4 weeks')),
|
||||
])
|
||||
(None, _("show all data")),
|
||||
(timezone.timedelta(days=1), _("1 day")),
|
||||
(timezone.timedelta(days=2), _("2 days")),
|
||||
(timezone.timedelta(days=3), _("3 days")),
|
||||
(timezone.timedelta(weeks=1), _("1 week")),
|
||||
(timezone.timedelta(weeks=4), _("4 weeks")),
|
||||
],
|
||||
)
|
||||
language = models.CharField(
|
||||
choices=settings.LANGUAGES,
|
||||
default=settings.LANGUAGE_CODE,
|
||||
max_length=255,
|
||||
verbose_name=_('Language')
|
||||
verbose_name=_("Language"),
|
||||
)
|
||||
timezone = models.CharField(
|
||||
choices=tuple(zip(pytz.common_timezones, pytz.common_timezones)),
|
||||
default=timezone.get_default_timezone_name(),
|
||||
max_length=100,
|
||||
verbose_name=_('Timezone')
|
||||
verbose_name=_("Timezone"),
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return str(format_lazy(_('{user}\'s Settings'), user=self.user))
|
||||
return str(format_lazy(_("{user}'s Settings"), user=self.user))
|
||||
|
||||
def api_key(self, reset=False):
|
||||
"""
|
||||
|
|
|
@ -5,13 +5,7 @@ from django.utils.translation import gettext_lazy as _
|
|||
from dotenv import load_dotenv, find_dotenv
|
||||
|
||||
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
||||
BASE_DIR = os.path.dirname(
|
||||
os.path.dirname(
|
||||
os.path.dirname(
|
||||
os.path.abspath(__file__)
|
||||
)
|
||||
)
|
||||
)
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
# Environment variables
|
||||
# Check for and load environment variables from a .env file.
|
||||
|
@ -20,63 +14,61 @@ load_dotenv(find_dotenv())
|
|||
|
||||
# Required settings
|
||||
|
||||
ALLOWED_HOSTS = os.environ.get('ALLOWED_HOSTS', '*').split(',')
|
||||
SECRET_KEY = os.environ.get('SECRET_KEY') or None
|
||||
DEBUG = bool(strtobool(os.environ.get('DEBUG') or 'False'))
|
||||
ALLOWED_HOSTS = os.environ.get("ALLOWED_HOSTS", "*").split(",")
|
||||
SECRET_KEY = os.environ.get("SECRET_KEY") or None
|
||||
DEBUG = bool(strtobool(os.environ.get("DEBUG") or "False"))
|
||||
|
||||
|
||||
# Applications
|
||||
# https://docs.djangoproject.com/en/3.0/ref/applications/
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'api',
|
||||
'babybuddy',
|
||||
'core',
|
||||
'dashboard',
|
||||
'reports',
|
||||
|
||||
'axes',
|
||||
'django_filters',
|
||||
'rest_framework',
|
||||
'rest_framework.authtoken',
|
||||
'widget_tweaks',
|
||||
'imagekit',
|
||||
'storages',
|
||||
'import_export',
|
||||
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'django.contrib.humanize',
|
||||
"api",
|
||||
"babybuddy",
|
||||
"core",
|
||||
"dashboard",
|
||||
"reports",
|
||||
"axes",
|
||||
"django_filters",
|
||||
"rest_framework",
|
||||
"rest_framework.authtoken",
|
||||
"widget_tweaks",
|
||||
"imagekit",
|
||||
"storages",
|
||||
"import_export",
|
||||
"django.contrib.admin",
|
||||
"django.contrib.auth",
|
||||
"django.contrib.contenttypes",
|
||||
"django.contrib.sessions",
|
||||
"django.contrib.messages",
|
||||
"django.contrib.staticfiles",
|
||||
"django.contrib.humanize",
|
||||
]
|
||||
|
||||
# Middleware
|
||||
# https://docs.djangoproject.com/en/3.0/ref/middleware/
|
||||
|
||||
MIDDLEWARE = [
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'whitenoise.middleware.WhiteNoiseMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'babybuddy.middleware.RollingSessionMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'babybuddy.middleware.UserTimezoneMiddleware',
|
||||
'django.middleware.locale.LocaleMiddleware',
|
||||
'babybuddy.middleware.UserLanguageMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
'axes.middleware.AxesMiddleware',
|
||||
"django.middleware.security.SecurityMiddleware",
|
||||
"whitenoise.middleware.WhiteNoiseMiddleware",
|
||||
"django.contrib.sessions.middleware.SessionMiddleware",
|
||||
"babybuddy.middleware.RollingSessionMiddleware",
|
||||
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
||||
"babybuddy.middleware.UserTimezoneMiddleware",
|
||||
"django.middleware.locale.LocaleMiddleware",
|
||||
"babybuddy.middleware.UserLanguageMiddleware",
|
||||
"django.middleware.common.CommonMiddleware",
|
||||
"django.middleware.csrf.CsrfViewMiddleware",
|
||||
"django.contrib.messages.middleware.MessageMiddleware",
|
||||
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
||||
"axes.middleware.AxesMiddleware",
|
||||
]
|
||||
|
||||
|
||||
# URL dispatcher
|
||||
# https://docs.djangoproject.com/en/3.0/topics/http/urls/
|
||||
|
||||
ROOT_URLCONF = 'babybuddy.urls'
|
||||
ROOT_URLCONF = "babybuddy.urls"
|
||||
|
||||
|
||||
# Templates
|
||||
|
@ -84,15 +76,15 @@ ROOT_URLCONF = 'babybuddy.urls'
|
|||
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [],
|
||||
'APP_DIRS': True,
|
||||
'OPTIONS': {
|
||||
'context_processors': [
|
||||
'django.template.context_processors.debug',
|
||||
'django.template.context_processors.request',
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
||||
"DIRS": [],
|
||||
"APP_DIRS": True,
|
||||
"OPTIONS": {
|
||||
"context_processors": [
|
||||
"django.template.context_processors.debug",
|
||||
"django.template.context_processors.request",
|
||||
"django.contrib.auth.context_processors.auth",
|
||||
"django.contrib.messages.context_processors.messages",
|
||||
],
|
||||
},
|
||||
},
|
||||
|
@ -103,28 +95,30 @@ TEMPLATES = [
|
|||
# https://docs.djangoproject.com/en/3.0/ref/settings/#databases
|
||||
|
||||
config = {
|
||||
'ENGINE': os.getenv('DB_ENGINE') or 'django.db.backends.sqlite3',
|
||||
'NAME': os.getenv('DB_NAME') or os.path.join(BASE_DIR, 'data/db.sqlite3')
|
||||
"ENGINE": os.getenv("DB_ENGINE") or "django.db.backends.sqlite3",
|
||||
"NAME": os.getenv("DB_NAME") or os.path.join(BASE_DIR, "data/db.sqlite3"),
|
||||
}
|
||||
if os.getenv('DB_USER'):
|
||||
config['USER'] = os.getenv('DB_USER')
|
||||
if os.environ.get('DB_PASSWORD') or os.environ.get('POSTGRES_PASSWORD'):
|
||||
config['PASSWORD'] = os.environ.get('DB_PASSWORD') or os.environ.get('POSTGRES_PASSWORD')
|
||||
if os.getenv('DB_HOST'):
|
||||
config['HOST'] = os.getenv('DB_HOST')
|
||||
if os.getenv('DB_PORT'):
|
||||
config['PORT'] = os.getenv('DB_PORT')
|
||||
if os.getenv("DB_USER"):
|
||||
config["USER"] = os.getenv("DB_USER")
|
||||
if os.environ.get("DB_PASSWORD") or os.environ.get("POSTGRES_PASSWORD"):
|
||||
config["PASSWORD"] = os.environ.get("DB_PASSWORD") or os.environ.get(
|
||||
"POSTGRES_PASSWORD"
|
||||
)
|
||||
if os.getenv("DB_HOST"):
|
||||
config["HOST"] = os.getenv("DB_HOST")
|
||||
if os.getenv("DB_PORT"):
|
||||
config["PORT"] = os.getenv("DB_PORT")
|
||||
|
||||
DATABASES = {'default': config}
|
||||
DATABASES = {"default": config}
|
||||
|
||||
|
||||
# Cache
|
||||
# https://docs.djangoproject.com/en/3.0/topics/cache/
|
||||
|
||||
CACHES = {
|
||||
'default': {
|
||||
'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
|
||||
'LOCATION': 'cache_default',
|
||||
"default": {
|
||||
"BACKEND": "django.core.cache.backends.db.DatabaseCache",
|
||||
"LOCATION": "cache_default",
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -132,22 +126,22 @@ CACHES = {
|
|||
# WGSI
|
||||
# https://docs.djangoproject.com/en/3.0/howto/deployment/wsgi/
|
||||
|
||||
WSGI_APPLICATION = 'babybuddy.wsgi.application'
|
||||
WSGI_APPLICATION = "babybuddy.wsgi.application"
|
||||
|
||||
|
||||
# Authentication
|
||||
# https://docs.djangoproject.com/en/3.0/topics/auth/default/
|
||||
|
||||
AUTHENTICATION_BACKENDS = [
|
||||
'axes.backends.AxesBackend',
|
||||
'django.contrib.auth.backends.ModelBackend',
|
||||
"axes.backends.AxesBackend",
|
||||
"django.contrib.auth.backends.ModelBackend",
|
||||
]
|
||||
|
||||
LOGIN_REDIRECT_URL = 'babybuddy:root-router'
|
||||
LOGIN_REDIRECT_URL = "babybuddy:root-router"
|
||||
|
||||
LOGIN_URL = 'babybuddy:login'
|
||||
LOGIN_URL = "babybuddy:login"
|
||||
|
||||
LOGOUT_REDIRECT_URL = 'babybuddy:login'
|
||||
LOGOUT_REDIRECT_URL = "babybuddy:login"
|
||||
|
||||
|
||||
# Timezone
|
||||
|
@ -155,32 +149,32 @@ LOGOUT_REDIRECT_URL = 'babybuddy:login'
|
|||
|
||||
USE_TZ = True
|
||||
|
||||
TIME_ZONE = os.environ.get('TIME_ZONE') or 'UTC'
|
||||
TIME_ZONE = os.environ.get("TIME_ZONE") or "UTC"
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/3.0/topics/i18n/
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
LANGUAGE_CODE = 'en-US'
|
||||
LANGUAGE_CODE = "en-US"
|
||||
|
||||
LOCALE_PATHS = [
|
||||
os.path.join(BASE_DIR, "locale"),
|
||||
]
|
||||
|
||||
LANGUAGES = [
|
||||
('en-US', _('English (US)')),
|
||||
('en-GB', _('English (UK)')),
|
||||
('nl', _('Dutch')),
|
||||
('fr', _('French')),
|
||||
('fi', _('Finnish')),
|
||||
('de', _('German')),
|
||||
('it', _('Italian')),
|
||||
('pl', _('Polish')),
|
||||
('pt', _('Portuguese')),
|
||||
('es', _('Spanish')),
|
||||
('sv', _('Swedish')),
|
||||
('tr', _('Turkish'))
|
||||
("en-US", _("English (US)")),
|
||||
("en-GB", _("English (UK)")),
|
||||
("nl", _("Dutch")),
|
||||
("fr", _("French")),
|
||||
("fi", _("Finnish")),
|
||||
("de", _("German")),
|
||||
("it", _("Italian")),
|
||||
("pl", _("Polish")),
|
||||
("pt", _("Portuguese")),
|
||||
("es", _("Spanish")),
|
||||
("sv", _("Swedish")),
|
||||
("tr", _("Turkish")),
|
||||
]
|
||||
|
||||
|
||||
|
@ -195,49 +189,51 @@ USE_L10N = True
|
|||
# conditionals on this setting. See babybuddy/forms/en/formats.py for an example
|
||||
# implementation for the English locale.
|
||||
|
||||
USE_24_HOUR_TIME_FORMAT = bool(strtobool(os.environ.get('USE_24_HOUR_TIME_FORMAT') or 'False'))
|
||||
USE_24_HOUR_TIME_FORMAT = bool(
|
||||
strtobool(os.environ.get("USE_24_HOUR_TIME_FORMAT") or "False")
|
||||
)
|
||||
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/3.0/howto/static-files/
|
||||
# http://whitenoise.evans.io/en/stable/django.html
|
||||
|
||||
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
|
||||
STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"
|
||||
|
||||
STATICFILES_FINDERS = [
|
||||
'django.contrib.staticfiles.finders.FileSystemFinder',
|
||||
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
|
||||
"django.contrib.staticfiles.finders.FileSystemFinder",
|
||||
"django.contrib.staticfiles.finders.AppDirectoriesFinder",
|
||||
]
|
||||
|
||||
STATIC_URL = 'static/'
|
||||
STATIC_URL = "static/"
|
||||
|
||||
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
|
||||
STATIC_ROOT = os.path.join(BASE_DIR, "static")
|
||||
|
||||
WHITENOISE_ROOT = os.path.join(BASE_DIR, 'static', 'babybuddy', 'root')
|
||||
WHITENOISE_ROOT = os.path.join(BASE_DIR, "static", "babybuddy", "root")
|
||||
|
||||
|
||||
# Media files (User uploaded content)
|
||||
# https://docs.djangoproject.com/en/3.0/topics/files/
|
||||
|
||||
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
|
||||
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
|
||||
|
||||
MEDIA_URL = 'media/'
|
||||
MEDIA_URL = "media/"
|
||||
|
||||
AWS_STORAGE_BUCKET_NAME = os.environ.get('AWS_STORAGE_BUCKET_NAME') or None
|
||||
AWS_STORAGE_BUCKET_NAME = os.environ.get("AWS_STORAGE_BUCKET_NAME") or None
|
||||
|
||||
AWS_ACCESS_KEY_ID = os.environ.get('AWS_ACCESS_KEY_ID') or None
|
||||
AWS_ACCESS_KEY_ID = os.environ.get("AWS_ACCESS_KEY_ID") or None
|
||||
|
||||
AWS_SECRET_ACCESS_KEY = os.environ.get('AWS_SECRET_ACCESS_KEY') or None
|
||||
AWS_SECRET_ACCESS_KEY = os.environ.get("AWS_SECRET_ACCESS_KEY") or None
|
||||
|
||||
if AWS_STORAGE_BUCKET_NAME:
|
||||
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
|
||||
DEFAULT_FILE_STORAGE = "storages.backends.s3boto3.S3Boto3Storage"
|
||||
|
||||
|
||||
# Security
|
||||
|
||||
# https://docs.djangoproject.com/en/3.2/ref/settings/#secure-proxy-ssl-header
|
||||
if os.environ.get('SECURE_PROXY_SSL_HEADER'):
|
||||
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
|
||||
if os.environ.get("SECURE_PROXY_SSL_HEADER"):
|
||||
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
|
||||
|
||||
# https://docs.djangoproject.com/en/3.2/topics/http/sessions/#settings
|
||||
SESSION_COOKIE_HTTPONLY = True
|
||||
|
@ -250,19 +246,19 @@ CSRF_COOKIE_HTTPONLY = True
|
|||
# https://docs.djangoproject.com/en/3.2/topics/auth/passwords/
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||
'OPTIONS': {
|
||||
'min_length': 8,
|
||||
}
|
||||
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
|
||||
"OPTIONS": {
|
||||
"min_length": 8,
|
||||
},
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
|
||||
},
|
||||
]
|
||||
|
||||
|
@ -270,31 +266,28 @@ AUTH_PASSWORD_VALIDATORS = [
|
|||
# https://www.django-rest-framework.org/
|
||||
|
||||
REST_FRAMEWORK = {
|
||||
'DEFAULT_AUTHENTICATION_CLASSES': [
|
||||
'rest_framework.authentication.SessionAuthentication',
|
||||
'rest_framework.authentication.TokenAuthentication',
|
||||
"DEFAULT_AUTHENTICATION_CLASSES": [
|
||||
"rest_framework.authentication.SessionAuthentication",
|
||||
"rest_framework.authentication.TokenAuthentication",
|
||||
],
|
||||
'DEFAULT_FILTER_BACKENDS': [
|
||||
'django_filters.rest_framework.DjangoFilterBackend',
|
||||
"DEFAULT_FILTER_BACKENDS": [
|
||||
"django_filters.rest_framework.DjangoFilterBackend",
|
||||
],
|
||||
'DEFAULT_METADATA_CLASS': 'api.metadata.APIMetadata',
|
||||
'DEFAULT_PAGINATION_CLASS':
|
||||
'rest_framework.pagination.LimitOffsetPagination',
|
||||
'DEFAULT_PERMISSION_CLASSES': [
|
||||
'api.permissions.BabyBuddyDjangoModelPermissions'
|
||||
"DEFAULT_METADATA_CLASS": "api.metadata.APIMetadata",
|
||||
"DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.LimitOffsetPagination",
|
||||
"DEFAULT_PERMISSION_CLASSES": ["api.permissions.BabyBuddyDjangoModelPermissions"],
|
||||
"DEFAULT_RENDERER_CLASSES": [
|
||||
"rest_framework.renderers.JSONRenderer",
|
||||
],
|
||||
'DEFAULT_RENDERER_CLASSES': [
|
||||
'rest_framework.renderers.JSONRenderer',
|
||||
],
|
||||
'PAGE_SIZE': 100
|
||||
"PAGE_SIZE": 100,
|
||||
}
|
||||
|
||||
# Import/Export configuration
|
||||
# See https://django-import-export.readthedocs.io/
|
||||
|
||||
IMPORT_EXPORT_IMPORT_PERMISSION_CODE = 'add'
|
||||
IMPORT_EXPORT_IMPORT_PERMISSION_CODE = "add"
|
||||
|
||||
IMPORT_EXPORT_EXPORT_PERMISSION_CODE = 'change'
|
||||
IMPORT_EXPORT_EXPORT_PERMISSION_CODE = "change"
|
||||
|
||||
IMPORT_EXPORT_USE_TRANSACTIONS = True
|
||||
|
||||
|
@ -314,13 +307,13 @@ ROLLING_SESSION_REFRESH = 86400
|
|||
# Set default auto field for models.
|
||||
# See https://docs.djangoproject.com/en/3.2/releases/3.2/#customizing-type-of-auto-created-primary-keys
|
||||
|
||||
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
|
||||
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
|
||||
|
||||
# Baby Buddy configuration
|
||||
# See README.md#configuration for details about these settings.
|
||||
|
||||
BABY_BUDDY = {
|
||||
'NAP_START_MIN': os.environ.get('NAP_START_MIN') or '06:00',
|
||||
'NAP_START_MAX': os.environ.get('NAP_START_MAX') or '18:00',
|
||||
'ALLOW_UPLOADS': bool(strtobool(os.environ.get('ALLOW_UPLOADS') or 'True'))
|
||||
"NAP_START_MIN": os.environ.get("NAP_START_MIN") or "06:00",
|
||||
"NAP_START_MAX": os.environ.get("NAP_START_MAX") or "18:00",
|
||||
"ALLOW_UPLOADS": bool(strtobool(os.environ.get("ALLOW_UPLOADS") or "True")),
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
from .base import *
|
||||
|
||||
SECRET_KEY = 'CISECRETKEYIGUESS'
|
||||
SECRET_KEY = "CISECRETKEYIGUESS"
|
||||
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/3.0/howto/static-files/
|
||||
|
||||
STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.StaticFilesStorage'
|
||||
STATICFILES_STORAGE = "django.contrib.staticfiles.storage.StaticFilesStorage"
|
||||
|
|
|
@ -3,7 +3,7 @@ from .base import *
|
|||
# Quick-start development settings - unsuitable for production
|
||||
# https://docs.djangoproject.com/en/3.0/howto/deployment/checklist/
|
||||
|
||||
SECRET_KEY = 'CHANGE ME'
|
||||
SECRET_KEY = "CHANGE ME"
|
||||
DEBUG = True
|
||||
|
||||
|
||||
|
@ -14,13 +14,13 @@ DEBUG = True
|
|||
# production static files.
|
||||
|
||||
# DEBUG = False
|
||||
STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.StaticFilesStorage'
|
||||
STATICFILES_STORAGE = "django.contrib.staticfiles.storage.StaticFilesStorage"
|
||||
|
||||
|
||||
# Django Rest Framework
|
||||
# https://www.django-rest-framework.org/
|
||||
|
||||
REST_FRAMEWORK['DEFAULT_RENDERER_CLASSES'] = (
|
||||
'rest_framework.renderers.JSONRenderer',
|
||||
'rest_framework.renderers.BrowsableAPIRenderer',
|
||||
REST_FRAMEWORK["DEFAULT_RENDERER_CLASSES"] = (
|
||||
"rest_framework.renderers.JSONRenderer",
|
||||
"rest_framework.renderers.BrowsableAPIRenderer",
|
||||
)
|
||||
|
|
|
@ -5,32 +5,32 @@ from .base import *
|
|||
# Default to not allow uploads.
|
||||
# Heroku does not support file storage for this functionality.
|
||||
|
||||
BABY_BUDDY['ALLOW_UPLOADS'] = bool(strtobool(os.environ.get('ALLOW_UPLOADS') or 'False'))
|
||||
BABY_BUDDY["ALLOW_UPLOADS"] = bool(
|
||||
strtobool(os.environ.get("ALLOW_UPLOADS") or "False")
|
||||
)
|
||||
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/3.0/ref/settings/#databases
|
||||
|
||||
DATABASES = {
|
||||
'default': dj_database_url.config(conn_max_age=500)
|
||||
}
|
||||
DATABASES = {"default": dj_database_url.config(conn_max_age=500)}
|
||||
|
||||
|
||||
# Email
|
||||
# https://docs.djangoproject.com/en/3.0/topics/email/
|
||||
# https://devcenter.heroku.com/articles/sendgrid#python
|
||||
|
||||
SENDGRID_USERNAME = os.environ.get('SENDGRID_USERNAME', None) # noqa: F405
|
||||
SENDGRID_PASSWORD = os.environ.get('SENDGRID_PASSWORD', None) # noqa: F405
|
||||
SENDGRID_USERNAME = os.environ.get("SENDGRID_USERNAME", None) # noqa: F405
|
||||
SENDGRID_PASSWORD = os.environ.get("SENDGRID_PASSWORD", None) # noqa: F405
|
||||
|
||||
# Use SendGrid if we have the addon installed, else just print to console which
|
||||
# is accessible via Heroku logs
|
||||
if SENDGRID_USERNAME and SENDGRID_PASSWORD:
|
||||
EMAIL_HOST = 'smtp.sendgrid.net'
|
||||
EMAIL_HOST = "smtp.sendgrid.net"
|
||||
EMAIL_HOST_USER = SENDGRID_USERNAME
|
||||
EMAIL_HOST_PASSWORD = SENDGRID_PASSWORD
|
||||
EMAIL_PORT = 587
|
||||
EMAIL_USE_TLS = True
|
||||
EMAIL_TIMEOUT = 60
|
||||
else:
|
||||
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
|
||||
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
|
||||
|
|
|
@ -3,24 +3,24 @@ from .base import *
|
|||
# Production settings
|
||||
# See babybuddy.settings.base for additional settings information.
|
||||
|
||||
SECRET_KEY = ''
|
||||
SECRET_KEY = ""
|
||||
|
||||
ALLOWED_HOSTS = ['']
|
||||
ALLOWED_HOSTS = [""]
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/3.0/ref/settings/#databases
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': os.path.join(BASE_DIR, '../data/db.sqlite3'),
|
||||
"default": {
|
||||
"ENGINE": "django.db.backends.sqlite3",
|
||||
"NAME": os.path.join(BASE_DIR, "../data/db.sqlite3"),
|
||||
}
|
||||
}
|
||||
|
||||
# Media files
|
||||
# https://docs.djangoproject.com/en/3.0/topics/files/
|
||||
|
||||
MEDIA_ROOT = os.path.join(BASE_DIR, '../data/media')
|
||||
MEDIA_ROOT = os.path.join(BASE_DIR, "../data/media")
|
||||
|
||||
# Security
|
||||
# After setting up SSL, uncomment the settings below for enhanced security of
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
from .base import *
|
||||
|
||||
SECRET_KEY = 'TESTS'
|
||||
SECRET_KEY = "TESTS"
|
||||
|
||||
# Password hasher configuration
|
||||
# See https://docs.djangoproject.com/en/3.2/ref/settings/#password-hashers
|
||||
# See https://docs.djangoproject.com/en/3.2/topics/testing/overview/#password-hashing
|
||||
|
||||
PASSWORD_HASHERS = [
|
||||
'django.contrib.auth.hashers.MD5PasswordHasher',
|
||||
"django.contrib.auth.hashers.MD5PasswordHasher",
|
||||
]
|
||||
|
||||
# Axes configuration
|
||||
|
|
|
@ -19,12 +19,11 @@ def relative_url(context, field_name, value):
|
|||
:param value: the new value for field_name.
|
||||
:return: encoded relative url with updated query string.
|
||||
"""
|
||||
url = '?{}={}'.format(field_name, value)
|
||||
querystring = context['request'].GET.urlencode().split('&')
|
||||
filtered_querystring = filter(
|
||||
lambda p: p.split('=')[0] != field_name, querystring)
|
||||
encoded_querystring = '&'.join(filtered_querystring)
|
||||
return '{}&{}'.format(url, encoded_querystring)
|
||||
url = "?{}={}".format(field_name, value)
|
||||
querystring = context["request"].GET.urlencode().split("&")
|
||||
filtered_querystring = filter(lambda p: p.split("=")[0] != field_name, querystring)
|
||||
encoded_querystring = "&".join(filtered_querystring)
|
||||
return "{}&{}".format(url, encoded_querystring)
|
||||
|
||||
|
||||
@register.simple_tag()
|
||||
|
@ -34,7 +33,7 @@ def version_string():
|
|||
|
||||
:return: version string ('n.n.n (commit)').
|
||||
"""
|
||||
config = apps.get_app_config('babybuddy')
|
||||
config = apps.get_app_config("babybuddy")
|
||||
return config.version_string
|
||||
|
||||
|
||||
|
|
|
@ -10,16 +10,16 @@ from babybuddy.middleware import update_en_gb_date_formats
|
|||
|
||||
|
||||
class GbFormatsTestCase(TestCase):
|
||||
@override_settings(LANGUAGE_CODE='en-GB')
|
||||
@override_settings(LANGUAGE_CODE="en-GB")
|
||||
def test_datetime_input_formats(self):
|
||||
update_en_gb_date_formats()
|
||||
field = DateTimeField()
|
||||
supported_custom_examples = [
|
||||
'20/01/2020',
|
||||
'20/01/2020 9:30 AM',
|
||||
'20/01/2020 9:30:03 AM',
|
||||
'01/10/2020 11:30 PM',
|
||||
'01/10/2020 11:30:03 AM',
|
||||
"20/01/2020",
|
||||
"20/01/2020 9:30 AM",
|
||||
"20/01/2020 9:30:03 AM",
|
||||
"01/10/2020 11:30 PM",
|
||||
"01/10/2020 11:30:03 AM",
|
||||
]
|
||||
|
||||
for example in supported_custom_examples:
|
||||
|
@ -30,18 +30,18 @@ class GbFormatsTestCase(TestCase):
|
|||
self.fail('Format of "{}" not recognized!'.format(example))
|
||||
|
||||
with self.assertRaises(ValidationError):
|
||||
field.to_python('invalid date string!')
|
||||
field.to_python("invalid date string!")
|
||||
|
||||
# @tag('isolate')
|
||||
@override_settings(LANGUAGE_CODE='en-GB', USE_24_HOUR_TIME_FORMAT=True)
|
||||
@override_settings(LANGUAGE_CODE="en-GB", USE_24_HOUR_TIME_FORMAT=True)
|
||||
def test_use_24_hour_time_format(self):
|
||||
update_en_gb_date_formats()
|
||||
field = DateTimeField()
|
||||
supported_custom_examples = [
|
||||
'25/10/2006 2:30:59',
|
||||
'25/10/2006 2:30',
|
||||
'25/10/2006 14:30:59',
|
||||
'25/10/2006 14:30',
|
||||
"25/10/2006 2:30:59",
|
||||
"25/10/2006 2:30",
|
||||
"25/10/2006 14:30:59",
|
||||
"25/10/2006 14:30",
|
||||
]
|
||||
|
||||
for example in supported_custom_examples:
|
||||
|
@ -52,20 +52,16 @@ class GbFormatsTestCase(TestCase):
|
|||
self.fail('Format of "{}" not recognized!'.format(example))
|
||||
|
||||
with self.assertRaises(ValidationError):
|
||||
field.to_python('invalid date string!')
|
||||
field.to_python("invalid date string!")
|
||||
|
||||
dt = datetime.datetime(year=2011, month=11, day=4, hour=23, minute=5,
|
||||
second=59)
|
||||
self.assertEqual(
|
||||
date_format(dt, 'DATETIME_FORMAT'), '4 November 2011 23:05:59')
|
||||
dt = datetime.datetime(year=2011, month=11, day=4, hour=23, minute=5, second=59)
|
||||
self.assertEqual(date_format(dt, "DATETIME_FORMAT"), "4 November 2011 23:05:59")
|
||||
|
||||
dt = datetime.datetime(year=2011, month=11, day=4, hour=2, minute=5,
|
||||
second=59)
|
||||
self.assertEqual(
|
||||
date_format(dt, 'SHORT_DATETIME_FORMAT'), '04/11/2011 02:05')
|
||||
dt = datetime.datetime(year=2011, month=11, day=4, hour=2, minute=5, second=59)
|
||||
self.assertEqual(date_format(dt, "SHORT_DATETIME_FORMAT"), "04/11/2011 02:05")
|
||||
|
||||
t = datetime.time(hour=16, minute=2, second=25)
|
||||
self.assertEqual(time_format(t), '16:02')
|
||||
self.assertEqual(time_format(t), "16:02")
|
||||
|
||||
# def test_short_month_day_format(self):
|
||||
# update_en_gb_date_formats()
|
||||
|
|
|
@ -14,10 +14,10 @@ class FormatsTestCase(TestCase):
|
|||
update_en_us_date_formats()
|
||||
field = DateTimeField()
|
||||
supported_custom_examples = [
|
||||
'01/20/2020 9:30 AM',
|
||||
'01/20/2020 9:30:03 AM',
|
||||
'10/01/2020 11:30 PM',
|
||||
'10/01/2020 11:30:03 AM',
|
||||
"01/20/2020 9:30 AM",
|
||||
"01/20/2020 9:30:03 AM",
|
||||
"10/01/2020 11:30 PM",
|
||||
"10/01/2020 11:30:03 AM",
|
||||
]
|
||||
|
||||
for example in supported_custom_examples:
|
||||
|
@ -28,18 +28,18 @@ class FormatsTestCase(TestCase):
|
|||
self.fail('Format of "{}" not recognized!'.format(example))
|
||||
|
||||
with self.assertRaises(ValidationError):
|
||||
field.to_python('invalid date string!')
|
||||
field.to_python("invalid date string!")
|
||||
|
||||
@tag('isolate')
|
||||
@override_settings(LANGUAGE_CODE='en-US', USE_24_HOUR_TIME_FORMAT=True)
|
||||
@tag("isolate")
|
||||
@override_settings(LANGUAGE_CODE="en-US", USE_24_HOUR_TIME_FORMAT=True)
|
||||
def test_use_24_hour_time_format(self):
|
||||
update_en_us_date_formats()
|
||||
field = DateTimeField()
|
||||
supported_custom_examples = [
|
||||
'10/25/2006 2:30:59',
|
||||
'10/25/2006 2:30',
|
||||
'10/25/2006 14:30:59',
|
||||
'10/25/2006 14:30',
|
||||
"10/25/2006 2:30:59",
|
||||
"10/25/2006 2:30",
|
||||
"10/25/2006 14:30:59",
|
||||
"10/25/2006 14:30",
|
||||
]
|
||||
|
||||
for example in supported_custom_examples:
|
||||
|
@ -50,23 +50,18 @@ class FormatsTestCase(TestCase):
|
|||
self.fail('Format of "{}" not recognized!'.format(example))
|
||||
|
||||
with self.assertRaises(ValidationError):
|
||||
field.to_python('invalid date string!')
|
||||
field.to_python("invalid date string!")
|
||||
|
||||
dt = datetime.datetime(year=2011, month=11, day=4, hour=23, minute=5,
|
||||
second=59)
|
||||
self.assertEqual(
|
||||
date_format(dt, 'DATETIME_FORMAT'), 'Nov. 4, 2011, 23:05:59')
|
||||
dt = datetime.datetime(year=2011, month=11, day=4, hour=23, minute=5, second=59)
|
||||
self.assertEqual(date_format(dt, "DATETIME_FORMAT"), "Nov. 4, 2011, 23:05:59")
|
||||
|
||||
dt = datetime.datetime(year=2011, month=11, day=4, hour=2, minute=5,
|
||||
second=59)
|
||||
self.assertEqual(
|
||||
date_format(dt, 'SHORT_DATETIME_FORMAT'), '11/04/2011 2:05:59')
|
||||
dt = datetime.datetime(year=2011, month=11, day=4, hour=2, minute=5, second=59)
|
||||
self.assertEqual(date_format(dt, "SHORT_DATETIME_FORMAT"), "11/04/2011 2:05:59")
|
||||
|
||||
t = datetime.time(hour=16, minute=2, second=25)
|
||||
self.assertEqual(time_format(t), '16:02:25')
|
||||
self.assertEqual(time_format(t), "16:02:25")
|
||||
|
||||
def test_short_month_day_format(self):
|
||||
update_en_us_date_formats()
|
||||
dt = datetime.datetime(year=2021, month=7, day=31, hour=5, minute=5,
|
||||
second=5)
|
||||
self.assertEqual(date_format(dt, 'SHORT_MONTH_DAY_FORMAT'), 'Jul 31')
|
||||
dt = datetime.datetime(year=2021, month=7, day=31, hour=5, minute=5, second=5)
|
||||
self.assertEqual(date_format(dt, "SHORT_MONTH_DAY_FORMAT"), "Jul 31")
|
||||
|
|
|
@ -8,17 +8,17 @@ from core.models import Child
|
|||
|
||||
class CommandsTestCase(TransactionTestCase):
|
||||
def test_migrate(self):
|
||||
call_command('migrate', verbosity=0)
|
||||
self.assertIsInstance(User.objects.get(username='admin'), User)
|
||||
call_command("migrate", verbosity=0)
|
||||
self.assertIsInstance(User.objects.get(username="admin"), User)
|
||||
|
||||
def test_fake(self):
|
||||
call_command('migrate', verbosity=0)
|
||||
call_command('fake', children=1, days=7, verbosity=0)
|
||||
call_command("migrate", verbosity=0)
|
||||
call_command("fake", children=1, days=7, verbosity=0)
|
||||
self.assertEqual(Child.objects.count(), 1)
|
||||
call_command('fake', children=2, days=7, verbosity=0)
|
||||
call_command("fake", children=2, days=7, verbosity=0)
|
||||
self.assertEqual(Child.objects.count(), 3)
|
||||
|
||||
def test_reset(self):
|
||||
call_command('reset', verbosity=0, interactive=False)
|
||||
self.assertIsInstance(User.objects.get(username='admin'), User)
|
||||
call_command("reset", verbosity=0, interactive=False)
|
||||
self.assertIsInstance(User.objects.get(username="admin"), User)
|
||||
self.assertEqual(Child.objects.count(), 1)
|
||||
|
|
|
@ -14,55 +14,58 @@ class FormsTestCase(TestCase):
|
|||
def setUpClass(cls):
|
||||
super(FormsTestCase, cls).setUpClass()
|
||||
fake = Factory.create()
|
||||
call_command('migrate', verbosity=0)
|
||||
call_command('fake', verbosity=0)
|
||||
call_command("migrate", verbosity=0)
|
||||
call_command("fake", verbosity=0)
|
||||
|
||||
cls.c = HttpClient()
|
||||
|
||||
fake_user = fake.simple_profile()
|
||||
cls.credentials = {
|
||||
'username': fake_user['username'],
|
||||
'password': fake.password()
|
||||
"username": fake_user["username"],
|
||||
"password": fake.password(),
|
||||
}
|
||||
cls.user = User.objects.create_user(
|
||||
is_superuser=True, **cls.credentials)
|
||||
cls.user = User.objects.create_user(is_superuser=True, **cls.credentials)
|
||||
|
||||
cls.settings_template = {
|
||||
'first_name': 'User',
|
||||
'last_name': 'Name',
|
||||
'email': 'user@user.user',
|
||||
'dashboard_refresh_rate': '',
|
||||
'language': 'en-US',
|
||||
'timezone': 'UTC',
|
||||
'next': '/user/settings/'
|
||||
"first_name": "User",
|
||||
"last_name": "Name",
|
||||
"email": "user@user.user",
|
||||
"dashboard_refresh_rate": "",
|
||||
"language": "en-US",
|
||||
"timezone": "UTC",
|
||||
"next": "/user/settings/",
|
||||
}
|
||||
|
||||
def test_change_password(self):
|
||||
self.c.login(**self.credentials)
|
||||
|
||||
page = self.c.get('/user/password/')
|
||||
page = self.c.get("/user/password/")
|
||||
self.assertEqual(page.status_code, 200)
|
||||
|
||||
params = {
|
||||
'old_password': 'wrong',
|
||||
'new_password1': 'mynewpassword',
|
||||
'new_password2': 'notmynewpassword'
|
||||
"old_password": "wrong",
|
||||
"new_password1": "mynewpassword",
|
||||
"new_password2": "notmynewpassword",
|
||||
}
|
||||
|
||||
page = self.c.post('/user/password/', params)
|
||||
page = self.c.post("/user/password/", params)
|
||||
self.assertEqual(page.status_code, 200)
|
||||
self.assertFormError(page, 'form', 'old_password',
|
||||
'Your old password was entered incorrectly. '
|
||||
'Please enter it again.')
|
||||
self.assertFormError(
|
||||
page,
|
||||
"form",
|
||||
"old_password",
|
||||
"Your old password was entered incorrectly. " "Please enter it again.",
|
||||
)
|
||||
|
||||
params['old_password'] = self.credentials['password']
|
||||
page = self.c.post('/user/password/', params)
|
||||
params["old_password"] = self.credentials["password"]
|
||||
page = self.c.post("/user/password/", params)
|
||||
self.assertEqual(page.status_code, 200)
|
||||
self.assertFormError(page, 'form', 'new_password2',
|
||||
"The two password fields didn’t match.")
|
||||
self.assertFormError(
|
||||
page, "form", "new_password2", "The two password fields didn’t match."
|
||||
)
|
||||
|
||||
params['new_password2'] = 'mynewpassword'
|
||||
page = self.c.post('/user/password/', params)
|
||||
params["new_password2"] = "mynewpassword"
|
||||
page = self.c.post("/user/password/", params)
|
||||
self.assertEqual(page.status_code, 200)
|
||||
|
||||
def test_user_forms(self):
|
||||
|
@ -71,38 +74,38 @@ class FormsTestCase(TestCase):
|
|||
self.c.login(**self.credentials)
|
||||
|
||||
params = {
|
||||
'username': 'username',
|
||||
'first_name': 'User',
|
||||
'last_name': 'Name',
|
||||
'email': 'user@user.user',
|
||||
'password1': 'd47o8dD&#hu3ulu3',
|
||||
'password2': 'd47o8dD&#hu3ulu3'
|
||||
"username": "username",
|
||||
"first_name": "User",
|
||||
"last_name": "Name",
|
||||
"email": "user@user.user",
|
||||
"password1": "d47o8dD&#hu3ulu3",
|
||||
"password2": "d47o8dD&#hu3ulu3",
|
||||
}
|
||||
|
||||
page = self.c.post('/users/add/', params)
|
||||
page = self.c.post("/users/add/", params)
|
||||
self.assertEqual(page.status_code, 302)
|
||||
new_user = User.objects.get(username='username')
|
||||
new_user = User.objects.get(username="username")
|
||||
self.assertIsInstance(new_user, User)
|
||||
|
||||
params['first_name'] = 'Changed'
|
||||
page = self.c.post('/users/{}/edit/'.format(new_user.id), params)
|
||||
params["first_name"] = "Changed"
|
||||
page = self.c.post("/users/{}/edit/".format(new_user.id), params)
|
||||
self.assertEqual(page.status_code, 302)
|
||||
new_user.refresh_from_db()
|
||||
self.assertEqual(new_user.first_name, params['first_name'])
|
||||
self.assertEqual(new_user.first_name, params["first_name"])
|
||||
|
||||
page = self.c.post('/users/{}/delete/'.format(new_user.id))
|
||||
page = self.c.post("/users/{}/delete/".format(new_user.id))
|
||||
self.assertEqual(page.status_code, 302)
|
||||
self.assertQuerysetEqual(User.objects.filter(username='username'), [])
|
||||
self.assertQuerysetEqual(User.objects.filter(username="username"), [])
|
||||
|
||||
def test_user_settings(self):
|
||||
self.c.login(**self.credentials)
|
||||
|
||||
params = self.settings_template.copy()
|
||||
params['first_name'] = 'New First Name'
|
||||
params["first_name"] = "New First Name"
|
||||
|
||||
page = self.c.post('/user/settings/', params, follow=True)
|
||||
page = self.c.post("/user/settings/", params, follow=True)
|
||||
self.assertEqual(page.status_code, 200)
|
||||
self.assertContains(page, 'New First Name')
|
||||
self.assertContains(page, "New First Name")
|
||||
|
||||
def test_user_regenerate_api_key(self):
|
||||
self.c.login(**self.credentials)
|
||||
|
@ -110,53 +113,50 @@ class FormsTestCase(TestCase):
|
|||
api_key_before = User.objects.get(pk=self.user.id).settings.api_key()
|
||||
|
||||
params = self.settings_template.copy()
|
||||
params['api_key_regenerate'] = 'Regenerate'
|
||||
params["api_key_regenerate"] = "Regenerate"
|
||||
|
||||
page = self.c.post('/user/settings/', params, follow=True)
|
||||
page = self.c.post("/user/settings/", params, follow=True)
|
||||
self.assertEqual(page.status_code, 200)
|
||||
self.assertNotEqual(
|
||||
api_key_before,
|
||||
User.objects.get(pk=self.user.id).settings.api_key()
|
||||
api_key_before, User.objects.get(pk=self.user.id).settings.api_key()
|
||||
)
|
||||
|
||||
def test_user_settings_invalid(self):
|
||||
self.c.login(**self.credentials)
|
||||
|
||||
params = self.settings_template.copy()
|
||||
params['email'] = 'Not an email address'
|
||||
params["email"] = "Not an email address"
|
||||
|
||||
page = self.c.post('/user/settings/', params)
|
||||
page = self.c.post("/user/settings/", params)
|
||||
self.assertEqual(page.status_code, 200)
|
||||
self.assertFormError(page, 'user_form', 'email',
|
||||
'Enter a valid email address.')
|
||||
self.assertFormError(page, "user_form", "email", "Enter a valid email address.")
|
||||
|
||||
def test_user_settings_language(self):
|
||||
self.c.login(**self.credentials)
|
||||
|
||||
params = self.settings_template.copy()
|
||||
params['language'] = 'fr'
|
||||
page = self.c.post('/user/settings/', data=params, follow=True)
|
||||
self.assertContains(page, 'Paramètres Utilisateur')
|
||||
params["language"] = "fr"
|
||||
page = self.c.post("/user/settings/", data=params, follow=True)
|
||||
self.assertContains(page, "Paramètres Utilisateur")
|
||||
|
||||
@override_settings(TIME_ZONE='US/Eastern')
|
||||
@override_settings(TIME_ZONE="US/Eastern")
|
||||
def test_user_settings_timezone(self):
|
||||
self.c.login(**self.credentials)
|
||||
|
||||
self.assertEqual(timezone.get_default_timezone_name(), 'US/Eastern')
|
||||
self.assertEqual(timezone.get_default_timezone_name(), "US/Eastern")
|
||||
params = self.settings_template.copy()
|
||||
params['timezone'] = 'US/Pacific'
|
||||
page = self.c.post('/user/settings/', data=params, follow=True)
|
||||
params["timezone"] = "US/Pacific"
|
||||
page = self.c.post("/user/settings/", data=params, follow=True)
|
||||
self.assertEqual(page.status_code, 200)
|
||||
self.assertEqual(timezone.get_current_timezone_name(),
|
||||
params['timezone'])
|
||||
self.assertEqual(timezone.get_current_timezone_name(), params["timezone"])
|
||||
|
||||
def test_user_settings_dashboard_hide_empty_on(self):
|
||||
self.c.login(**self.credentials)
|
||||
|
||||
params = self.settings_template.copy()
|
||||
params['dashboard_hide_empty'] = 'on'
|
||||
params["dashboard_hide_empty"] = "on"
|
||||
|
||||
page = self.c.post('/user/settings/', data=params, follow=True)
|
||||
page = self.c.post("/user/settings/", data=params, follow=True)
|
||||
self.assertEqual(page.status_code, 200)
|
||||
self.user.refresh_from_db()
|
||||
self.assertTrue(self.user.settings.dashboard_hide_empty)
|
||||
|
@ -165,22 +165,24 @@ class FormsTestCase(TestCase):
|
|||
self.c.login(**self.credentials)
|
||||
|
||||
params = self.settings_template.copy()
|
||||
params['dashboard_refresh_rate'] = '0:05:00'
|
||||
params["dashboard_refresh_rate"] = "0:05:00"
|
||||
|
||||
page = self.c.post('/user/settings/', data=params, follow=True)
|
||||
page = self.c.post("/user/settings/", data=params, follow=True)
|
||||
self.assertEqual(page.status_code, 200)
|
||||
self.user.refresh_from_db()
|
||||
self.assertEqual(self.user.settings.dashboard_refresh_rate,
|
||||
datetime.timedelta(seconds=300))
|
||||
self.assertEqual(
|
||||
self.user.settings.dashboard_refresh_rate, datetime.timedelta(seconds=300)
|
||||
)
|
||||
|
||||
def test_user_settings_dashboard_hide_age(self):
|
||||
self.c.login(**self.credentials)
|
||||
|
||||
params = self.settings_template.copy()
|
||||
params['dashboard_hide_age'] = '1 day, 0:00:00'
|
||||
params["dashboard_hide_age"] = "1 day, 0:00:00"
|
||||
|
||||
page = self.c.post('/user/settings/', data=params, follow=True)
|
||||
page = self.c.post("/user/settings/", data=params, follow=True)
|
||||
self.assertEqual(page.status_code, 200)
|
||||
self.user.refresh_from_db()
|
||||
self.assertEqual(self.user.settings.dashboard_hide_age,
|
||||
datetime.timedelta(days=1))
|
||||
self.assertEqual(
|
||||
self.user.settings.dashboard_hide_age, datetime.timedelta(days=1)
|
||||
)
|
||||
|
|
|
@ -8,22 +8,18 @@ from babybuddy.models import Settings
|
|||
|
||||
class SettingsTestCase(TestCase):
|
||||
def setUp(self):
|
||||
call_command('migrate', verbosity=0)
|
||||
call_command("migrate", verbosity=0)
|
||||
|
||||
def test_settings(self):
|
||||
credentials = {
|
||||
'username': 'Test',
|
||||
'password': 'User'
|
||||
}
|
||||
credentials = {"username": "Test", "password": "User"}
|
||||
user = User.objects.create_user(is_superuser=True, **credentials)
|
||||
self.assertIsInstance(user.settings, Settings)
|
||||
self.assertEqual(
|
||||
user.settings.dashboard_refresh_rate_milliseconds, 60000)
|
||||
self.assertEqual(user.settings.dashboard_refresh_rate_milliseconds, 60000)
|
||||
|
||||
user.settings.dashboard_refresh_rate = None
|
||||
user.save()
|
||||
self.assertIsNone(user.settings.dashboard_refresh_rate_milliseconds)
|
||||
|
||||
user.settings.language = 'fr'
|
||||
user.settings.language = "fr"
|
||||
user.save()
|
||||
self.assertEqual(user.settings.language, 'fr')
|
||||
self.assertEqual(user.settings.language, "fr")
|
||||
|
|
|
@ -9,9 +9,11 @@ from babybuddy.templatetags import babybuddy_tags
|
|||
class TemplateTagsTestCase(TestCase):
|
||||
def test_child_count(self):
|
||||
self.assertEqual(babybuddy_tags.get_child_count(), 0)
|
||||
Child.objects.create(first_name='Test', last_name='Child',
|
||||
birth_date=timezone.localdate())
|
||||
Child.objects.create(
|
||||
first_name="Test", last_name="Child", birth_date=timezone.localdate()
|
||||
)
|
||||
self.assertEqual(babybuddy_tags.get_child_count(), 1)
|
||||
Child.objects.create(first_name='Test', last_name='Child 2',
|
||||
birth_date=timezone.localdate())
|
||||
Child.objects.create(
|
||||
first_name="Test", last_name="Child 2", birth_date=timezone.localdate()
|
||||
)
|
||||
self.assertEqual(babybuddy_tags.get_child_count(), 2)
|
||||
|
|
|
@ -14,63 +14,62 @@ class ViewsTestCase(TestCase):
|
|||
def setUpClass(cls):
|
||||
super(ViewsTestCase, cls).setUpClass()
|
||||
fake = Factory.create()
|
||||
call_command('migrate', verbosity=0)
|
||||
call_command("migrate", verbosity=0)
|
||||
|
||||
cls.c = HttpClient()
|
||||
|
||||
fake_user = fake.simple_profile()
|
||||
cls.credentials = {
|
||||
'username': fake_user['username'],
|
||||
'password': fake.password()
|
||||
"username": fake_user["username"],
|
||||
"password": fake.password(),
|
||||
}
|
||||
cls.user = User.objects.create_user(
|
||||
is_superuser=True, **cls.credentials)
|
||||
cls.user = User.objects.create_user(is_superuser=True, **cls.credentials)
|
||||
|
||||
cls.c.login(**cls.credentials)
|
||||
|
||||
def test_root_router(self):
|
||||
page = self.c.get('/')
|
||||
self.assertEqual(page.url, '/dashboard/')
|
||||
page = self.c.get("/")
|
||||
self.assertEqual(page.url, "/dashboard/")
|
||||
|
||||
@override_settings(ROLLING_SESSION_REFRESH=1)
|
||||
def test_rolling_sessions(self):
|
||||
self.c.get('/')
|
||||
session1 = str(self.c.cookies['sessionid'])
|
||||
self.c.get("/")
|
||||
session1 = str(self.c.cookies["sessionid"])
|
||||
# Sleep longer than ROLLING_SESSION_REFRESH.
|
||||
time.sleep(2)
|
||||
self.c.get('/')
|
||||
session2 = str(self.c.cookies['sessionid'])
|
||||
self.c.get('/')
|
||||
session3 = str(self.c.cookies['sessionid'])
|
||||
self.c.get("/")
|
||||
session2 = str(self.c.cookies["sessionid"])
|
||||
self.c.get("/")
|
||||
session3 = str(self.c.cookies["sessionid"])
|
||||
self.assertNotEqual(session1, session2)
|
||||
self.assertEqual(session2, session3)
|
||||
|
||||
def test_user_settings(self):
|
||||
page = self.c.get('/user/settings/')
|
||||
page = self.c.get("/user/settings/")
|
||||
self.assertEqual(page.status_code, 200)
|
||||
|
||||
def test_user_views(self):
|
||||
# Staff setting is required to access user management.
|
||||
page = self.c.get('/users/')
|
||||
page = self.c.get("/users/")
|
||||
self.assertEqual(page.status_code, 403)
|
||||
self.user.is_staff = True
|
||||
self.user.save()
|
||||
|
||||
page = self.c.get('/users/')
|
||||
page = self.c.get("/users/")
|
||||
self.assertEqual(page.status_code, 200)
|
||||
page = self.c.get('/users/add/')
|
||||
page = self.c.get("/users/add/")
|
||||
self.assertEqual(page.status_code, 200)
|
||||
|
||||
entry = User.objects.first()
|
||||
page = self.c.get('/users/{}/edit/'.format(entry.id))
|
||||
page = self.c.get("/users/{}/edit/".format(entry.id))
|
||||
self.assertEqual(page.status_code, 200)
|
||||
page = self.c.get('/users/{}/delete/'.format(entry.id))
|
||||
page = self.c.get("/users/{}/delete/".format(entry.id))
|
||||
self.assertEqual(page.status_code, 200)
|
||||
|
||||
def test_welcome(self):
|
||||
page = self.c.get('/welcome/')
|
||||
page = self.c.get("/welcome/")
|
||||
self.assertEqual(page.status_code, 200)
|
||||
|
||||
def test_logout_get_fails(self):
|
||||
page = self.c.get('/logout/')
|
||||
page = self.c.get("/logout/")
|
||||
self.assertEqual(page.status_code, 405)
|
||||
|
|
|
@ -8,54 +8,30 @@ from django.urls import include, path
|
|||
from . import views
|
||||
|
||||
app_patterns = [
|
||||
path('login/', auth_views.LoginView.as_view(), name='login'),
|
||||
path('logout/', views.LogoutView.as_view(), name='logout'),
|
||||
path("login/", auth_views.LoginView.as_view(), name="login"),
|
||||
path("logout/", views.LogoutView.as_view(), name="logout"),
|
||||
path(
|
||||
'password_reset/',
|
||||
auth_views.PasswordResetView.as_view(),
|
||||
name='password_reset'
|
||||
),
|
||||
|
||||
path('', views.RootRouter.as_view(), name='root-router'),
|
||||
path('welcome/', views.Welcome.as_view(), name='welcome'),
|
||||
|
||||
path('users/', views.UserList.as_view(), name='user-list'),
|
||||
path('users/add/', views.UserAdd.as_view(), name='user-add'),
|
||||
path(
|
||||
'users/<int:pk>/edit/',
|
||||
views.UserUpdate.as_view(),
|
||||
name='user-update'
|
||||
),
|
||||
path(
|
||||
'users/<int:pk>/delete/',
|
||||
views.UserDelete.as_view(),
|
||||
name='user-delete'
|
||||
),
|
||||
|
||||
path(
|
||||
'user/password/',
|
||||
views.UserPassword.as_view(),
|
||||
name='user-password'
|
||||
),
|
||||
path(
|
||||
'user/settings/',
|
||||
views.UserSettings.as_view(),
|
||||
name='user-settings'
|
||||
"password_reset/", auth_views.PasswordResetView.as_view(), name="password_reset"
|
||||
),
|
||||
path("", views.RootRouter.as_view(), name="root-router"),
|
||||
path("welcome/", views.Welcome.as_view(), name="welcome"),
|
||||
path("users/", views.UserList.as_view(), name="user-list"),
|
||||
path("users/add/", views.UserAdd.as_view(), name="user-add"),
|
||||
path("users/<int:pk>/edit/", views.UserUpdate.as_view(), name="user-update"),
|
||||
path("users/<int:pk>/delete/", views.UserDelete.as_view(), name="user-delete"),
|
||||
path("user/password/", views.UserPassword.as_view(), name="user-password"),
|
||||
path("user/settings/", views.UserSettings.as_view(), name="user-settings"),
|
||||
]
|
||||
|
||||
urlpatterns = [
|
||||
path('admin/', admin.site.urls),
|
||||
path('', include('api.urls', namespace='api')),
|
||||
path('', include((app_patterns, 'babybuddy'), namespace='babybuddy')),
|
||||
path('user/lang', include('django.conf.urls.i18n')),
|
||||
path('', include('core.urls', namespace='core')),
|
||||
path('', include('dashboard.urls', namespace='dashboard')),
|
||||
path('', include('reports.urls', namespace='reports')),
|
||||
path("admin/", admin.site.urls),
|
||||
path("", include("api.urls", namespace="api")),
|
||||
path("", include((app_patterns, "babybuddy"), namespace="babybuddy")),
|
||||
path("user/lang", include("django.conf.urls.i18n")),
|
||||
path("", include("core.urls", namespace="core")),
|
||||
path("", include("dashboard.urls", namespace="dashboard")),
|
||||
path("", include("reports.urls", namespace="reports")),
|
||||
]
|
||||
|
||||
if settings.DEBUG: # pragma: no cover
|
||||
urlpatterns += static(
|
||||
settings.MEDIA_URL,
|
||||
document_root=settings.MEDIA_ROOT
|
||||
)
|
||||
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
|
|
|
@ -22,16 +22,16 @@ from django.views.i18n import set_language
|
|||
from django_filters.views import FilterView
|
||||
|
||||
from babybuddy import forms
|
||||
from babybuddy.mixins import LoginRequiredMixin, PermissionRequiredMixin, \
|
||||
StaffOnlyMixin
|
||||
from babybuddy.mixins import LoginRequiredMixin, PermissionRequiredMixin, StaffOnlyMixin
|
||||
|
||||
|
||||
class RootRouter(LoginRequiredMixin, RedirectView):
|
||||
"""
|
||||
Redirects to the site dashboard.
|
||||
"""
|
||||
|
||||
def get_redirect_url(self, *args, **kwargs):
|
||||
self.url = reverse('dashboard:dashboard')
|
||||
self.url = reverse("dashboard:dashboard")
|
||||
return super(RootRouter, self).get_redirect_url(self, *args, **kwargs)
|
||||
|
||||
|
||||
|
@ -40,86 +40,85 @@ class BabyBuddyFilterView(FilterView):
|
|||
Disables "strictness" for django-filter. It is unclear from the
|
||||
documentation exactly what this does...
|
||||
"""
|
||||
|
||||
# TODO Figure out the correct way to use this.
|
||||
strict = False
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
children = {
|
||||
o.child for o in context['object_list'] if hasattr(o, "child")
|
||||
}
|
||||
children = {o.child for o in context["object_list"] if hasattr(o, "child")}
|
||||
if len(children) == 1:
|
||||
context['unique_child'] = True
|
||||
context["unique_child"] = True
|
||||
return context
|
||||
|
||||
|
||||
@method_decorator(csrf_protect, name='dispatch')
|
||||
@method_decorator(never_cache, name='dispatch')
|
||||
@method_decorator(require_POST, name='dispatch')
|
||||
@method_decorator(csrf_protect, name="dispatch")
|
||||
@method_decorator(never_cache, name="dispatch")
|
||||
@method_decorator(require_POST, name="dispatch")
|
||||
class LogoutView(LogoutViewBase):
|
||||
pass
|
||||
|
||||
|
||||
class UserList(StaffOnlyMixin, BabyBuddyFilterView):
|
||||
model = User
|
||||
template_name = 'babybuddy/user_list.html'
|
||||
ordering = 'username'
|
||||
template_name = "babybuddy/user_list.html"
|
||||
ordering = "username"
|
||||
paginate_by = 10
|
||||
filterset_fields = ('username', 'first_name', 'last_name', 'email')
|
||||
filterset_fields = ("username", "first_name", "last_name", "email")
|
||||
|
||||
|
||||
class UserAdd(StaffOnlyMixin, PermissionRequiredMixin, SuccessMessageMixin,
|
||||
CreateView):
|
||||
class UserAdd(StaffOnlyMixin, PermissionRequiredMixin, SuccessMessageMixin, CreateView):
|
||||
model = User
|
||||
template_name = 'babybuddy/user_form.html'
|
||||
permission_required = ('admin.add_user',)
|
||||
template_name = "babybuddy/user_form.html"
|
||||
permission_required = ("admin.add_user",)
|
||||
form_class = forms.UserAddForm
|
||||
success_url = reverse_lazy('babybuddy:user-list')
|
||||
success_message = gettext_lazy('User %(username)s added!')
|
||||
success_url = reverse_lazy("babybuddy:user-list")
|
||||
success_message = gettext_lazy("User %(username)s added!")
|
||||
|
||||
|
||||
class UserUpdate(StaffOnlyMixin, PermissionRequiredMixin,
|
||||
SuccessMessageMixin, UpdateView):
|
||||
class UserUpdate(
|
||||
StaffOnlyMixin, PermissionRequiredMixin, SuccessMessageMixin, UpdateView
|
||||
):
|
||||
model = User
|
||||
template_name = 'babybuddy/user_form.html'
|
||||
permission_required = ('admin.change_user',)
|
||||
template_name = "babybuddy/user_form.html"
|
||||
permission_required = ("admin.change_user",)
|
||||
form_class = forms.UserUpdateForm
|
||||
success_url = reverse_lazy('babybuddy:user-list')
|
||||
success_message = gettext_lazy('User %(username)s updated.')
|
||||
success_url = reverse_lazy("babybuddy:user-list")
|
||||
success_message = gettext_lazy("User %(username)s updated.")
|
||||
|
||||
|
||||
class UserDelete(StaffOnlyMixin, PermissionRequiredMixin,
|
||||
DeleteView, SuccessMessageMixin):
|
||||
class UserDelete(
|
||||
StaffOnlyMixin, PermissionRequiredMixin, DeleteView, SuccessMessageMixin
|
||||
):
|
||||
model = User
|
||||
template_name = 'babybuddy/user_confirm_delete.html'
|
||||
permission_required = ('admin.delete_user',)
|
||||
success_url = reverse_lazy('babybuddy:user-list')
|
||||
template_name = "babybuddy/user_confirm_delete.html"
|
||||
permission_required = ("admin.delete_user",)
|
||||
success_url = reverse_lazy("babybuddy:user-list")
|
||||
|
||||
def get_success_message(self, cleaned_data):
|
||||
return format_lazy(gettext_lazy(
|
||||
'User {user} deleted.'), user=self.get_object()
|
||||
)
|
||||
return format_lazy(gettext_lazy("User {user} deleted."), user=self.get_object())
|
||||
|
||||
|
||||
class UserPassword(LoginRequiredMixin, View):
|
||||
"""
|
||||
Handles user password changes.
|
||||
"""
|
||||
|
||||
form_class = forms.UserPasswordForm
|
||||
template_name = 'babybuddy/user_password_form.html'
|
||||
template_name = "babybuddy/user_password_form.html"
|
||||
|
||||
def get(self, request):
|
||||
return render(request, self.template_name, {
|
||||
'form': self.form_class(request.user)
|
||||
})
|
||||
return render(
|
||||
request, self.template_name, {"form": self.form_class(request.user)}
|
||||
)
|
||||
|
||||
def post(self, request):
|
||||
form = PasswordChangeForm(request.user, request.POST)
|
||||
if form.is_valid():
|
||||
user = form.save()
|
||||
update_session_auth_hash(request, user)
|
||||
messages.success(request, _('Password updated.'))
|
||||
return render(request, self.template_name, {'form': form})
|
||||
messages.success(request, _("Password updated."))
|
||||
return render(request, self.template_name, {"form": form})
|
||||
|
||||
|
||||
class UserSettings(LoginRequiredMixin, View):
|
||||
|
@ -127,42 +126,47 @@ class UserSettings(LoginRequiredMixin, View):
|
|||
Handles both the User and Settings models.
|
||||
Based on this SO answer: https://stackoverflow.com/a/45056835.
|
||||
"""
|
||||
|
||||
form_user_class = forms.UserForm
|
||||
form_settings_class = forms.UserSettingsForm
|
||||
template_name = 'babybuddy/user_settings_form.html'
|
||||
template_name = "babybuddy/user_settings_form.html"
|
||||
|
||||
def get(self, request):
|
||||
return render(request, self.template_name, {
|
||||
'form_user': self.form_user_class(instance=request.user),
|
||||
'form_settings': self.form_settings_class(
|
||||
instance=request.user.settings)
|
||||
})
|
||||
return render(
|
||||
request,
|
||||
self.template_name,
|
||||
{
|
||||
"form_user": self.form_user_class(instance=request.user),
|
||||
"form_settings": self.form_settings_class(
|
||||
instance=request.user.settings
|
||||
),
|
||||
},
|
||||
)
|
||||
|
||||
def post(self, request):
|
||||
if request.POST.get('api_key_regenerate'):
|
||||
if request.POST.get("api_key_regenerate"):
|
||||
request.user.settings.api_key(reset=True)
|
||||
messages.success(request, _('User API key regenerated.'))
|
||||
return redirect('babybuddy:user-settings')
|
||||
messages.success(request, _("User API key regenerated."))
|
||||
return redirect("babybuddy:user-settings")
|
||||
|
||||
form_user = self.form_user_class(
|
||||
instance=request.user,
|
||||
data=request.POST)
|
||||
form_user = self.form_user_class(instance=request.user, data=request.POST)
|
||||
form_settings = self.form_settings_class(
|
||||
instance=request.user.settings,
|
||||
data=request.POST)
|
||||
instance=request.user.settings, data=request.POST
|
||||
)
|
||||
if form_user.is_valid() and form_settings.is_valid():
|
||||
user = form_user.save(commit=False)
|
||||
user_settings = form_settings.save(commit=False)
|
||||
user.settings = user_settings
|
||||
user.save()
|
||||
translation.activate(user.settings.language)
|
||||
messages.success(request, _('Settings saved!'))
|
||||
messages.success(request, _("Settings saved!"))
|
||||
translation.deactivate()
|
||||
return set_language(request)
|
||||
return render(request, self.template_name, {
|
||||
'user_form': form_user,
|
||||
'settings_form': form_settings
|
||||
})
|
||||
return render(
|
||||
request,
|
||||
self.template_name,
|
||||
{"user_form": form_user, "settings_form": form_settings},
|
||||
)
|
||||
|
||||
|
||||
class Welcome(LoginRequiredMixin, TemplateView):
|
||||
|
@ -170,4 +174,5 @@ class Welcome(LoginRequiredMixin, TemplateView):
|
|||
Basic introduction to Baby Buddy (meant to be shown when no data is in the
|
||||
database).
|
||||
"""
|
||||
template_name = 'babybuddy/welcome.html'
|
||||
|
||||
template_name = "babybuddy/welcome.html"
|
||||
|
|
124
core/admin.py
124
core/admin.py
|
@ -9,31 +9,30 @@ from core import models
|
|||
|
||||
|
||||
class ImportExportResourceBase(resources.ModelResource):
|
||||
id = fields.Field(attribute='id')
|
||||
child = fields.Field(attribute='child_id', column_name='child_id')
|
||||
child_first_name = fields.Field(
|
||||
attribute='child__first_name', readonly=True)
|
||||
child_last_name = fields.Field(attribute='child__last_name', readonly=True)
|
||||
id = fields.Field(attribute="id")
|
||||
child = fields.Field(attribute="child_id", column_name="child_id")
|
||||
child_first_name = fields.Field(attribute="child__first_name", readonly=True)
|
||||
child_last_name = fields.Field(attribute="child__last_name", readonly=True)
|
||||
|
||||
class Meta:
|
||||
clean_model_instances = True
|
||||
exclude = ('duration',)
|
||||
exclude = ("duration",)
|
||||
|
||||
|
||||
class ChildImportExportResource(resources.ModelResource):
|
||||
class Meta:
|
||||
model = models.Child
|
||||
exclude = ('picture', 'slug')
|
||||
exclude = ("picture", "slug")
|
||||
|
||||
|
||||
@admin.register(models.Child)
|
||||
class ChildAdmin(ImportExportMixin, ExportActionMixin, admin.ModelAdmin):
|
||||
list_display = ('first_name', 'last_name', 'birth_date', 'slug')
|
||||
list_filter = ('last_name',)
|
||||
search_fields = ('first_name', 'last_name', 'birth_date')
|
||||
fields = ['first_name', 'last_name', 'birth_date']
|
||||
if settings.BABY_BUDDY['ALLOW_UPLOADS']:
|
||||
fields.append('picture')
|
||||
list_display = ("first_name", "last_name", "birth_date", "slug")
|
||||
list_filter = ("last_name",)
|
||||
search_fields = ("first_name", "last_name", "birth_date")
|
||||
fields = ["first_name", "last_name", "birth_date"]
|
||||
if settings.BABY_BUDDY["ALLOW_UPLOADS"]:
|
||||
fields.append("picture")
|
||||
resource_class = ChildImportExportResource
|
||||
|
||||
|
||||
|
@ -43,11 +42,13 @@ class DiaperChangeImportExportResource(ImportExportResourceBase):
|
|||
|
||||
|
||||
@admin.register(models.DiaperChange)
|
||||
class DiaperChangeAdmin(ImportExportMixin, ExportActionMixin,
|
||||
admin.ModelAdmin):
|
||||
list_display = ('child', 'time', 'wet', 'solid', 'color')
|
||||
list_filter = ('child', 'wet', 'solid', 'color')
|
||||
search_fields = ('child__first_name', 'child__last_name',)
|
||||
class DiaperChangeAdmin(ImportExportMixin, ExportActionMixin, admin.ModelAdmin):
|
||||
list_display = ("child", "time", "wet", "solid", "color")
|
||||
list_filter = ("child", "wet", "solid", "color")
|
||||
search_fields = (
|
||||
"child__first_name",
|
||||
"child__last_name",
|
||||
)
|
||||
resource_class = DiaperChangeImportExportResource
|
||||
|
||||
|
||||
|
@ -58,11 +59,18 @@ class FeedingImportExportResource(ImportExportResourceBase):
|
|||
|
||||
@admin.register(models.Feeding)
|
||||
class FeedingAdmin(ImportExportMixin, ExportActionMixin, admin.ModelAdmin):
|
||||
list_display = ('start', 'end', 'duration', 'child', 'type', 'method',
|
||||
'amount')
|
||||
list_filter = ('child', 'type', 'method',)
|
||||
search_fields = ('child__first_name', 'child__last_name', 'type',
|
||||
'method',)
|
||||
list_display = ("start", "end", "duration", "child", "type", "method", "amount")
|
||||
list_filter = (
|
||||
"child",
|
||||
"type",
|
||||
"method",
|
||||
)
|
||||
search_fields = (
|
||||
"child__first_name",
|
||||
"child__last_name",
|
||||
"type",
|
||||
"method",
|
||||
)
|
||||
resource_class = FeedingImportExportResource
|
||||
|
||||
|
||||
|
@ -73,9 +81,13 @@ class NoteImportExportResource(ImportExportResourceBase):
|
|||
|
||||
@admin.register(models.Note)
|
||||
class NoteAdmin(ImportExportMixin, ExportActionMixin, admin.ModelAdmin):
|
||||
list_display = ('time', 'child', 'note',)
|
||||
list_filter = ('child',)
|
||||
search_fields = ('child__last_name',)
|
||||
list_display = (
|
||||
"time",
|
||||
"child",
|
||||
"note",
|
||||
)
|
||||
list_filter = ("child",)
|
||||
search_fields = ("child__last_name",)
|
||||
resource_class = NoteImportExportResource
|
||||
|
||||
|
||||
|
@ -86,9 +98,12 @@ class SleepImportExportResource(ImportExportResourceBase):
|
|||
|
||||
@admin.register(models.Sleep)
|
||||
class SleepAdmin(ImportExportMixin, ExportActionMixin, admin.ModelAdmin):
|
||||
list_display = ('start', 'end', 'duration', 'child', 'nap')
|
||||
list_filter = ('child',)
|
||||
search_fields = ('child__first_name', 'child__last_name',)
|
||||
list_display = ("start", "end", "duration", "child", "nap")
|
||||
list_filter = ("child",)
|
||||
search_fields = (
|
||||
"child__first_name",
|
||||
"child__last_name",
|
||||
)
|
||||
resource_class = SleepImportExportResource
|
||||
|
||||
|
||||
|
@ -99,18 +114,25 @@ class TemperatureImportExportResource(ImportExportResourceBase):
|
|||
|
||||
@admin.register(models.Temperature)
|
||||
class TemperatureAdmin(ImportExportMixin, ExportActionMixin, admin.ModelAdmin):
|
||||
list_display = ('child', 'temperature', 'time',)
|
||||
list_filter = ('child',)
|
||||
search_fields = ('child__first_name', 'child__last_name', 'temperature',)
|
||||
list_display = (
|
||||
"child",
|
||||
"temperature",
|
||||
"time",
|
||||
)
|
||||
list_filter = ("child",)
|
||||
search_fields = (
|
||||
"child__first_name",
|
||||
"child__last_name",
|
||||
"temperature",
|
||||
)
|
||||
resource_class = TemperatureImportExportResource
|
||||
|
||||
|
||||
@admin.register(models.Timer)
|
||||
class TimerAdmin(admin.ModelAdmin):
|
||||
list_display = ('name', 'child', 'start', 'end', 'duration', 'active',
|
||||
'user')
|
||||
list_filter = ('child', 'active', 'user')
|
||||
search_fields = ('child__first_name', 'child__last_name', 'name', 'user')
|
||||
list_display = ("name", "child", "start", "end", "duration", "active", "user")
|
||||
list_filter = ("child", "active", "user")
|
||||
search_fields = ("child__first_name", "child__last_name", "name", "user")
|
||||
|
||||
|
||||
class TummyTimeImportExportResource(ImportExportResourceBase):
|
||||
|
@ -120,9 +142,19 @@ class TummyTimeImportExportResource(ImportExportResourceBase):
|
|||
|
||||
@admin.register(models.TummyTime)
|
||||
class TummyTimeAdmin(ImportExportMixin, ExportActionMixin, admin.ModelAdmin):
|
||||
list_display = ('start', 'end', 'duration', 'child', 'milestone',)
|
||||
list_filter = ('child',)
|
||||
search_fields = ('child__first_name', 'child__last_name', 'milestone',)
|
||||
list_display = (
|
||||
"start",
|
||||
"end",
|
||||
"duration",
|
||||
"child",
|
||||
"milestone",
|
||||
)
|
||||
list_filter = ("child",)
|
||||
search_fields = (
|
||||
"child__first_name",
|
||||
"child__last_name",
|
||||
"milestone",
|
||||
)
|
||||
resource_class = TummyTimeImportExportResource
|
||||
|
||||
|
||||
|
@ -133,7 +165,15 @@ class WeightImportExportResource(ImportExportResourceBase):
|
|||
|
||||
@admin.register(models.Weight)
|
||||
class WeightAdmin(ImportExportMixin, ExportActionMixin, admin.ModelAdmin):
|
||||
list_display = ('child', 'weight', 'date',)
|
||||
list_filter = ('child',)
|
||||
search_fields = ('child__first_name', 'child__last_name', 'weight',)
|
||||
list_display = (
|
||||
"child",
|
||||
"weight",
|
||||
"date",
|
||||
)
|
||||
list_filter = ("child",)
|
||||
search_fields = (
|
||||
"child__first_name",
|
||||
"child__last_name",
|
||||
"weight",
|
||||
)
|
||||
resource_class = WeightImportExportResource
|
||||
|
|
252
core/forms.py
252
core/forms.py
|
@ -17,45 +17,48 @@ def set_initial_values(kwargs, form_type):
|
|||
"""
|
||||
|
||||
# Never update initial values for existing instance (e.g. edit operation).
|
||||
if kwargs.get('instance', None):
|
||||
if kwargs.get("instance", None):
|
||||
return kwargs
|
||||
|
||||
# Add the "initial" kwarg if it does not already exist.
|
||||
if not kwargs.get('initial'):
|
||||
if not kwargs.get("initial"):
|
||||
kwargs.update(initial={})
|
||||
|
||||
# Set Child based on `child` kwarg or single Chile database.
|
||||
child_slug = kwargs.get('child', None)
|
||||
child_slug = kwargs.get("child", None)
|
||||
if child_slug:
|
||||
kwargs['initial'].update({
|
||||
'child': models.Child.objects.filter(slug=child_slug).first(),
|
||||
})
|
||||
kwargs["initial"].update(
|
||||
{
|
||||
"child": models.Child.objects.filter(slug=child_slug).first(),
|
||||
}
|
||||
)
|
||||
elif models.Child.count() == 1:
|
||||
kwargs['initial'].update({'child': models.Child.objects.first()})
|
||||
kwargs["initial"].update({"child": models.Child.objects.first()})
|
||||
|
||||
# Set start and end time based on Timer from `timer` kwarg.
|
||||
timer_id = kwargs.get('timer', None)
|
||||
timer_id = kwargs.get("timer", None)
|
||||
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()
|
||||
})
|
||||
kwargs["initial"].update(
|
||||
{"timer": timer, "start": timer.start, "end": timer.end or timezone.now()}
|
||||
)
|
||||
|
||||
# Set type and method values for Feeding instance based on last feed.
|
||||
if form_type == FeedingForm and 'child' in kwargs['initial']:
|
||||
last_feeding = models.Feeding.objects.filter(
|
||||
child=kwargs['initial']['child']).order_by('end').last()
|
||||
if form_type == FeedingForm and "child" in kwargs["initial"]:
|
||||
last_feeding = (
|
||||
models.Feeding.objects.filter(child=kwargs["initial"]["child"])
|
||||
.order_by("end")
|
||||
.last()
|
||||
)
|
||||
if last_feeding:
|
||||
last_method = last_feeding.method
|
||||
last_feed_args = {'type': last_feeding.type}
|
||||
if last_method not in ['left breast', 'right breast']:
|
||||
last_feed_args['method'] = last_method
|
||||
kwargs['initial'].update(last_feed_args)
|
||||
last_feed_args = {"type": last_feeding.type}
|
||||
if last_method not in ["left breast", "right breast"]:
|
||||
last_feed_args["method"] = last_method
|
||||
kwargs["initial"].update(last_feed_args)
|
||||
|
||||
# Remove custom kwargs so they do not interfere with `super` calls.
|
||||
for key in ['child', 'timer']:
|
||||
for key in ["child", "timer"]:
|
||||
try:
|
||||
kwargs.pop(key)
|
||||
except KeyError:
|
||||
|
@ -67,7 +70,7 @@ def set_initial_values(kwargs, form_type):
|
|||
class CoreModelForm(forms.ModelForm):
|
||||
def __init__(self, *args, **kwargs):
|
||||
# Set `timer_id` so the Timer can be stopped in the `save` method.
|
||||
self.timer_id = kwargs.get('timer', None)
|
||||
self.timer_id = kwargs.get("timer", None)
|
||||
kwargs = set_initial_values(kwargs, type(self))
|
||||
super(CoreModelForm, self).__init__(*args, **kwargs)
|
||||
|
||||
|
@ -85,18 +88,16 @@ class CoreModelForm(forms.ModelForm):
|
|||
class ChildForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = models.Child
|
||||
fields = [
|
||||
'first_name',
|
||||
'last_name',
|
||||
'birth_date'
|
||||
]
|
||||
if settings.BABY_BUDDY['ALLOW_UPLOADS']:
|
||||
fields.append('picture')
|
||||
fields = ["first_name", "last_name", "birth_date"]
|
||||
if settings.BABY_BUDDY["ALLOW_UPLOADS"]:
|
||||
fields.append("picture")
|
||||
widgets = {
|
||||
'birth_date': forms.DateInput(attrs={
|
||||
'autocomplete': 'off',
|
||||
'data-target': '#datetimepicker_date',
|
||||
}),
|
||||
"birth_date": forms.DateInput(
|
||||
attrs={
|
||||
"autocomplete": "off",
|
||||
"data-target": "#datetimepicker_date",
|
||||
}
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
|
@ -108,10 +109,11 @@ class ChildDeleteForm(forms.ModelForm):
|
|||
fields = []
|
||||
|
||||
def clean_confirm_name(self):
|
||||
confirm_name = self.cleaned_data['confirm_name']
|
||||
confirm_name = self.cleaned_data["confirm_name"]
|
||||
if confirm_name != str(self.instance):
|
||||
raise forms.ValidationError(
|
||||
_('Name does not match child name.'), code='confirm_mismatch')
|
||||
_("Name does not match child name."), code="confirm_mismatch"
|
||||
)
|
||||
return confirm_name
|
||||
|
||||
def save(self, commit=True):
|
||||
|
@ -123,88 +125,104 @@ class ChildDeleteForm(forms.ModelForm):
|
|||
class DiaperChangeForm(CoreModelForm):
|
||||
class Meta:
|
||||
model = models.DiaperChange
|
||||
fields = ['child', 'time', 'wet', 'solid', 'color', 'amount', 'notes']
|
||||
fields = ["child", "time", "wet", "solid", "color", "amount", "notes"]
|
||||
widgets = {
|
||||
'time': forms.DateTimeInput(attrs={
|
||||
'autocomplete': 'off',
|
||||
'data-target': '#datetimepicker_time',
|
||||
}),
|
||||
'notes': forms.Textarea(attrs={'rows': 5}),
|
||||
"time": forms.DateTimeInput(
|
||||
attrs={
|
||||
"autocomplete": "off",
|
||||
"data-target": "#datetimepicker_time",
|
||||
}
|
||||
),
|
||||
"notes": forms.Textarea(attrs={"rows": 5}),
|
||||
}
|
||||
|
||||
|
||||
class FeedingForm(CoreModelForm):
|
||||
class Meta:
|
||||
model = models.Feeding
|
||||
fields = ['child', 'start', 'end', 'type', 'method', 'amount', 'notes']
|
||||
fields = ["child", "start", "end", "type", "method", "amount", "notes"]
|
||||
widgets = {
|
||||
'start': forms.DateTimeInput(attrs={
|
||||
'autocomplete': 'off',
|
||||
'data-target': '#datetimepicker_start',
|
||||
}),
|
||||
'end': forms.DateTimeInput(attrs={
|
||||
'autocomplete': 'off',
|
||||
'data-target': '#datetimepicker_end',
|
||||
}),
|
||||
'notes': forms.Textarea(attrs={'rows': 5}),
|
||||
"start": forms.DateTimeInput(
|
||||
attrs={
|
||||
"autocomplete": "off",
|
||||
"data-target": "#datetimepicker_start",
|
||||
}
|
||||
),
|
||||
"end": forms.DateTimeInput(
|
||||
attrs={
|
||||
"autocomplete": "off",
|
||||
"data-target": "#datetimepicker_end",
|
||||
}
|
||||
),
|
||||
"notes": forms.Textarea(attrs={"rows": 5}),
|
||||
}
|
||||
|
||||
|
||||
class NoteForm(CoreModelForm):
|
||||
class Meta:
|
||||
model = models.Note
|
||||
fields = ['child', 'note', 'time']
|
||||
fields = ["child", "note", "time"]
|
||||
widgets = {
|
||||
'time': forms.DateTimeInput(attrs={
|
||||
'autocomplete': 'off',
|
||||
'data-target': '#datetimepicker_time',
|
||||
}),
|
||||
"time": forms.DateTimeInput(
|
||||
attrs={
|
||||
"autocomplete": "off",
|
||||
"data-target": "#datetimepicker_time",
|
||||
}
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
class SleepForm(CoreModelForm):
|
||||
class Meta:
|
||||
model = models.Sleep
|
||||
fields = ['child', 'start', 'end', 'notes']
|
||||
fields = ["child", "start", "end", "notes"]
|
||||
widgets = {
|
||||
'start': forms.DateTimeInput(attrs={
|
||||
'autocomplete': 'off',
|
||||
'data-target': '#datetimepicker_start',
|
||||
}),
|
||||
'end': forms.DateTimeInput(attrs={
|
||||
'autocomplete': 'off',
|
||||
'data-target': '#datetimepicker_end',
|
||||
}),
|
||||
'notes': forms.Textarea(attrs={'rows': 5}),
|
||||
"start": forms.DateTimeInput(
|
||||
attrs={
|
||||
"autocomplete": "off",
|
||||
"data-target": "#datetimepicker_start",
|
||||
}
|
||||
),
|
||||
"end": forms.DateTimeInput(
|
||||
attrs={
|
||||
"autocomplete": "off",
|
||||
"data-target": "#datetimepicker_end",
|
||||
}
|
||||
),
|
||||
"notes": forms.Textarea(attrs={"rows": 5}),
|
||||
}
|
||||
|
||||
|
||||
class TemperatureForm(CoreModelForm):
|
||||
class Meta:
|
||||
model = models.Temperature
|
||||
fields = ['child', 'temperature', 'time', 'notes']
|
||||
fields = ["child", "temperature", "time", "notes"]
|
||||
widgets = {
|
||||
'time': forms.DateTimeInput(attrs={
|
||||
'autocomplete': 'off',
|
||||
'data-target': '#datetimepicker_time',
|
||||
}),
|
||||
'notes': forms.Textarea(attrs={'rows': 5}),
|
||||
"time": forms.DateTimeInput(
|
||||
attrs={
|
||||
"autocomplete": "off",
|
||||
"data-target": "#datetimepicker_time",
|
||||
}
|
||||
),
|
||||
"notes": forms.Textarea(attrs={"rows": 5}),
|
||||
}
|
||||
|
||||
|
||||
class TimerForm(CoreModelForm):
|
||||
class Meta:
|
||||
model = models.Timer
|
||||
fields = ['child', 'name', 'start']
|
||||
fields = ["child", "name", "start"]
|
||||
widgets = {
|
||||
'start': forms.DateTimeInput(attrs={
|
||||
'autocomplete': 'off',
|
||||
'data-target': '#datetimepicker_start',
|
||||
})
|
||||
"start": forms.DateTimeInput(
|
||||
attrs={
|
||||
"autocomplete": "off",
|
||||
"data-target": "#datetimepicker_start",
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.user = kwargs.pop('user')
|
||||
self.user = kwargs.pop("user")
|
||||
super(TimerForm, self).__init__(*args, **kwargs)
|
||||
|
||||
def save(self, commit=True):
|
||||
|
@ -217,66 +235,78 @@ class TimerForm(CoreModelForm):
|
|||
class TummyTimeForm(CoreModelForm):
|
||||
class Meta:
|
||||
model = models.TummyTime
|
||||
fields = ['child', 'start', 'end', 'milestone']
|
||||
fields = ["child", "start", "end", "milestone"]
|
||||
widgets = {
|
||||
'start': forms.DateTimeInput(attrs={
|
||||
'autocomplete': 'off',
|
||||
'data-target': '#datetimepicker_start',
|
||||
}),
|
||||
'end': forms.DateTimeInput(attrs={
|
||||
'autocomplete': 'off',
|
||||
'data-target': '#datetimepicker_end',
|
||||
}),
|
||||
"start": forms.DateTimeInput(
|
||||
attrs={
|
||||
"autocomplete": "off",
|
||||
"data-target": "#datetimepicker_start",
|
||||
}
|
||||
),
|
||||
"end": forms.DateTimeInput(
|
||||
attrs={
|
||||
"autocomplete": "off",
|
||||
"data-target": "#datetimepicker_end",
|
||||
}
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
class WeightForm(CoreModelForm):
|
||||
class Meta:
|
||||
model = models.Weight
|
||||
fields = ['child', 'weight', 'date', 'notes']
|
||||
fields = ["child", "weight", "date", "notes"]
|
||||
widgets = {
|
||||
'date': forms.DateInput(attrs={
|
||||
'autocomplete': 'off',
|
||||
'data-target': '#datetimepicker_date',
|
||||
}),
|
||||
'notes': forms.Textarea(attrs={'rows': 5}),
|
||||
"date": forms.DateInput(
|
||||
attrs={
|
||||
"autocomplete": "off",
|
||||
"data-target": "#datetimepicker_date",
|
||||
}
|
||||
),
|
||||
"notes": forms.Textarea(attrs={"rows": 5}),
|
||||
}
|
||||
|
||||
|
||||
class HeightForm(CoreModelForm):
|
||||
class Meta:
|
||||
model = models.Height
|
||||
fields = ['child', 'height', 'date', 'notes']
|
||||
fields = ["child", "height", "date", "notes"]
|
||||
widgets = {
|
||||
'date': forms.DateInput(attrs={
|
||||
'autocomplete': 'off',
|
||||
'data-target': '#datetimepicker_date',
|
||||
}),
|
||||
'notes': forms.Textarea(attrs={'rows': 5}),
|
||||
"date": forms.DateInput(
|
||||
attrs={
|
||||
"autocomplete": "off",
|
||||
"data-target": "#datetimepicker_date",
|
||||
}
|
||||
),
|
||||
"notes": forms.Textarea(attrs={"rows": 5}),
|
||||
}
|
||||
|
||||
|
||||
class HeadCircumferenceForm(CoreModelForm):
|
||||
class Meta:
|
||||
model = models.HeadCircumference
|
||||
fields = ['child', 'head_circumference', 'date', 'notes']
|
||||
fields = ["child", "head_circumference", "date", "notes"]
|
||||
widgets = {
|
||||
'date': forms.DateInput(attrs={
|
||||
'autocomplete': 'off',
|
||||
'data-target': '#datetimepicker_date',
|
||||
}),
|
||||
'notes': forms.Textarea(attrs={'rows': 5}),
|
||||
"date": forms.DateInput(
|
||||
attrs={
|
||||
"autocomplete": "off",
|
||||
"data-target": "#datetimepicker_date",
|
||||
}
|
||||
),
|
||||
"notes": forms.Textarea(attrs={"rows": 5}),
|
||||
}
|
||||
|
||||
|
||||
class BMIForm(CoreModelForm):
|
||||
class Meta:
|
||||
model = models.BMI
|
||||
fields = ['child', 'bmi', 'date', 'notes']
|
||||
fields = ["child", "bmi", "date", "notes"]
|
||||
widgets = {
|
||||
'date': forms.DateInput(attrs={
|
||||
'autocomplete': 'off',
|
||||
'data-target': '#datetimepicker_date',
|
||||
}),
|
||||
'notes': forms.Textarea(attrs={'rows': 5}),
|
||||
"date": forms.DateInput(
|
||||
attrs={
|
||||
"autocomplete": "off",
|
||||
"data-target": "#datetimepicker_date",
|
||||
}
|
||||
),
|
||||
"notes": forms.Textarea(attrs={"rows": 5}),
|
||||
}
|
||||
|
|
|
@ -15,109 +15,238 @@ class Migration(migrations.Migration):
|
|||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Child',
|
||||
name="Child",
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('first_name', models.CharField(max_length=255)),
|
||||
('last_name', models.CharField(max_length=255)),
|
||||
('birth_date', models.DateField()),
|
||||
('slug', models.SlugField(editable=False, max_length=100, unique=True)),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("first_name", models.CharField(max_length=255)),
|
||||
("last_name", models.CharField(max_length=255)),
|
||||
("birth_date", models.DateField()),
|
||||
("slug", models.SlugField(editable=False, max_length=100, unique=True)),
|
||||
],
|
||||
options={
|
||||
'ordering': ['last_name', 'first_name'],
|
||||
'default_permissions': ('view', 'add', 'change', 'delete'),
|
||||
'verbose_name_plural': 'Children',
|
||||
"ordering": ["last_name", "first_name"],
|
||||
"default_permissions": ("view", "add", "change", "delete"),
|
||||
"verbose_name_plural": "Children",
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='DiaperChange',
|
||||
name="DiaperChange",
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('time', models.DateTimeField()),
|
||||
('wet', models.BooleanField()),
|
||||
('solid', models.BooleanField()),
|
||||
('color', models.CharField(blank=True, choices=[('black', 'Black'), ('brown', 'Brown'), ('green', 'Green'), ('yellow', 'Yellow')], max_length=255)),
|
||||
('child', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='diaper_change', to='core.Child')),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("time", models.DateTimeField()),
|
||||
("wet", models.BooleanField()),
|
||||
("solid", models.BooleanField()),
|
||||
(
|
||||
"color",
|
||||
models.CharField(
|
||||
blank=True,
|
||||
choices=[
|
||||
("black", "Black"),
|
||||
("brown", "Brown"),
|
||||
("green", "Green"),
|
||||
("yellow", "Yellow"),
|
||||
],
|
||||
max_length=255,
|
||||
),
|
||||
),
|
||||
(
|
||||
"child",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="diaper_change",
|
||||
to="core.Child",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'ordering': ['-time'],
|
||||
'default_permissions': ('view', 'add', 'change', 'delete'),
|
||||
"ordering": ["-time"],
|
||||
"default_permissions": ("view", "add", "change", "delete"),
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Feeding',
|
||||
name="Feeding",
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('start', models.DateTimeField()),
|
||||
('end', models.DateTimeField()),
|
||||
('duration', models.DurationField(editable=False, null=True)),
|
||||
('type', models.CharField(choices=[('breast milk', 'Breast milk'), ('formula', 'Formula')], max_length=255)),
|
||||
('method', models.CharField(choices=[('bottle', 'Bottle'), ('left breast', 'Left breast'), ('right breast', 'Right breast')], max_length=255)),
|
||||
('amount', models.FloatField(blank=True, null=True)),
|
||||
('child', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='feeding', to='core.Child')),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("start", models.DateTimeField()),
|
||||
("end", models.DateTimeField()),
|
||||
("duration", models.DurationField(editable=False, null=True)),
|
||||
(
|
||||
"type",
|
||||
models.CharField(
|
||||
choices=[
|
||||
("breast milk", "Breast milk"),
|
||||
("formula", "Formula"),
|
||||
],
|
||||
max_length=255,
|
||||
),
|
||||
),
|
||||
(
|
||||
"method",
|
||||
models.CharField(
|
||||
choices=[
|
||||
("bottle", "Bottle"),
|
||||
("left breast", "Left breast"),
|
||||
("right breast", "Right breast"),
|
||||
],
|
||||
max_length=255,
|
||||
),
|
||||
),
|
||||
("amount", models.FloatField(blank=True, null=True)),
|
||||
(
|
||||
"child",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="feeding",
|
||||
to="core.Child",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'ordering': ['-start'],
|
||||
'default_permissions': ('view', 'add', 'change', 'delete'),
|
||||
"ordering": ["-start"],
|
||||
"default_permissions": ("view", "add", "change", "delete"),
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Note',
|
||||
name="Note",
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('note', models.TextField()),
|
||||
('time', models.DateTimeField(auto_now=True)),
|
||||
('child', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='note', to='core.Child')),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("note", models.TextField()),
|
||||
("time", models.DateTimeField(auto_now=True)),
|
||||
(
|
||||
"child",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="note",
|
||||
to="core.Child",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'ordering': ['-time'],
|
||||
'default_permissions': ('view', 'add', 'change', 'delete'),
|
||||
"ordering": ["-time"],
|
||||
"default_permissions": ("view", "add", "change", "delete"),
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Sleep',
|
||||
name="Sleep",
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('start', models.DateTimeField()),
|
||||
('end', models.DateTimeField()),
|
||||
('duration', models.DurationField(editable=False, null=True)),
|
||||
('child', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='sleep', to='core.Child')),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("start", models.DateTimeField()),
|
||||
("end", models.DateTimeField()),
|
||||
("duration", models.DurationField(editable=False, null=True)),
|
||||
(
|
||||
"child",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="sleep",
|
||||
to="core.Child",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'ordering': ['-start'],
|
||||
'default_permissions': ('view', 'add', 'change', 'delete'),
|
||||
'verbose_name_plural': 'Sleep',
|
||||
"ordering": ["-start"],
|
||||
"default_permissions": ("view", "add", "change", "delete"),
|
||||
"verbose_name_plural": "Sleep",
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Timer',
|
||||
name="Timer",
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(blank=True, max_length=255, null=True)),
|
||||
('start', models.DateTimeField(auto_now_add=True)),
|
||||
('end', models.DateTimeField(blank=True, editable=False, null=True)),
|
||||
('duration', models.DurationField(editable=False, null=True)),
|
||||
('active', models.BooleanField(default=True, editable=False)),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='timers', to=settings.AUTH_USER_MODEL)),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("name", models.CharField(blank=True, max_length=255, null=True)),
|
||||
("start", models.DateTimeField(auto_now_add=True)),
|
||||
("end", models.DateTimeField(blank=True, editable=False, null=True)),
|
||||
("duration", models.DurationField(editable=False, null=True)),
|
||||
("active", models.BooleanField(default=True, editable=False)),
|
||||
(
|
||||
"user",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="timers",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'ordering': ['-active', '-start', '-end'],
|
||||
'default_permissions': ('view', 'add', 'change', 'delete'),
|
||||
"ordering": ["-active", "-start", "-end"],
|
||||
"default_permissions": ("view", "add", "change", "delete"),
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='TummyTime',
|
||||
name="TummyTime",
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('start', models.DateTimeField()),
|
||||
('end', models.DateTimeField()),
|
||||
('duration', models.DurationField(editable=False, null=True)),
|
||||
('milestone', models.CharField(blank=True, max_length=255)),
|
||||
('child', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tummy_time', to='core.Child')),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("start", models.DateTimeField()),
|
||||
("end", models.DateTimeField()),
|
||||
("duration", models.DurationField(editable=False, null=True)),
|
||||
("milestone", models.CharField(blank=True, max_length=255)),
|
||||
(
|
||||
"child",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="tummy_time",
|
||||
to="core.Child",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'ordering': ['-start'],
|
||||
'default_permissions': ('view', 'add', 'change', 'delete'),
|
||||
"ordering": ["-start"],
|
||||
"default_permissions": ("view", "add", "change", "delete"),
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
|
@ -7,13 +7,15 @@ import django.utils.timezone
|
|||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0001_initial'),
|
||||
("core", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='timer',
|
||||
name='start',
|
||||
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='Start Time'),
|
||||
model_name="timer",
|
||||
name="start",
|
||||
field=models.DateTimeField(
|
||||
default=django.utils.timezone.now, verbose_name="Start Time"
|
||||
),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -7,22 +7,37 @@ import django.db.models.deletion
|
|||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0002_auto_20171028_1257'),
|
||||
("core", "0002_auto_20171028_1257"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Weight',
|
||||
name="Weight",
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('weight', models.FloatField()),
|
||||
('date', models.DateField()),
|
||||
('child', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='weight', to='core.Child')),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("weight", models.FloatField()),
|
||||
("date", models.DateField()),
|
||||
(
|
||||
"child",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="weight",
|
||||
to="core.Child",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'default_permissions': ('view', 'add', 'change', 'delete'),
|
||||
'verbose_name_plural': 'Weight',
|
||||
'ordering': ['-date'],
|
||||
"default_permissions": ("view", "add", "change", "delete"),
|
||||
"verbose_name_plural": "Weight",
|
||||
"ordering": ["-date"],
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
|
@ -6,13 +6,13 @@ from django.db import migrations, models
|
|||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0003_weight'),
|
||||
("core", "0003_weight"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='child',
|
||||
name='picture',
|
||||
field=models.ImageField(blank=True, null=True, upload_to='child/picture/'),
|
||||
model_name="child",
|
||||
name="picture",
|
||||
field=models.ImageField(blank=True, null=True, upload_to="child/picture/"),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -9,230 +9,352 @@ import django.utils.timezone
|
|||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0004_child_picture'),
|
||||
("core", "0004_child_picture"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='child',
|
||||
options={'default_permissions': ('view', 'add', 'change', 'delete'), 'ordering': ['last_name', 'first_name'], 'verbose_name': 'Child', 'verbose_name_plural': 'Children'},
|
||||
name="child",
|
||||
options={
|
||||
"default_permissions": ("view", "add", "change", "delete"),
|
||||
"ordering": ["last_name", "first_name"],
|
||||
"verbose_name": "Child",
|
||||
"verbose_name_plural": "Children",
|
||||
},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='diaperchange',
|
||||
options={'default_permissions': ('view', 'add', 'change', 'delete'), 'ordering': ['-time'], 'verbose_name': 'Diaper Change', 'verbose_name_plural': 'Diaper Changes'},
|
||||
name="diaperchange",
|
||||
options={
|
||||
"default_permissions": ("view", "add", "change", "delete"),
|
||||
"ordering": ["-time"],
|
||||
"verbose_name": "Diaper Change",
|
||||
"verbose_name_plural": "Diaper Changes",
|
||||
},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='feeding',
|
||||
options={'default_permissions': ('view', 'add', 'change', 'delete'), 'ordering': ['-start'], 'verbose_name': 'Feeding', 'verbose_name_plural': 'Feedings'},
|
||||
name="feeding",
|
||||
options={
|
||||
"default_permissions": ("view", "add", "change", "delete"),
|
||||
"ordering": ["-start"],
|
||||
"verbose_name": "Feeding",
|
||||
"verbose_name_plural": "Feedings",
|
||||
},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='note',
|
||||
options={'default_permissions': ('view', 'add', 'change', 'delete'), 'ordering': ['-time'], 'verbose_name': 'Note', 'verbose_name_plural': 'Notes'},
|
||||
name="note",
|
||||
options={
|
||||
"default_permissions": ("view", "add", "change", "delete"),
|
||||
"ordering": ["-time"],
|
||||
"verbose_name": "Note",
|
||||
"verbose_name_plural": "Notes",
|
||||
},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='sleep',
|
||||
options={'default_permissions': ('view', 'add', 'change', 'delete'), 'ordering': ['-start'], 'verbose_name': 'Sleep', 'verbose_name_plural': 'Sleep'},
|
||||
name="sleep",
|
||||
options={
|
||||
"default_permissions": ("view", "add", "change", "delete"),
|
||||
"ordering": ["-start"],
|
||||
"verbose_name": "Sleep",
|
||||
"verbose_name_plural": "Sleep",
|
||||
},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='timer',
|
||||
options={'default_permissions': ('view', 'add', 'change', 'delete'), 'ordering': ['-active', '-start', '-end'], 'verbose_name': 'Timer', 'verbose_name_plural': 'Timers'},
|
||||
name="timer",
|
||||
options={
|
||||
"default_permissions": ("view", "add", "change", "delete"),
|
||||
"ordering": ["-active", "-start", "-end"],
|
||||
"verbose_name": "Timer",
|
||||
"verbose_name_plural": "Timers",
|
||||
},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='tummytime',
|
||||
options={'default_permissions': ('view', 'add', 'change', 'delete'), 'ordering': ['-start'], 'verbose_name': 'Tummy Time', 'verbose_name_plural': 'Tummy Time'},
|
||||
name="tummytime",
|
||||
options={
|
||||
"default_permissions": ("view", "add", "change", "delete"),
|
||||
"ordering": ["-start"],
|
||||
"verbose_name": "Tummy Time",
|
||||
"verbose_name_plural": "Tummy Time",
|
||||
},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='weight',
|
||||
options={'default_permissions': ('view', 'add', 'change', 'delete'), 'ordering': ['-date'], 'verbose_name': 'Weight', 'verbose_name_plural': 'Weight'},
|
||||
name="weight",
|
||||
options={
|
||||
"default_permissions": ("view", "add", "change", "delete"),
|
||||
"ordering": ["-date"],
|
||||
"verbose_name": "Weight",
|
||||
"verbose_name_plural": "Weight",
|
||||
},
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='child',
|
||||
name='birth_date',
|
||||
field=models.DateField(verbose_name='Birth date'),
|
||||
model_name="child",
|
||||
name="birth_date",
|
||||
field=models.DateField(verbose_name="Birth date"),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='child',
|
||||
name='first_name',
|
||||
field=models.CharField(max_length=255, verbose_name='First name'),
|
||||
model_name="child",
|
||||
name="first_name",
|
||||
field=models.CharField(max_length=255, verbose_name="First name"),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='child',
|
||||
name='last_name',
|
||||
field=models.CharField(max_length=255, verbose_name='Last name'),
|
||||
model_name="child",
|
||||
name="last_name",
|
||||
field=models.CharField(max_length=255, verbose_name="Last name"),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='child',
|
||||
name='picture',
|
||||
field=models.ImageField(blank=True, null=True, upload_to='child/picture/', verbose_name='Picture'),
|
||||
model_name="child",
|
||||
name="picture",
|
||||
field=models.ImageField(
|
||||
blank=True,
|
||||
null=True,
|
||||
upload_to="child/picture/",
|
||||
verbose_name="Picture",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='child',
|
||||
name='slug',
|
||||
field=models.SlugField(editable=False, max_length=100, unique=True, verbose_name='Slug'),
|
||||
model_name="child",
|
||||
name="slug",
|
||||
field=models.SlugField(
|
||||
editable=False, max_length=100, unique=True, verbose_name="Slug"
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='diaperchange',
|
||||
name='child',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='diaper_change', to='core.Child', verbose_name='Child'),
|
||||
model_name="diaperchange",
|
||||
name="child",
|
||||
field=models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="diaper_change",
|
||||
to="core.Child",
|
||||
verbose_name="Child",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='diaperchange',
|
||||
name='color',
|
||||
field=models.CharField(blank=True, choices=[('black', 'Black'), ('brown', 'Brown'), ('green', 'Green'), ('yellow', 'Yellow')], max_length=255, verbose_name='Color'),
|
||||
model_name="diaperchange",
|
||||
name="color",
|
||||
field=models.CharField(
|
||||
blank=True,
|
||||
choices=[
|
||||
("black", "Black"),
|
||||
("brown", "Brown"),
|
||||
("green", "Green"),
|
||||
("yellow", "Yellow"),
|
||||
],
|
||||
max_length=255,
|
||||
verbose_name="Color",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='diaperchange',
|
||||
name='solid',
|
||||
field=models.BooleanField(verbose_name='Solid'),
|
||||
model_name="diaperchange",
|
||||
name="solid",
|
||||
field=models.BooleanField(verbose_name="Solid"),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='diaperchange',
|
||||
name='time',
|
||||
field=models.DateTimeField(verbose_name='Time'),
|
||||
model_name="diaperchange",
|
||||
name="time",
|
||||
field=models.DateTimeField(verbose_name="Time"),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='diaperchange',
|
||||
name='wet',
|
||||
field=models.BooleanField(verbose_name='Wet'),
|
||||
model_name="diaperchange",
|
||||
name="wet",
|
||||
field=models.BooleanField(verbose_name="Wet"),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='feeding',
|
||||
name='amount',
|
||||
field=models.FloatField(blank=True, null=True, verbose_name='Amount'),
|
||||
model_name="feeding",
|
||||
name="amount",
|
||||
field=models.FloatField(blank=True, null=True, verbose_name="Amount"),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='feeding',
|
||||
name='child',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='feeding', to='core.Child', verbose_name='Child'),
|
||||
model_name="feeding",
|
||||
name="child",
|
||||
field=models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="feeding",
|
||||
to="core.Child",
|
||||
verbose_name="Child",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='feeding',
|
||||
name='duration',
|
||||
field=models.DurationField(editable=False, null=True, verbose_name='Duration'),
|
||||
model_name="feeding",
|
||||
name="duration",
|
||||
field=models.DurationField(
|
||||
editable=False, null=True, verbose_name="Duration"
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='feeding',
|
||||
name='end',
|
||||
field=models.DateTimeField(verbose_name='End time'),
|
||||
model_name="feeding",
|
||||
name="end",
|
||||
field=models.DateTimeField(verbose_name="End time"),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='feeding',
|
||||
name='method',
|
||||
field=models.CharField(choices=[('bottle', 'Bottle'), ('left breast', 'Left breast'), ('right breast', 'Right breast')], max_length=255, verbose_name='Method'),
|
||||
model_name="feeding",
|
||||
name="method",
|
||||
field=models.CharField(
|
||||
choices=[
|
||||
("bottle", "Bottle"),
|
||||
("left breast", "Left breast"),
|
||||
("right breast", "Right breast"),
|
||||
],
|
||||
max_length=255,
|
||||
verbose_name="Method",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='feeding',
|
||||
name='start',
|
||||
field=models.DateTimeField(verbose_name='Start time'),
|
||||
model_name="feeding",
|
||||
name="start",
|
||||
field=models.DateTimeField(verbose_name="Start time"),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='feeding',
|
||||
name='type',
|
||||
field=models.CharField(choices=[('breast milk', 'Breast milk'), ('formula', 'Formula')], max_length=255, verbose_name='Type'),
|
||||
model_name="feeding",
|
||||
name="type",
|
||||
field=models.CharField(
|
||||
choices=[("breast milk", "Breast milk"), ("formula", "Formula")],
|
||||
max_length=255,
|
||||
verbose_name="Type",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='note',
|
||||
name='child',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='note', to='core.Child', verbose_name='Child'),
|
||||
model_name="note",
|
||||
name="child",
|
||||
field=models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="note",
|
||||
to="core.Child",
|
||||
verbose_name="Child",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='note',
|
||||
name='note',
|
||||
field=models.TextField(verbose_name='Note'),
|
||||
model_name="note",
|
||||
name="note",
|
||||
field=models.TextField(verbose_name="Note"),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='note',
|
||||
name='time',
|
||||
field=models.DateTimeField(auto_now=True, verbose_name='Time'),
|
||||
model_name="note",
|
||||
name="time",
|
||||
field=models.DateTimeField(auto_now=True, verbose_name="Time"),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='sleep',
|
||||
name='child',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='sleep', to='core.Child', verbose_name='Child'),
|
||||
model_name="sleep",
|
||||
name="child",
|
||||
field=models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="sleep",
|
||||
to="core.Child",
|
||||
verbose_name="Child",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='sleep',
|
||||
name='duration',
|
||||
field=models.DurationField(editable=False, null=True, verbose_name='Duration'),
|
||||
model_name="sleep",
|
||||
name="duration",
|
||||
field=models.DurationField(
|
||||
editable=False, null=True, verbose_name="Duration"
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='sleep',
|
||||
name='end',
|
||||
field=models.DateTimeField(verbose_name='End time'),
|
||||
model_name="sleep",
|
||||
name="end",
|
||||
field=models.DateTimeField(verbose_name="End time"),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='sleep',
|
||||
name='start',
|
||||
field=models.DateTimeField(verbose_name='Start time'),
|
||||
model_name="sleep",
|
||||
name="start",
|
||||
field=models.DateTimeField(verbose_name="Start time"),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='timer',
|
||||
name='active',
|
||||
field=models.BooleanField(default=True, editable=False, verbose_name='Active'),
|
||||
model_name="timer",
|
||||
name="active",
|
||||
field=models.BooleanField(
|
||||
default=True, editable=False, verbose_name="Active"
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='timer',
|
||||
name='duration',
|
||||
field=models.DurationField(editable=False, null=True, verbose_name='Duration'),
|
||||
model_name="timer",
|
||||
name="duration",
|
||||
field=models.DurationField(
|
||||
editable=False, null=True, verbose_name="Duration"
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='timer',
|
||||
name='end',
|
||||
field=models.DateTimeField(blank=True, editable=False, null=True, verbose_name='End time'),
|
||||
model_name="timer",
|
||||
name="end",
|
||||
field=models.DateTimeField(
|
||||
blank=True, editable=False, null=True, verbose_name="End time"
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='timer',
|
||||
name='name',
|
||||
field=models.CharField(blank=True, max_length=255, null=True, verbose_name='Name'),
|
||||
model_name="timer",
|
||||
name="name",
|
||||
field=models.CharField(
|
||||
blank=True, max_length=255, null=True, verbose_name="Name"
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='timer',
|
||||
name='start',
|
||||
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='Start time'),
|
||||
model_name="timer",
|
||||
name="start",
|
||||
field=models.DateTimeField(
|
||||
default=django.utils.timezone.now, verbose_name="Start time"
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='timer',
|
||||
name='user',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='timers', to=settings.AUTH_USER_MODEL, verbose_name='User'),
|
||||
model_name="timer",
|
||||
name="user",
|
||||
field=models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="timers",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
verbose_name="User",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='tummytime',
|
||||
name='child',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tummy_time', to='core.Child', verbose_name='Child'),
|
||||
model_name="tummytime",
|
||||
name="child",
|
||||
field=models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="tummy_time",
|
||||
to="core.Child",
|
||||
verbose_name="Child",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='tummytime',
|
||||
name='duration',
|
||||
field=models.DurationField(editable=False, null=True, verbose_name='Duration'),
|
||||
model_name="tummytime",
|
||||
name="duration",
|
||||
field=models.DurationField(
|
||||
editable=False, null=True, verbose_name="Duration"
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='tummytime',
|
||||
name='end',
|
||||
field=models.DateTimeField(verbose_name='End time'),
|
||||
model_name="tummytime",
|
||||
name="end",
|
||||
field=models.DateTimeField(verbose_name="End time"),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='tummytime',
|
||||
name='milestone',
|
||||
field=models.CharField(blank=True, max_length=255, verbose_name='Milestone'),
|
||||
model_name="tummytime",
|
||||
name="milestone",
|
||||
field=models.CharField(
|
||||
blank=True, max_length=255, verbose_name="Milestone"
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='tummytime',
|
||||
name='start',
|
||||
field=models.DateTimeField(verbose_name='Start time'),
|
||||
model_name="tummytime",
|
||||
name="start",
|
||||
field=models.DateTimeField(verbose_name="Start time"),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='weight',
|
||||
name='child',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='weight', to='core.Child', verbose_name='Child'),
|
||||
model_name="weight",
|
||||
name="child",
|
||||
field=models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="weight",
|
||||
to="core.Child",
|
||||
verbose_name="Child",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='weight',
|
||||
name='date',
|
||||
field=models.DateField(verbose_name='Date'),
|
||||
model_name="weight",
|
||||
name="date",
|
||||
field=models.DateField(verbose_name="Date"),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='weight',
|
||||
name='weight',
|
||||
field=models.FloatField(verbose_name='Weight'),
|
||||
model_name="weight",
|
||||
name="weight",
|
||||
field=models.FloatField(verbose_name="Weight"),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -6,13 +6,22 @@ from django.db import migrations, models
|
|||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0005_auto_20190416_2048'),
|
||||
("core", "0005_auto_20190416_2048"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='feeding',
|
||||
name='method',
|
||||
field=models.CharField(choices=[('bottle', 'Bottle'), ('left breast', 'Left breast'), ('right breast', 'Right breast'), ('both breasts', 'Both breasts')], max_length=255, verbose_name='Method'),
|
||||
model_name="feeding",
|
||||
name="method",
|
||||
field=models.CharField(
|
||||
choices=[
|
||||
("bottle", "Bottle"),
|
||||
("left breast", "Left breast"),
|
||||
("right breast", "Right breast"),
|
||||
("both breasts", "Both breasts"),
|
||||
],
|
||||
max_length=255,
|
||||
verbose_name="Method",
|
||||
),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -7,23 +7,39 @@ import django.db.models.deletion
|
|||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0006_auto_20190502_1701'),
|
||||
("core", "0006_auto_20190502_1701"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Temperature',
|
||||
name="Temperature",
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('temperature', models.FloatField(verbose_name='Temperature')),
|
||||
('time', models.DateTimeField(verbose_name='Time')),
|
||||
('child', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='temperature', to='core.Child', verbose_name='Child')),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("temperature", models.FloatField(verbose_name="Temperature")),
|
||||
("time", models.DateTimeField(verbose_name="Time")),
|
||||
(
|
||||
"child",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="temperature",
|
||||
to="core.Child",
|
||||
verbose_name="Child",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Temperature',
|
||||
'verbose_name_plural': 'Temperature',
|
||||
'ordering': ['-time'],
|
||||
'default_permissions': ('view', 'add', 'change', 'delete'),
|
||||
"verbose_name": "Temperature",
|
||||
"verbose_name_plural": "Temperature",
|
||||
"ordering": ["-time"],
|
||||
"default_permissions": ("view", "add", "change", "delete"),
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
|
@ -6,13 +6,21 @@ from django.db import migrations, models
|
|||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0007_temperature'),
|
||||
("core", "0007_temperature"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='feeding',
|
||||
name='type',
|
||||
field=models.CharField(choices=[('breast milk', 'Breast milk'), ('formula', 'Formula'), ('fortified breast milk', 'Fortified breast milk')], max_length=255, verbose_name='Type'),
|
||||
model_name="feeding",
|
||||
name="type",
|
||||
field=models.CharField(
|
||||
choices=[
|
||||
("breast milk", "Breast milk"),
|
||||
("formula", "Formula"),
|
||||
("fortified breast milk", "Fortified breast milk"),
|
||||
],
|
||||
max_length=255,
|
||||
verbose_name="Type",
|
||||
),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -6,13 +6,13 @@ from django.db import migrations, models
|
|||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0008_auto_20190607_1422'),
|
||||
("core", "0008_auto_20190607_1422"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='diaperchange',
|
||||
name='amount',
|
||||
field=models.FloatField(blank=True, null=True, verbose_name='Amount'),
|
||||
model_name="diaperchange",
|
||||
name="amount",
|
||||
field=models.FloatField(blank=True, null=True, verbose_name="Amount"),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -7,13 +7,20 @@ import django.db.models.deletion
|
|||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0009_diaperchange_amount'),
|
||||
("core", "0009_diaperchange_amount"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='timer',
|
||||
name='child',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='timers', to='core.Child', verbose_name='Child'),
|
||||
model_name="timer",
|
||||
name="child",
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="timers",
|
||||
to="core.Child",
|
||||
verbose_name="Child",
|
||||
),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -6,33 +6,33 @@ from django.db import migrations, models
|
|||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0010_timer_child'),
|
||||
("core", "0010_timer_child"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='diaperchange',
|
||||
name='notes',
|
||||
field=models.TextField(blank=True, null=True, verbose_name='Notes'),
|
||||
model_name="diaperchange",
|
||||
name="notes",
|
||||
field=models.TextField(blank=True, null=True, verbose_name="Notes"),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='feeding',
|
||||
name='notes',
|
||||
field=models.TextField(blank=True, null=True, verbose_name='Notes'),
|
||||
model_name="feeding",
|
||||
name="notes",
|
||||
field=models.TextField(blank=True, null=True, verbose_name="Notes"),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='sleep',
|
||||
name='notes',
|
||||
field=models.TextField(blank=True, null=True, verbose_name='Notes'),
|
||||
model_name="sleep",
|
||||
name="notes",
|
||||
field=models.TextField(blank=True, null=True, verbose_name="Notes"),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='temperature',
|
||||
name='notes',
|
||||
field=models.TextField(blank=True, null=True, verbose_name='Notes'),
|
||||
model_name="temperature",
|
||||
name="notes",
|
||||
field=models.TextField(blank=True, null=True, verbose_name="Notes"),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='weight',
|
||||
name='notes',
|
||||
field=models.TextField(blank=True, null=True, verbose_name='Notes'),
|
||||
model_name="weight",
|
||||
name="notes",
|
||||
field=models.TextField(blank=True, null=True, verbose_name="Notes"),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -7,13 +7,15 @@ import django.utils.timezone
|
|||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0011_auto_20200214_1939'),
|
||||
("core", "0011_auto_20200214_1939"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='note',
|
||||
name='time',
|
||||
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='Time'),
|
||||
model_name="note",
|
||||
name="time",
|
||||
field=models.DateTimeField(
|
||||
default=django.utils.timezone.now, verbose_name="Time"
|
||||
),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -6,18 +6,38 @@ from django.db import migrations, models
|
|||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0012_auto_20200813_0238'),
|
||||
("core", "0012_auto_20200813_0238"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='feeding',
|
||||
name='method',
|
||||
field=models.CharField(choices=[('bottle', 'Bottle'), ('left breast', 'Left breast'), ('right breast', 'Right breast'), ('both breasts', 'Both breasts'), ('parent fed', 'Parent fed'), ('self fed', 'Self fed')], max_length=255, verbose_name='Method'),
|
||||
model_name="feeding",
|
||||
name="method",
|
||||
field=models.CharField(
|
||||
choices=[
|
||||
("bottle", "Bottle"),
|
||||
("left breast", "Left breast"),
|
||||
("right breast", "Right breast"),
|
||||
("both breasts", "Both breasts"),
|
||||
("parent fed", "Parent fed"),
|
||||
("self fed", "Self fed"),
|
||||
],
|
||||
max_length=255,
|
||||
verbose_name="Method",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='feeding',
|
||||
name='type',
|
||||
field=models.CharField(choices=[('breast milk', 'Breast milk'), ('formula', 'Formula'), ('fortified breast milk', 'Fortified breast milk'), ('solid food', 'Solid food')], max_length=255, verbose_name='Type'),
|
||||
model_name="feeding",
|
||||
name="type",
|
||||
field=models.CharField(
|
||||
choices=[
|
||||
("breast milk", "Breast milk"),
|
||||
("formula", "Formula"),
|
||||
("fortified breast milk", "Fortified breast milk"),
|
||||
("solid food", "Solid food"),
|
||||
],
|
||||
max_length=255,
|
||||
verbose_name="Type",
|
||||
),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -6,13 +6,19 @@ from django.db import migrations, models
|
|||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0013_auto_20210415_0528'),
|
||||
("core", "0013_auto_20210415_0528"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='child',
|
||||
name='slug',
|
||||
field=models.SlugField(allow_unicode=True, editable=False, max_length=100, unique=True, verbose_name='Slug'),
|
||||
model_name="child",
|
||||
name="slug",
|
||||
field=models.SlugField(
|
||||
allow_unicode=True,
|
||||
editable=False,
|
||||
max_length=100,
|
||||
unique=True,
|
||||
verbose_name="Slug",
|
||||
),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -4,25 +4,26 @@ from django.db import migrations, models
|
|||
def set_napping(apps, schema_editor):
|
||||
# The model must be imported to ensure its overridden `save` method is run.
|
||||
from core import models
|
||||
|
||||
for sleep in models.Sleep.objects.all():
|
||||
sleep.save()
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
('core', '0014_alter_child_slug'),
|
||||
("core", "0014_alter_child_slug"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='sleep',
|
||||
name='napping',
|
||||
field=models.BooleanField(null=True, verbose_name='Napping'),
|
||||
model_name="sleep",
|
||||
name="napping",
|
||||
field=models.BooleanField(null=True, verbose_name="Napping"),
|
||||
),
|
||||
migrations.RunPython(set_napping, reverse_code=migrations.RunPython.noop),
|
||||
migrations.AlterField(
|
||||
model_name='sleep',
|
||||
name='napping',
|
||||
field=models.BooleanField(verbose_name='Napping'),
|
||||
model_name="sleep",
|
||||
name="napping",
|
||||
field=models.BooleanField(verbose_name="Napping"),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -6,13 +6,15 @@ from django.db import migrations, models
|
|||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0015_add_nap_field_for_sleep'),
|
||||
("core", "0015_add_nap_field_for_sleep"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='sleep',
|
||||
name='napping',
|
||||
field=models.BooleanField(editable=False, null=True, verbose_name='Napping'),
|
||||
model_name="sleep",
|
||||
name="napping",
|
||||
field=models.BooleanField(
|
||||
editable=False, null=True, verbose_name="Napping"
|
||||
),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -6,13 +6,15 @@ from django.db import migrations, models
|
|||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0016_alter_sleep_napping'),
|
||||
("core", "0016_alter_sleep_napping"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='child',
|
||||
name='last_name',
|
||||
field=models.CharField(blank=True, max_length=255, verbose_name='Last name'),
|
||||
model_name="child",
|
||||
name="last_name",
|
||||
field=models.CharField(
|
||||
blank=True, max_length=255, verbose_name="Last name"
|
||||
),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -7,56 +7,116 @@ import django.db.models.deletion
|
|||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0017_alter_child_last_name'),
|
||||
("core", "0017_alter_child_last_name"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Height',
|
||||
name="Height",
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('height', models.FloatField(verbose_name='Height')),
|
||||
('date', models.DateField(verbose_name='Date')),
|
||||
('notes', models.TextField(blank=True, null=True, verbose_name='Notes')),
|
||||
('child', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='height', to='core.child', verbose_name='Child')),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("height", models.FloatField(verbose_name="Height")),
|
||||
("date", models.DateField(verbose_name="Date")),
|
||||
(
|
||||
"notes",
|
||||
models.TextField(blank=True, null=True, verbose_name="Notes"),
|
||||
),
|
||||
(
|
||||
"child",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="height",
|
||||
to="core.child",
|
||||
verbose_name="Child",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Height',
|
||||
'verbose_name_plural': 'Height',
|
||||
'ordering': ['-date'],
|
||||
'default_permissions': ('view', 'add', 'change', 'delete'),
|
||||
"verbose_name": "Height",
|
||||
"verbose_name_plural": "Height",
|
||||
"ordering": ["-date"],
|
||||
"default_permissions": ("view", "add", "change", "delete"),
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='HeadCircumference',
|
||||
name="HeadCircumference",
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('head_circumference', models.FloatField(verbose_name='Head Circumference')),
|
||||
('date', models.DateField(verbose_name='Date')),
|
||||
('notes', models.TextField(blank=True, null=True, verbose_name='Notes')),
|
||||
('child', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='head_circumference', to='core.child', verbose_name='Child')),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
(
|
||||
"head_circumference",
|
||||
models.FloatField(verbose_name="Head Circumference"),
|
||||
),
|
||||
("date", models.DateField(verbose_name="Date")),
|
||||
(
|
||||
"notes",
|
||||
models.TextField(blank=True, null=True, verbose_name="Notes"),
|
||||
),
|
||||
(
|
||||
"child",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="head_circumference",
|
||||
to="core.child",
|
||||
verbose_name="Child",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Head Circumference',
|
||||
'verbose_name_plural': 'Head Circumference',
|
||||
'ordering': ['-date'],
|
||||
'default_permissions': ('view', 'add', 'change', 'delete'),
|
||||
"verbose_name": "Head Circumference",
|
||||
"verbose_name_plural": "Head Circumference",
|
||||
"ordering": ["-date"],
|
||||
"default_permissions": ("view", "add", "change", "delete"),
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='BMI',
|
||||
name="BMI",
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('bmi', models.FloatField(verbose_name='BMI')),
|
||||
('date', models.DateField(verbose_name='Date')),
|
||||
('notes', models.TextField(blank=True, null=True, verbose_name='Notes')),
|
||||
('child', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='bmi', to='core.child', verbose_name='Child')),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("bmi", models.FloatField(verbose_name="BMI")),
|
||||
("date", models.DateField(verbose_name="Date")),
|
||||
(
|
||||
"notes",
|
||||
models.TextField(blank=True, null=True, verbose_name="Notes"),
|
||||
),
|
||||
(
|
||||
"child",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="bmi",
|
||||
to="core.child",
|
||||
verbose_name="Child",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'BMI',
|
||||
'verbose_name_plural': 'BMI',
|
||||
'ordering': ['-date'],
|
||||
'default_permissions': ('view', 'add', 'change', 'delete'),
|
||||
"verbose_name": "BMI",
|
||||
"verbose_name_plural": "BMI",
|
||||
"ordering": ["-date"],
|
||||
"default_permissions": ("view", "add", "change", "delete"),
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
516
core/models.py
516
core/models.py
|
@ -20,8 +20,8 @@ def validate_date(date, field_name):
|
|||
"""
|
||||
if date and date > timezone.localdate():
|
||||
raise ValidationError(
|
||||
{field_name: _('Date can not be in the future.')},
|
||||
code='date_invalid')
|
||||
{field_name: _("Date can not be in the future.")}, code="date_invalid"
|
||||
)
|
||||
|
||||
|
||||
def validate_duration(model, max_duration=timedelta(hours=24)):
|
||||
|
@ -34,10 +34,10 @@ def validate_duration(model, max_duration=timedelta(hours=24)):
|
|||
if model.start and model.end:
|
||||
if model.start > model.end:
|
||||
raise ValidationError(
|
||||
_('Start time must come before end time.'),
|
||||
code='end_before_start')
|
||||
_("Start time must come before end time."), code="end_before_start"
|
||||
)
|
||||
if model.end - model.start > max_duration:
|
||||
raise ValidationError(_('Duration too long.'), code='max_duration')
|
||||
raise ValidationError(_("Duration too long."), code="max_duration")
|
||||
|
||||
|
||||
def validate_unique_period(queryset, model):
|
||||
|
@ -53,8 +53,9 @@ def validate_unique_period(queryset, model):
|
|||
if model.start and model.end:
|
||||
if queryset.filter(start__lt=model.end, end__gt=model.start):
|
||||
raise ValidationError(
|
||||
_('Another entry intersects the specified time period.'),
|
||||
code='period_intersection')
|
||||
_("Another entry intersects the specified time period."),
|
||||
code="period_intersection",
|
||||
)
|
||||
|
||||
|
||||
def validate_time(time, field_name):
|
||||
|
@ -66,47 +67,38 @@ def validate_time(time, field_name):
|
|||
"""
|
||||
if time and time > timezone.localtime():
|
||||
raise ValidationError(
|
||||
{field_name: _('Date/time can not be in the future.')},
|
||||
code='time_invalid')
|
||||
{field_name: _("Date/time can not be in the future.")}, code="time_invalid"
|
||||
)
|
||||
|
||||
|
||||
class Child(models.Model):
|
||||
model_name = 'child'
|
||||
first_name = models.CharField(max_length=255, verbose_name=_('First name'))
|
||||
model_name = "child"
|
||||
first_name = models.CharField(max_length=255, verbose_name=_("First name"))
|
||||
last_name = models.CharField(
|
||||
blank=True,
|
||||
max_length=255,
|
||||
verbose_name=_('Last name')
|
||||
)
|
||||
birth_date = models.DateField(
|
||||
blank=False,
|
||||
null=False,
|
||||
verbose_name=_('Birth date')
|
||||
blank=True, max_length=255, verbose_name=_("Last name")
|
||||
)
|
||||
birth_date = models.DateField(blank=False, null=False, verbose_name=_("Birth date"))
|
||||
slug = models.SlugField(
|
||||
allow_unicode=True,
|
||||
blank=False,
|
||||
editable=False,
|
||||
max_length=100,
|
||||
unique=True,
|
||||
verbose_name=_('Slug')
|
||||
verbose_name=_("Slug"),
|
||||
)
|
||||
picture = models.ImageField(
|
||||
blank=True,
|
||||
null=True,
|
||||
upload_to='child/picture/',
|
||||
verbose_name=_('Picture')
|
||||
blank=True, null=True, upload_to="child/picture/", verbose_name=_("Picture")
|
||||
)
|
||||
|
||||
objects = models.Manager()
|
||||
|
||||
cache_key_count = 'core.child.count'
|
||||
cache_key_count = "core.child.count"
|
||||
|
||||
class Meta:
|
||||
default_permissions = ('view', 'add', 'change', 'delete')
|
||||
ordering = ['last_name', 'first_name']
|
||||
verbose_name = _('Child')
|
||||
verbose_name_plural = _('Children')
|
||||
default_permissions = ("view", "add", "change", "delete")
|
||||
ordering = ["last_name", "first_name"]
|
||||
verbose_name = _("Child")
|
||||
verbose_name_plural = _("Children")
|
||||
|
||||
def __str__(self):
|
||||
return self.name()
|
||||
|
@ -124,132 +116,119 @@ class Child(models.Model):
|
|||
if not self.last_name:
|
||||
return self.first_name
|
||||
if reverse:
|
||||
return '{}, {}'.format(self.last_name, self.first_name)
|
||||
return '{} {}'.format(self.first_name, self.last_name)
|
||||
return "{}, {}".format(self.last_name, self.first_name)
|
||||
return "{} {}".format(self.first_name, self.last_name)
|
||||
|
||||
@classmethod
|
||||
def count(cls):
|
||||
""" Get a (cached) count of total number of Child instances. """
|
||||
"""Get a (cached) count of total number of Child instances."""
|
||||
return cache.get_or_set(cls.cache_key_count, Child.objects.count, None)
|
||||
|
||||
|
||||
class DiaperChange(models.Model):
|
||||
model_name = 'diaperchange'
|
||||
model_name = "diaperchange"
|
||||
child = models.ForeignKey(
|
||||
'Child',
|
||||
"Child",
|
||||
on_delete=models.CASCADE,
|
||||
related_name='diaper_change',
|
||||
verbose_name=_('Child')
|
||||
related_name="diaper_change",
|
||||
verbose_name=_("Child"),
|
||||
)
|
||||
time = models.DateTimeField(
|
||||
blank=False,
|
||||
null=False,
|
||||
verbose_name=_('Time')
|
||||
)
|
||||
wet = models.BooleanField(verbose_name=_('Wet'))
|
||||
solid = models.BooleanField(verbose_name=_('Solid'))
|
||||
time = models.DateTimeField(blank=False, null=False, verbose_name=_("Time"))
|
||||
wet = models.BooleanField(verbose_name=_("Wet"))
|
||||
solid = models.BooleanField(verbose_name=_("Solid"))
|
||||
color = models.CharField(
|
||||
blank=True,
|
||||
choices=[
|
||||
('black', _('Black')),
|
||||
('brown', _('Brown')),
|
||||
('green', _('Green')),
|
||||
('yellow', _('Yellow')),
|
||||
("black", _("Black")),
|
||||
("brown", _("Brown")),
|
||||
("green", _("Green")),
|
||||
("yellow", _("Yellow")),
|
||||
],
|
||||
max_length=255,
|
||||
verbose_name=_('Color')
|
||||
verbose_name=_("Color"),
|
||||
)
|
||||
amount = models.FloatField(blank=True, null=True, verbose_name=_('Amount'))
|
||||
notes = models.TextField(blank=True, null=True, verbose_name=_('Notes'))
|
||||
amount = models.FloatField(blank=True, null=True, verbose_name=_("Amount"))
|
||||
notes = models.TextField(blank=True, null=True, verbose_name=_("Notes"))
|
||||
|
||||
objects = models.Manager()
|
||||
|
||||
class Meta:
|
||||
default_permissions = ('view', 'add', 'change', 'delete')
|
||||
ordering = ['-time']
|
||||
verbose_name = _('Diaper Change')
|
||||
verbose_name_plural = _('Diaper Changes')
|
||||
default_permissions = ("view", "add", "change", "delete")
|
||||
ordering = ["-time"]
|
||||
verbose_name = _("Diaper Change")
|
||||
verbose_name_plural = _("Diaper Changes")
|
||||
|
||||
def __str__(self):
|
||||
return str(_('Diaper Change'))
|
||||
return str(_("Diaper Change"))
|
||||
|
||||
def attributes(self):
|
||||
attributes = []
|
||||
if self.wet:
|
||||
attributes.append(self._meta.get_field('wet').verbose_name)
|
||||
attributes.append(self._meta.get_field("wet").verbose_name)
|
||||
if self.solid:
|
||||
attributes.append(self._meta.get_field('solid').verbose_name)
|
||||
attributes.append(self._meta.get_field("solid").verbose_name)
|
||||
if self.color:
|
||||
attributes.append(self.get_color_display())
|
||||
return attributes
|
||||
|
||||
def clean(self):
|
||||
validate_time(self.time, 'time')
|
||||
validate_time(self.time, "time")
|
||||
|
||||
# One or both of Wet and Solid is required.
|
||||
if not self.wet and not self.solid:
|
||||
raise ValidationError(
|
||||
_('Wet and/or solid is required.'), code='wet_or_solid')
|
||||
_("Wet and/or solid is required."), code="wet_or_solid"
|
||||
)
|
||||
|
||||
|
||||
class Feeding(models.Model):
|
||||
model_name = 'feeding'
|
||||
model_name = "feeding"
|
||||
child = models.ForeignKey(
|
||||
'Child',
|
||||
"Child",
|
||||
on_delete=models.CASCADE,
|
||||
related_name='feeding',
|
||||
verbose_name=_('Child')
|
||||
)
|
||||
start = models.DateTimeField(
|
||||
blank=False,
|
||||
null=False,
|
||||
verbose_name=_('Start time')
|
||||
)
|
||||
end = models.DateTimeField(
|
||||
blank=False,
|
||||
null=False,
|
||||
verbose_name=_('End time')
|
||||
related_name="feeding",
|
||||
verbose_name=_("Child"),
|
||||
)
|
||||
start = models.DateTimeField(blank=False, null=False, verbose_name=_("Start time"))
|
||||
end = models.DateTimeField(blank=False, null=False, verbose_name=_("End time"))
|
||||
duration = models.DurationField(
|
||||
editable=False,
|
||||
null=True,
|
||||
verbose_name=_('Duration')
|
||||
editable=False, null=True, verbose_name=_("Duration")
|
||||
)
|
||||
type = models.CharField(
|
||||
choices=[
|
||||
('breast milk', _('Breast milk')),
|
||||
('formula', _('Formula')),
|
||||
('fortified breast milk', _('Fortified breast milk')),
|
||||
('solid food', _('Solid food')),
|
||||
("breast milk", _("Breast milk")),
|
||||
("formula", _("Formula")),
|
||||
("fortified breast milk", _("Fortified breast milk")),
|
||||
("solid food", _("Solid food")),
|
||||
],
|
||||
max_length=255,
|
||||
verbose_name=_('Type')
|
||||
verbose_name=_("Type"),
|
||||
)
|
||||
method = models.CharField(
|
||||
choices=[
|
||||
('bottle', _('Bottle')),
|
||||
('left breast', _('Left breast')),
|
||||
('right breast', _('Right breast')),
|
||||
('both breasts', _('Both breasts')),
|
||||
('parent fed', _('Parent fed')),
|
||||
('self fed', _('Self fed')),
|
||||
("bottle", _("Bottle")),
|
||||
("left breast", _("Left breast")),
|
||||
("right breast", _("Right breast")),
|
||||
("both breasts", _("Both breasts")),
|
||||
("parent fed", _("Parent fed")),
|
||||
("self fed", _("Self fed")),
|
||||
],
|
||||
max_length=255,
|
||||
verbose_name=_('Method')
|
||||
verbose_name=_("Method"),
|
||||
)
|
||||
amount = models.FloatField(blank=True, null=True, verbose_name=_('Amount'))
|
||||
notes = models.TextField(blank=True, null=True, verbose_name=_('Notes'))
|
||||
amount = models.FloatField(blank=True, null=True, verbose_name=_("Amount"))
|
||||
notes = models.TextField(blank=True, null=True, verbose_name=_("Notes"))
|
||||
|
||||
objects = models.Manager()
|
||||
|
||||
class Meta:
|
||||
default_permissions = ('view', 'add', 'change', 'delete')
|
||||
ordering = ['-start']
|
||||
verbose_name = _('Feeding')
|
||||
verbose_name_plural = _('Feedings')
|
||||
default_permissions = ("view", "add", "change", "delete")
|
||||
ordering = ["-start"]
|
||||
verbose_name = _("Feeding")
|
||||
verbose_name_plural = _("Feedings")
|
||||
|
||||
def __str__(self):
|
||||
return str(_('Feeding'))
|
||||
return str(_("Feeding"))
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if self.start and self.end:
|
||||
|
@ -257,37 +236,32 @@ class Feeding(models.Model):
|
|||
super(Feeding, self).save(*args, **kwargs)
|
||||
|
||||
def clean(self):
|
||||
validate_time(self.start, 'start')
|
||||
validate_time(self.end, 'end')
|
||||
validate_time(self.start, "start")
|
||||
validate_time(self.end, "end")
|
||||
validate_duration(self)
|
||||
validate_unique_period(Feeding.objects.filter(child=self.child), self)
|
||||
|
||||
|
||||
class Note(models.Model):
|
||||
model_name = 'note'
|
||||
model_name = "note"
|
||||
child = models.ForeignKey(
|
||||
'Child',
|
||||
on_delete=models.CASCADE,
|
||||
related_name='note',
|
||||
verbose_name=_('Child')
|
||||
"Child", on_delete=models.CASCADE, related_name="note", verbose_name=_("Child")
|
||||
)
|
||||
note = models.TextField(verbose_name=_('Note'))
|
||||
note = models.TextField(verbose_name=_("Note"))
|
||||
time = models.DateTimeField(
|
||||
default=timezone.now,
|
||||
blank=False,
|
||||
verbose_name=_('Time')
|
||||
default=timezone.now, blank=False, verbose_name=_("Time")
|
||||
)
|
||||
|
||||
objects = models.Manager()
|
||||
|
||||
class Meta:
|
||||
default_permissions = ('view', 'add', 'change', 'delete')
|
||||
ordering = ['-time']
|
||||
verbose_name = _('Note')
|
||||
verbose_name_plural = _('Notes')
|
||||
default_permissions = ("view", "add", "change", "delete")
|
||||
ordering = ["-time"]
|
||||
verbose_name = _("Note")
|
||||
verbose_name_plural = _("Notes")
|
||||
|
||||
def __str__(self):
|
||||
return str(_('Note'))
|
||||
return str(_("Note"))
|
||||
|
||||
|
||||
class NapsManager(models.Manager):
|
||||
|
@ -297,53 +271,38 @@ class NapsManager(models.Manager):
|
|||
|
||||
|
||||
class Sleep(models.Model):
|
||||
model_name = 'sleep'
|
||||
model_name = "sleep"
|
||||
child = models.ForeignKey(
|
||||
'Child',
|
||||
on_delete=models.CASCADE,
|
||||
related_name='sleep',
|
||||
verbose_name=_('Child')
|
||||
)
|
||||
napping = models.BooleanField(
|
||||
editable=False,
|
||||
null=True,
|
||||
verbose_name=_('Napping')
|
||||
)
|
||||
start = models.DateTimeField(
|
||||
blank=False,
|
||||
null=False,
|
||||
verbose_name=_('Start time')
|
||||
)
|
||||
end = models.DateTimeField(
|
||||
blank=False,
|
||||
null=False,
|
||||
verbose_name=_('End time')
|
||||
"Child", on_delete=models.CASCADE, related_name="sleep", verbose_name=_("Child")
|
||||
)
|
||||
napping = models.BooleanField(editable=False, null=True, verbose_name=_("Napping"))
|
||||
start = models.DateTimeField(blank=False, null=False, verbose_name=_("Start time"))
|
||||
end = models.DateTimeField(blank=False, null=False, verbose_name=_("End time"))
|
||||
duration = models.DurationField(
|
||||
editable=False,
|
||||
null=True,
|
||||
verbose_name=_('Duration')
|
||||
editable=False, null=True, verbose_name=_("Duration")
|
||||
)
|
||||
notes = models.TextField(blank=True, null=True, verbose_name=_('Notes'))
|
||||
notes = models.TextField(blank=True, null=True, verbose_name=_("Notes"))
|
||||
|
||||
objects = models.Manager()
|
||||
naps = NapsManager()
|
||||
|
||||
class Meta:
|
||||
default_permissions = ('view', 'add', 'change', 'delete')
|
||||
ordering = ['-start']
|
||||
verbose_name = _('Sleep')
|
||||
verbose_name_plural = _('Sleep')
|
||||
default_permissions = ("view", "add", "change", "delete")
|
||||
ordering = ["-start"]
|
||||
verbose_name = _("Sleep")
|
||||
verbose_name_plural = _("Sleep")
|
||||
|
||||
def __str__(self):
|
||||
return str(_('Sleep'))
|
||||
return str(_("Sleep"))
|
||||
|
||||
@property
|
||||
def nap(self):
|
||||
nap_start_min = timezone.datetime.strptime(
|
||||
settings.BABY_BUDDY['NAP_START_MIN'], '%H:%M').time()
|
||||
settings.BABY_BUDDY["NAP_START_MIN"], "%H:%M"
|
||||
).time()
|
||||
nap_start_max = timezone.datetime.strptime(
|
||||
settings.BABY_BUDDY['NAP_START_MAX'], '%H:%M').time()
|
||||
settings.BABY_BUDDY["NAP_START_MAX"], "%H:%M"
|
||||
).time()
|
||||
local_start_time = timezone.localtime(self.start).time()
|
||||
return nap_start_min <= local_start_time <= nap_start_max
|
||||
|
||||
|
@ -354,115 +313,94 @@ class Sleep(models.Model):
|
|||
super(Sleep, self).save(*args, **kwargs)
|
||||
|
||||
def clean(self):
|
||||
validate_time(self.start, 'start')
|
||||
validate_time(self.end, 'end')
|
||||
validate_time(self.start, "start")
|
||||
validate_time(self.end, "end")
|
||||
validate_duration(self)
|
||||
validate_unique_period(Sleep.objects.filter(child=self.child), self)
|
||||
|
||||
|
||||
class Temperature(models.Model):
|
||||
model_name = 'temperature'
|
||||
model_name = "temperature"
|
||||
child = models.ForeignKey(
|
||||
'Child',
|
||||
"Child",
|
||||
on_delete=models.CASCADE,
|
||||
related_name='temperature',
|
||||
verbose_name=_('Child')
|
||||
related_name="temperature",
|
||||
verbose_name=_("Child"),
|
||||
)
|
||||
temperature = models.FloatField(
|
||||
blank=False,
|
||||
null=False,
|
||||
verbose_name=_('Temperature')
|
||||
blank=False, null=False, verbose_name=_("Temperature")
|
||||
)
|
||||
time = models.DateTimeField(
|
||||
blank=False,
|
||||
null=False,
|
||||
verbose_name=_('Time')
|
||||
)
|
||||
notes = models.TextField(blank=True, null=True, verbose_name=_('Notes'))
|
||||
time = models.DateTimeField(blank=False, null=False, verbose_name=_("Time"))
|
||||
notes = models.TextField(blank=True, null=True, verbose_name=_("Notes"))
|
||||
|
||||
objects = models.Manager()
|
||||
|
||||
class Meta:
|
||||
default_permissions = ('view', 'add', 'change', 'delete')
|
||||
ordering = ['-time']
|
||||
verbose_name = _('Temperature')
|
||||
verbose_name_plural = _('Temperature')
|
||||
default_permissions = ("view", "add", "change", "delete")
|
||||
ordering = ["-time"]
|
||||
verbose_name = _("Temperature")
|
||||
verbose_name_plural = _("Temperature")
|
||||
|
||||
def __str__(self):
|
||||
return str(_('Temperature'))
|
||||
return str(_("Temperature"))
|
||||
|
||||
def clean(self):
|
||||
validate_time(self.time, 'time')
|
||||
validate_time(self.time, "time")
|
||||
|
||||
|
||||
class Timer(models.Model):
|
||||
model_name = 'timer'
|
||||
model_name = "timer"
|
||||
child = models.ForeignKey(
|
||||
'Child',
|
||||
"Child",
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=models.CASCADE,
|
||||
related_name='timers',
|
||||
verbose_name=_('Child')
|
||||
related_name="timers",
|
||||
verbose_name=_("Child"),
|
||||
)
|
||||
name = models.CharField(
|
||||
blank=True,
|
||||
max_length=255,
|
||||
null=True,
|
||||
verbose_name=_('Name')
|
||||
blank=True, max_length=255, null=True, verbose_name=_("Name")
|
||||
)
|
||||
start = models.DateTimeField(
|
||||
default=timezone.now,
|
||||
blank=False,
|
||||
verbose_name=_('Start time')
|
||||
default=timezone.now, blank=False, verbose_name=_("Start time")
|
||||
)
|
||||
end = models.DateTimeField(
|
||||
blank=True,
|
||||
editable=False,
|
||||
null=True,
|
||||
verbose_name=_('End time')
|
||||
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')
|
||||
editable=False, null=True, verbose_name=_("Duration")
|
||||
)
|
||||
active = models.BooleanField(default=True, editable=False, verbose_name=_("Active"))
|
||||
user = models.ForeignKey(
|
||||
'auth.User',
|
||||
"auth.User",
|
||||
on_delete=models.CASCADE,
|
||||
related_name='timers',
|
||||
verbose_name=_('User')
|
||||
related_name="timers",
|
||||
verbose_name=_("User"),
|
||||
)
|
||||
|
||||
objects = models.Manager()
|
||||
|
||||
class Meta:
|
||||
default_permissions = ('view', 'add', 'change', 'delete')
|
||||
ordering = ['-active', '-start', '-end']
|
||||
verbose_name = _('Timer')
|
||||
verbose_name_plural = _('Timers')
|
||||
default_permissions = ("view", "add", "change", "delete")
|
||||
ordering = ["-active", "-start", "-end"]
|
||||
verbose_name = _("Timer")
|
||||
verbose_name_plural = _("Timers")
|
||||
|
||||
def __str__(self):
|
||||
return self.name or str(format_lazy(_('Timer #{id}'), id=self.id))
|
||||
return self.name or str(format_lazy(_("Timer #{id}"), id=self.id))
|
||||
|
||||
@property
|
||||
def title_with_child(self):
|
||||
""" Get Timer title with child name in parenthesis. """
|
||||
"""Get Timer title with child name in parenthesis."""
|
||||
title = str(self)
|
||||
# Only actually add the name if there is more than one Child instance.
|
||||
if title and self.child and Child.count() > 1:
|
||||
title = format_lazy('{title} ({child})', title=title,
|
||||
child=self.child)
|
||||
title = format_lazy("{title} ({child})", title=title, child=self.child)
|
||||
return title
|
||||
|
||||
@property
|
||||
def user_username(self):
|
||||
""" Get Timer user's name with a preference for the full name. """
|
||||
"""Get Timer user's name with a preference for the full name."""
|
||||
if self.user.get_full_name():
|
||||
return self.user.get_full_name()
|
||||
return self.user.get_username()
|
||||
|
@ -499,51 +437,39 @@ class Timer(models.Model):
|
|||
super(Timer, self).save(*args, **kwargs)
|
||||
|
||||
def clean(self):
|
||||
validate_time(self.start, 'start')
|
||||
validate_time(self.start, "start")
|
||||
if self.end:
|
||||
validate_time(self.end, 'end')
|
||||
validate_time(self.end, "end")
|
||||
validate_duration(self)
|
||||
|
||||
|
||||
class TummyTime(models.Model):
|
||||
model_name = 'tummytime'
|
||||
model_name = "tummytime"
|
||||
child = models.ForeignKey(
|
||||
'Child',
|
||||
"Child",
|
||||
on_delete=models.CASCADE,
|
||||
related_name='tummy_time',
|
||||
verbose_name=_('Child')
|
||||
)
|
||||
start = models.DateTimeField(
|
||||
blank=False,
|
||||
null=False,
|
||||
verbose_name=_('Start time')
|
||||
)
|
||||
end = models.DateTimeField(
|
||||
blank=False,
|
||||
null=False,
|
||||
verbose_name=_('End time')
|
||||
related_name="tummy_time",
|
||||
verbose_name=_("Child"),
|
||||
)
|
||||
start = models.DateTimeField(blank=False, null=False, verbose_name=_("Start time"))
|
||||
end = models.DateTimeField(blank=False, null=False, verbose_name=_("End time"))
|
||||
duration = models.DurationField(
|
||||
editable=False,
|
||||
null=True,
|
||||
verbose_name=_('Duration')
|
||||
editable=False, null=True, verbose_name=_("Duration")
|
||||
)
|
||||
milestone = models.CharField(
|
||||
blank=True,
|
||||
max_length=255,
|
||||
verbose_name=_('Milestone')
|
||||
blank=True, max_length=255, verbose_name=_("Milestone")
|
||||
)
|
||||
|
||||
objects = models.Manager()
|
||||
|
||||
class Meta:
|
||||
default_permissions = ('view', 'add', 'change', 'delete')
|
||||
ordering = ['-start']
|
||||
verbose_name = _('Tummy Time')
|
||||
verbose_name_plural = _('Tummy Time')
|
||||
default_permissions = ("view", "add", "change", "delete")
|
||||
ordering = ["-start"]
|
||||
verbose_name = _("Tummy Time")
|
||||
verbose_name_plural = _("Tummy Time")
|
||||
|
||||
def __str__(self):
|
||||
return str(_('Tummy Time'))
|
||||
return str(_("Tummy Time"))
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if self.start and self.end:
|
||||
|
@ -551,148 +477,114 @@ class TummyTime(models.Model):
|
|||
super(TummyTime, self).save(*args, **kwargs)
|
||||
|
||||
def clean(self):
|
||||
validate_time(self.start, 'start')
|
||||
validate_time(self.end, 'end')
|
||||
validate_time(self.start, "start")
|
||||
validate_time(self.end, "end")
|
||||
validate_duration(self)
|
||||
validate_unique_period(
|
||||
TummyTime.objects.filter(child=self.child), self)
|
||||
validate_unique_period(TummyTime.objects.filter(child=self.child), self)
|
||||
|
||||
|
||||
class Weight(models.Model):
|
||||
model_name = 'weight'
|
||||
model_name = "weight"
|
||||
child = models.ForeignKey(
|
||||
'Child',
|
||||
"Child",
|
||||
on_delete=models.CASCADE,
|
||||
related_name='weight',
|
||||
verbose_name=_('Child')
|
||||
related_name="weight",
|
||||
verbose_name=_("Child"),
|
||||
)
|
||||
weight = models.FloatField(
|
||||
blank=False,
|
||||
null=False,
|
||||
verbose_name=_('Weight')
|
||||
)
|
||||
date = models.DateField(
|
||||
blank=False,
|
||||
null=False,
|
||||
verbose_name=_('Date')
|
||||
)
|
||||
notes = models.TextField(blank=True, null=True, verbose_name=_('Notes'))
|
||||
weight = models.FloatField(blank=False, null=False, verbose_name=_("Weight"))
|
||||
date = models.DateField(blank=False, null=False, verbose_name=_("Date"))
|
||||
notes = models.TextField(blank=True, null=True, verbose_name=_("Notes"))
|
||||
|
||||
objects = models.Manager()
|
||||
|
||||
class Meta:
|
||||
default_permissions = ('view', 'add', 'change', 'delete')
|
||||
ordering = ['-date']
|
||||
verbose_name = _('Weight')
|
||||
verbose_name_plural = _('Weight')
|
||||
default_permissions = ("view", "add", "change", "delete")
|
||||
ordering = ["-date"]
|
||||
verbose_name = _("Weight")
|
||||
verbose_name_plural = _("Weight")
|
||||
|
||||
def __str__(self):
|
||||
return str(_('Weight'))
|
||||
return str(_("Weight"))
|
||||
|
||||
def clean(self):
|
||||
validate_date(self.date, 'date')
|
||||
validate_date(self.date, "date")
|
||||
|
||||
|
||||
class Height(models.Model):
|
||||
model_name = 'height'
|
||||
model_name = "height"
|
||||
child = models.ForeignKey(
|
||||
'Child',
|
||||
"Child",
|
||||
on_delete=models.CASCADE,
|
||||
related_name='height',
|
||||
verbose_name=_('Child')
|
||||
related_name="height",
|
||||
verbose_name=_("Child"),
|
||||
)
|
||||
height = models.FloatField(
|
||||
blank=False,
|
||||
null=False,
|
||||
verbose_name=_('Height')
|
||||
)
|
||||
date = models.DateField(
|
||||
blank=False,
|
||||
null=False,
|
||||
verbose_name=_('Date')
|
||||
)
|
||||
notes = models.TextField(blank=True, null=True, verbose_name=_('Notes'))
|
||||
height = models.FloatField(blank=False, null=False, verbose_name=_("Height"))
|
||||
date = models.DateField(blank=False, null=False, verbose_name=_("Date"))
|
||||
notes = models.TextField(blank=True, null=True, verbose_name=_("Notes"))
|
||||
|
||||
objects = models.Manager()
|
||||
|
||||
class Meta:
|
||||
default_permissions = ('view', 'add', 'change', 'delete')
|
||||
ordering = ['-date']
|
||||
verbose_name = _('Height')
|
||||
verbose_name_plural = _('Height')
|
||||
default_permissions = ("view", "add", "change", "delete")
|
||||
ordering = ["-date"]
|
||||
verbose_name = _("Height")
|
||||
verbose_name_plural = _("Height")
|
||||
|
||||
def __str__(self):
|
||||
return str(_('Height'))
|
||||
return str(_("Height"))
|
||||
|
||||
def clean(self):
|
||||
validate_date(self.date, 'date')
|
||||
validate_date(self.date, "date")
|
||||
|
||||
|
||||
class HeadCircumference(models.Model):
|
||||
model_name = 'head_circumference'
|
||||
model_name = "head_circumference"
|
||||
child = models.ForeignKey(
|
||||
'Child',
|
||||
"Child",
|
||||
on_delete=models.CASCADE,
|
||||
related_name='head_circumference',
|
||||
verbose_name=_('Child')
|
||||
related_name="head_circumference",
|
||||
verbose_name=_("Child"),
|
||||
)
|
||||
head_circumference = models.FloatField(
|
||||
blank=False,
|
||||
null=False,
|
||||
verbose_name=_('Head Circumference')
|
||||
blank=False, null=False, verbose_name=_("Head Circumference")
|
||||
)
|
||||
date = models.DateField(
|
||||
blank=False,
|
||||
null=False,
|
||||
verbose_name=_('Date')
|
||||
)
|
||||
notes = models.TextField(blank=True, null=True, verbose_name=_('Notes'))
|
||||
date = models.DateField(blank=False, null=False, verbose_name=_("Date"))
|
||||
notes = models.TextField(blank=True, null=True, verbose_name=_("Notes"))
|
||||
|
||||
objects = models.Manager()
|
||||
|
||||
class Meta:
|
||||
default_permissions = ('view', 'add', 'change', 'delete')
|
||||
ordering = ['-date']
|
||||
verbose_name = _('Head Circumference')
|
||||
verbose_name_plural = _('Head Circumference')
|
||||
default_permissions = ("view", "add", "change", "delete")
|
||||
ordering = ["-date"]
|
||||
verbose_name = _("Head Circumference")
|
||||
verbose_name_plural = _("Head Circumference")
|
||||
|
||||
def __str__(self):
|
||||
return str(_('Head Circumference'))
|
||||
return str(_("Head Circumference"))
|
||||
|
||||
def clean(self):
|
||||
validate_date(self.date, 'date')
|
||||
validate_date(self.date, "date")
|
||||
|
||||
|
||||
class BMI(models.Model):
|
||||
model_name = 'bmi'
|
||||
model_name = "bmi"
|
||||
child = models.ForeignKey(
|
||||
'Child',
|
||||
on_delete=models.CASCADE,
|
||||
related_name='bmi',
|
||||
verbose_name=_('Child')
|
||||
"Child", on_delete=models.CASCADE, related_name="bmi", verbose_name=_("Child")
|
||||
)
|
||||
bmi = models.FloatField(
|
||||
blank=False,
|
||||
null=False,
|
||||
verbose_name=_('BMI')
|
||||
)
|
||||
date = models.DateField(
|
||||
blank=False,
|
||||
null=False,
|
||||
verbose_name=_('Date')
|
||||
)
|
||||
notes = models.TextField(blank=True, null=True, verbose_name=_('Notes'))
|
||||
bmi = models.FloatField(blank=False, null=False, verbose_name=_("BMI"))
|
||||
date = models.DateField(blank=False, null=False, verbose_name=_("Date"))
|
||||
notes = models.TextField(blank=True, null=True, verbose_name=_("Notes"))
|
||||
|
||||
objects = models.Manager()
|
||||
|
||||
class Meta:
|
||||
default_permissions = ('view', 'add', 'change', 'delete')
|
||||
ordering = ['-date']
|
||||
verbose_name = _('BMI')
|
||||
verbose_name_plural = _('BMI')
|
||||
default_permissions = ("view", "add", "change", "delete")
|
||||
ordering = ["-date"]
|
||||
verbose_name = _("BMI")
|
||||
verbose_name_plural = _("BMI")
|
||||
|
||||
def __str__(self):
|
||||
return str(_('BMI'))
|
||||
return str(_("BMI"))
|
||||
|
||||
def clean(self):
|
||||
validate_date(self.date, 'date')
|
||||
validate_date(self.date, "date")
|
||||
|
|
|
@ -13,8 +13,8 @@ def bool_icon(value):
|
|||
:returns: a string of html for an icon representing the boolean.
|
||||
"""
|
||||
if value:
|
||||
classes = 'icon-true text-success'
|
||||
classes = "icon-true text-success"
|
||||
else:
|
||||
classes = 'icon-false text-danger'
|
||||
classes = "icon-false text-danger"
|
||||
icon_html = '<i class="{}" aria-hidden="true"></i>'.format(classes)
|
||||
return mark_safe(icon_html)
|
||||
|
|
|
@ -8,7 +8,7 @@ register = template.Library()
|
|||
|
||||
|
||||
@register.simple_tag(takes_context=True)
|
||||
def datetimepicker_format(context, format_string='L LT'):
|
||||
def datetimepicker_format(context, format_string="L LT"):
|
||||
"""
|
||||
Return a datetime format string for momentjs, with support for 24 hour time
|
||||
override setting.
|
||||
|
@ -17,8 +17,8 @@ def datetimepicker_format(context, format_string='L LT'):
|
|||
:return: the format string to use, as 24 hour time if configured.
|
||||
"""
|
||||
try:
|
||||
user = context['request'].user
|
||||
if hasattr(user, 'settings') and user.settings.language:
|
||||
user = context["request"].user
|
||||
if hasattr(user, "settings") and user.settings.language:
|
||||
language = user.settings.language
|
||||
else:
|
||||
language = settings.LANGUAGE_CODE
|
||||
|
@ -26,17 +26,17 @@ def datetimepicker_format(context, format_string='L LT'):
|
|||
language = None
|
||||
|
||||
if settings.USE_24_HOUR_TIME_FORMAT:
|
||||
if format_string == 'L LT':
|
||||
format_string = 'L HH:mm'
|
||||
elif format_string == 'L LTS':
|
||||
format_string = 'L HH:mm:ss'
|
||||
elif language and language == 'en-GB':
|
||||
if format_string == "L LT":
|
||||
format_string = "L HH:mm"
|
||||
elif format_string == "L LTS":
|
||||
format_string = "L HH:mm:ss"
|
||||
elif language and language == "en-GB":
|
||||
# Force 12-hour format if 24 hour format is not configured for en-GB
|
||||
# (Django default is 12H, momentjs default is 24H).
|
||||
if format_string == 'L LT':
|
||||
format_string = 'L h:mm a'
|
||||
elif format_string == 'L LTS':
|
||||
format_string = 'L h:mm:ss a'
|
||||
if format_string == "L LT":
|
||||
format_string = "L h:mm a"
|
||||
elif format_string == "L LTS":
|
||||
format_string = "L h:mm:ss a"
|
||||
|
||||
return format_string
|
||||
|
||||
|
@ -57,21 +57,22 @@ def datetime_short(date):
|
|||
|
||||
now = timezone.localtime()
|
||||
if now.date() == date.date():
|
||||
date_string = _('Today')
|
||||
time_string = formats.date_format(date, format='TIME_FORMAT')
|
||||
elif now.year == date.year and formats.get_format(
|
||||
'SHORT_MONTH_DAY_FORMAT') != 'SHORT_MONTH_DAY_FORMAT':
|
||||
date_string = _("Today")
|
||||
time_string = formats.date_format(date, format="TIME_FORMAT")
|
||||
elif (
|
||||
now.year == date.year
|
||||
and formats.get_format("SHORT_MONTH_DAY_FORMAT") != "SHORT_MONTH_DAY_FORMAT"
|
||||
):
|
||||
# Use the custom `SHORT_MONTH_DAY_FORMAT` format if available for the
|
||||
# current locale.
|
||||
date_string = formats.date_format(date,
|
||||
format='SHORT_MONTH_DAY_FORMAT')
|
||||
time_string = formats.date_format(date, format='TIME_FORMAT')
|
||||
date_string = formats.date_format(date, format="SHORT_MONTH_DAY_FORMAT")
|
||||
time_string = formats.date_format(date, format="TIME_FORMAT")
|
||||
|
||||
if not date_string:
|
||||
date_string = formats.date_format(date, format='SHORT_DATETIME_FORMAT')
|
||||
date_string = formats.date_format(date, format="SHORT_DATETIME_FORMAT")
|
||||
|
||||
if date_string and time_string:
|
||||
datetime_string = _('{}, {}').format(date_string, time_string)
|
||||
datetime_string = _("{}, {}").format(date_string, time_string)
|
||||
else:
|
||||
datetime_string = date_string
|
||||
|
||||
|
|
|
@ -17,18 +17,18 @@ def child_age_string(birth_date):
|
|||
:return: a string representation of time since `birth_date`.
|
||||
"""
|
||||
if not birth_date:
|
||||
return ''
|
||||
return ""
|
||||
# Return "0 days" for anything under one day.
|
||||
elif timezone.localdate() - birth_date < timezone.timedelta(days=1):
|
||||
return _('0 days')
|
||||
return _("0 days")
|
||||
try:
|
||||
return timesince.timesince(birth_date, depth=1)
|
||||
except (ValueError, TypeError):
|
||||
return ''
|
||||
return ""
|
||||
|
||||
|
||||
@register.filter
|
||||
def duration_string(duration, precision='s'):
|
||||
def duration_string(duration, precision="s"):
|
||||
"""
|
||||
Format a duration (e.g. "2 hours, 3 minutes, 35 seconds").
|
||||
:param duration: a timedetla instance.
|
||||
|
@ -37,11 +37,11 @@ def duration_string(duration, precision='s'):
|
|||
:returns: a string representation of the duration.
|
||||
"""
|
||||
if not duration:
|
||||
return ''
|
||||
return ""
|
||||
try:
|
||||
return utils.duration_string(duration, precision)
|
||||
except (ValueError, TypeError):
|
||||
return ''
|
||||
return ""
|
||||
|
||||
|
||||
@register.filter
|
||||
|
|
|
@ -8,7 +8,7 @@ from core.models import Timer
|
|||
register = template.Library()
|
||||
|
||||
|
||||
@register.inclusion_tag('core/timer_nav.html', takes_context=True)
|
||||
@register.inclusion_tag("core/timer_nav.html", takes_context=True)
|
||||
def timer_nav(context, active=True):
|
||||
"""
|
||||
Get a list of active Timer instances to include in the nav menu.
|
||||
|
@ -16,17 +16,17 @@ def timer_nav(context, active=True):
|
|||
:param active: the state of Timers to filter.
|
||||
:returns: a dictionary with timers data.
|
||||
"""
|
||||
request = context['request'] or None
|
||||
request = context["request"] or None
|
||||
timers = Timer.objects.filter(active=active)
|
||||
perms = context['perms'] or None
|
||||
perms = context["perms"] or None
|
||||
# The 'next' parameter is currently not used.
|
||||
return {'timers': timers, 'perms': perms, 'next': request.path}
|
||||
return {"timers": timers, "perms": perms, "next": request.path}
|
||||
|
||||
|
||||
@register.simple_tag(takes_context=True)
|
||||
def instance_add_url(context, url_name):
|
||||
timer = context['timer']
|
||||
url = '{}?timer={}'.format(reverse(url_name), timer.id)
|
||||
timer = context["timer"]
|
||||
url = "{}?timer={}".format(reverse(url_name), timer.id)
|
||||
if timer.child:
|
||||
url += '&child={}'.format(timer.child.slug)
|
||||
url += "&child={}".format(timer.child.slug)
|
||||
return url
|
||||
|
|
|
@ -20,35 +20,29 @@ class FormsTestCaseBase(TestCase):
|
|||
def setUpClass(cls):
|
||||
super(FormsTestCaseBase, cls).setUpClass()
|
||||
fake = Factory.create()
|
||||
call_command('migrate', verbosity=0)
|
||||
call_command("migrate", verbosity=0)
|
||||
|
||||
cls.c = HttpClient()
|
||||
|
||||
fake_user = fake.simple_profile()
|
||||
credentials = {
|
||||
'username': fake_user['username'],
|
||||
'password': fake.password()
|
||||
}
|
||||
cls.user = User.objects.create_user(
|
||||
is_superuser=True, **credentials)
|
||||
credentials = {"username": fake_user["username"], "password": fake.password()}
|
||||
cls.user = User.objects.create_user(is_superuser=True, **credentials)
|
||||
cls.c.login(**credentials)
|
||||
|
||||
cls.child = models.Child.objects.create(
|
||||
first_name='Child',
|
||||
last_name='One',
|
||||
birth_date=timezone.localdate()
|
||||
first_name="Child", last_name="One", birth_date=timezone.localdate()
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def localdate_string(datetime=None):
|
||||
""" Converts an object to a local date string for form input. """
|
||||
date_format = get_format('DATE_INPUT_FORMATS')[0]
|
||||
"""Converts an object to a local date string for form input."""
|
||||
date_format = get_format("DATE_INPUT_FORMATS")[0]
|
||||
return timezone.localdate(datetime).strftime(date_format)
|
||||
|
||||
@staticmethod
|
||||
def localtime_string(datetime=None):
|
||||
""" Converts an object to a local time string for form input. """
|
||||
datetime_format = get_format('DATETIME_INPUT_FORMATS')[0]
|
||||
"""Converts an object to a local time string for form input."""
|
||||
datetime_format = get_format("DATETIME_INPUT_FORMATS")[0]
|
||||
return timezone.localtime(datetime).strftime(datetime_format)
|
||||
|
||||
|
||||
|
@ -57,107 +51,98 @@ class InitialValuesTestCase(FormsTestCaseBase):
|
|||
def setUpClass(cls):
|
||||
super(InitialValuesTestCase, cls).setUpClass()
|
||||
cls.timer = models.Timer.objects.create(
|
||||
user=cls.user,
|
||||
start=timezone.localtime() - timezone.timedelta(minutes=30)
|
||||
user=cls.user, start=timezone.localtime() - timezone.timedelta(minutes=30)
|
||||
)
|
||||
|
||||
def test_child_with_one_child(self):
|
||||
page = self.c.get('/sleep/add/')
|
||||
self.assertEqual(page.context['form'].initial['child'], self.child)
|
||||
page = self.c.get("/sleep/add/")
|
||||
self.assertEqual(page.context["form"].initial["child"], self.child)
|
||||
|
||||
def test_child_with_parameter(self):
|
||||
child_two = models.Child.objects.create(
|
||||
first_name='Child',
|
||||
last_name='Two',
|
||||
birth_date=timezone.localdate()
|
||||
first_name="Child", last_name="Two", birth_date=timezone.localdate()
|
||||
)
|
||||
|
||||
page = self.c.get('/sleep/add/')
|
||||
self.assertTrue('child' not in page.context['form'].initial)
|
||||
page = self.c.get("/sleep/add/")
|
||||
self.assertTrue("child" not in page.context["form"].initial)
|
||||
|
||||
page = self.c.get('/sleep/add/?child={}'.format(self.child.slug))
|
||||
self.assertEqual(page.context['form'].initial['child'], self.child)
|
||||
page = self.c.get("/sleep/add/?child={}".format(self.child.slug))
|
||||
self.assertEqual(page.context["form"].initial["child"], self.child)
|
||||
|
||||
page = self.c.get('/sleep/add/?child={}'.format(child_two.slug))
|
||||
self.assertEqual(page.context['form'].initial['child'], child_two)
|
||||
page = self.c.get("/sleep/add/?child={}".format(child_two.slug))
|
||||
self.assertEqual(page.context["form"].initial["child"], child_two)
|
||||
|
||||
def test_feeding_type(self):
|
||||
child_two = models.Child.objects.create(
|
||||
first_name='Child',
|
||||
last_name='Two',
|
||||
birth_date=timezone.localdate()
|
||||
first_name="Child", last_name="Two", birth_date=timezone.localdate()
|
||||
)
|
||||
child_three = models.Child.objects.create(
|
||||
first_name='Child',
|
||||
last_name='Three',
|
||||
birth_date=timezone.localdate()
|
||||
first_name="Child", last_name="Three", birth_date=timezone.localdate()
|
||||
)
|
||||
start_time = timezone.localtime() - timezone.timedelta(hours=4)
|
||||
end_time = timezone.localtime() - timezone.timedelta(hours=3,
|
||||
minutes=30)
|
||||
end_time = timezone.localtime() - timezone.timedelta(hours=3, minutes=30)
|
||||
f_one = models.Feeding.objects.create(
|
||||
child=self.child,
|
||||
start=start_time,
|
||||
end=end_time,
|
||||
type='breast milk',
|
||||
method='left breast'
|
||||
type="breast milk",
|
||||
method="left breast",
|
||||
)
|
||||
f_two = models.Feeding.objects.create(
|
||||
child=child_two,
|
||||
start=start_time,
|
||||
end=end_time,
|
||||
type='formula',
|
||||
method='bottle'
|
||||
type="formula",
|
||||
method="bottle",
|
||||
)
|
||||
f_three = models.Feeding.objects.create(
|
||||
child=child_three,
|
||||
start=start_time,
|
||||
end=end_time,
|
||||
type='fortified breast milk',
|
||||
method='bottle'
|
||||
type="fortified breast milk",
|
||||
method="bottle",
|
||||
)
|
||||
|
||||
page = self.c.get('/feedings/add/')
|
||||
self.assertTrue('type' not in page.context['form'].initial)
|
||||
page = self.c.get("/feedings/add/")
|
||||
self.assertTrue("type" not in page.context["form"].initial)
|
||||
|
||||
page = self.c.get('/feedings/add/?child={}'.format(self.child.slug))
|
||||
self.assertEqual(page.context['form'].initial['type'], f_one.type)
|
||||
self.assertFalse('method' in page.context['form'].initial)
|
||||
page = self.c.get("/feedings/add/?child={}".format(self.child.slug))
|
||||
self.assertEqual(page.context["form"].initial["type"], f_one.type)
|
||||
self.assertFalse("method" in page.context["form"].initial)
|
||||
|
||||
page = self.c.get('/feedings/add/?child={}'.format(child_two.slug))
|
||||
self.assertEqual(page.context['form'].initial['type'], f_two.type)
|
||||
self.assertEqual(page.context['form'].initial['method'], f_two.method)
|
||||
page = self.c.get("/feedings/add/?child={}".format(child_two.slug))
|
||||
self.assertEqual(page.context["form"].initial["type"], f_two.type)
|
||||
self.assertEqual(page.context["form"].initial["method"], f_two.method)
|
||||
|
||||
page = self.c.get('/feedings/add/?child={}'.format(child_three.slug))
|
||||
self.assertEqual(page.context['form'].initial['type'], f_three.type)
|
||||
self.assertEqual(page.context['form'].initial['method'],
|
||||
f_three.method)
|
||||
page = self.c.get("/feedings/add/?child={}".format(child_three.slug))
|
||||
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):
|
||||
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/")
|
||||
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)
|
||||
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()
|
||||
params = {
|
||||
'child': self.child.id,
|
||||
'start': self.localtime_string(self.timer.start),
|
||||
'end': self.localtime_string(end)
|
||||
"child": self.child.id,
|
||||
"start": self.localtime_string(self.timer.start),
|
||||
"end": self.localtime_string(end),
|
||||
}
|
||||
page = self.c.post('/sleep/add/?timer={}'.format(self.timer.id),
|
||||
params, follow=True)
|
||||
page = self.c.post(
|
||||
"/sleep/add/?timer={}".format(self.timer.id), params, follow=True
|
||||
)
|
||||
self.assertEqual(page.status_code, 200)
|
||||
self.timer.refresh_from_db()
|
||||
self.assertFalse(self.timer.active)
|
||||
self.assertEqual(self.localtime_string(self.timer.end), params['end'])
|
||||
self.assertEqual(self.localtime_string(self.timer.end), params["end"])
|
||||
|
||||
|
||||
class ChildFormsTestCase(FormsTestCaseBase):
|
||||
|
@ -168,40 +153,44 @@ class ChildFormsTestCase(FormsTestCaseBase):
|
|||
|
||||
def test_add(self):
|
||||
params = {
|
||||
'first_name': 'Child',
|
||||
'last_name': 'Two',
|
||||
'birth_date': timezone.localdate()
|
||||
"first_name": "Child",
|
||||
"last_name": "Two",
|
||||
"birth_date": timezone.localdate(),
|
||||
}
|
||||
page = self.c.post('/children/add/', params, follow=True)
|
||||
page = self.c.post("/children/add/", params, follow=True)
|
||||
self.assertEqual(page.status_code, 200)
|
||||
self.assertContains(page, 'Child entry added')
|
||||
self.assertContains(page, "Child entry added")
|
||||
|
||||
def test_edit(self):
|
||||
params = {
|
||||
'first_name': 'Name',
|
||||
'last_name': 'Changed',
|
||||
'birth_date': self.child.birth_date
|
||||
"first_name": "Name",
|
||||
"last_name": "Changed",
|
||||
"birth_date": self.child.birth_date,
|
||||
}
|
||||
page = self.c.post('/children/{}/edit/'.format(self.child.slug),
|
||||
params, follow=True)
|
||||
page = self.c.post(
|
||||
"/children/{}/edit/".format(self.child.slug), params, follow=True
|
||||
)
|
||||
self.assertEqual(page.status_code, 200)
|
||||
self.child.refresh_from_db()
|
||||
self.assertEqual(self.child.last_name, params['last_name'])
|
||||
self.assertContains(page, 'Child entry updated')
|
||||
self.assertEqual(self.child.last_name, params["last_name"])
|
||||
self.assertContains(page, "Child entry updated")
|
||||
|
||||
def test_delete(self):
|
||||
params = {'confirm_name': 'Incorrect'}
|
||||
page = self.c.post('/children/{}/delete/'.format(self.child.slug),
|
||||
params, follow=True)
|
||||
params = {"confirm_name": "Incorrect"}
|
||||
page = self.c.post(
|
||||
"/children/{}/delete/".format(self.child.slug), params, follow=True
|
||||
)
|
||||
self.assertEqual(page.status_code, 200)
|
||||
self.assertFormError(page, 'form', 'confirm_name',
|
||||
'Name does not match child name.')
|
||||
self.assertFormError(
|
||||
page, "form", "confirm_name", "Name does not match child name."
|
||||
)
|
||||
|
||||
params['confirm_name'] = str(self.child)
|
||||
page = self.c.post('/children/{}/delete/'.format(self.child.slug),
|
||||
params, follow=True)
|
||||
params["confirm_name"] = str(self.child)
|
||||
page = self.c.post(
|
||||
"/children/{}/delete/".format(self.child.slug), params, follow=True
|
||||
)
|
||||
self.assertEqual(page.status_code, 200)
|
||||
self.assertContains(page, 'Child entry deleted')
|
||||
self.assertContains(page, "Child entry deleted")
|
||||
|
||||
|
||||
class DiaperChangeFormsTestCase(FormsTestCaseBase):
|
||||
|
@ -213,55 +202,48 @@ class DiaperChangeFormsTestCase(FormsTestCaseBase):
|
|||
time=timezone.localtime(),
|
||||
wet=True,
|
||||
solid=True,
|
||||
color='black',
|
||||
amount=0.45
|
||||
color="black",
|
||||
amount=0.45,
|
||||
)
|
||||
|
||||
def test_add(self):
|
||||
child = models.Child.objects.first()
|
||||
params = {
|
||||
'child': child.id,
|
||||
'time': self.localtime_string(),
|
||||
'color': 'black',
|
||||
'amount': 0.45
|
||||
"child": child.id,
|
||||
"time": self.localtime_string(),
|
||||
"color": "black",
|
||||
"amount": 0.45,
|
||||
}
|
||||
page = self.c.post('/changes/add/', params)
|
||||
page = self.c.post("/changes/add/", params)
|
||||
self.assertEqual(page.status_code, 200)
|
||||
self.assertFormError(page, 'form', None,
|
||||
'Wet and/or solid is required.')
|
||||
self.assertFormError(page, "form", None, "Wet and/or solid is required.")
|
||||
|
||||
params.update({'wet': 1, 'solid': 1, 'color': 'black'})
|
||||
page = self.c.post('/changes/add/', params, follow=True)
|
||||
params.update({"wet": 1, "solid": 1, "color": "black"})
|
||||
page = self.c.post("/changes/add/", params, follow=True)
|
||||
self.assertEqual(page.status_code, 200)
|
||||
self.assertContains(
|
||||
page,
|
||||
'Diaper Change entry for {} added'.format(str(child))
|
||||
)
|
||||
self.assertContains(page, "Diaper Change entry for {} added".format(str(child)))
|
||||
|
||||
def test_edit(self):
|
||||
params = {
|
||||
'child': self.change.child.id,
|
||||
'time': self.localtime_string(),
|
||||
'wet': self.change.wet,
|
||||
'solid': self.change.solid,
|
||||
'color': self.change.color,
|
||||
'amount': 1.23
|
||||
"child": self.change.child.id,
|
||||
"time": self.localtime_string(),
|
||||
"wet": self.change.wet,
|
||||
"solid": self.change.solid,
|
||||
"color": self.change.color,
|
||||
"amount": 1.23,
|
||||
}
|
||||
page = self.c.post('/changes/{}/'.format(self.change.id),
|
||||
params, follow=True)
|
||||
page = self.c.post("/changes/{}/".format(self.change.id), params, follow=True)
|
||||
self.assertEqual(page.status_code, 200)
|
||||
self.change.refresh_from_db()
|
||||
self.assertEqual(self.change.amount, params['amount'])
|
||||
self.assertEqual(self.change.amount, params["amount"])
|
||||
self.assertContains(
|
||||
page,
|
||||
'Diaper Change entry for {} updated'.format(str(self.change.child))
|
||||
page, "Diaper Change entry for {} updated".format(str(self.change.child))
|
||||
)
|
||||
|
||||
def test_delete(self):
|
||||
page = self.c.post('/changes/{}/delete/'.format(self.change.id),
|
||||
follow=True)
|
||||
page = self.c.post("/changes/{}/delete/".format(self.change.id), follow=True)
|
||||
self.assertEqual(page.status_code, 200)
|
||||
self.assertContains(page, 'Diaper Change entry deleted')
|
||||
self.assertContains(page, "Diaper Change entry deleted")
|
||||
|
||||
|
||||
class FeedingFormsTestCase(FormsTestCaseBase):
|
||||
|
@ -272,55 +254,49 @@ class FeedingFormsTestCase(FormsTestCaseBase):
|
|||
child=cls.child,
|
||||
start=timezone.localtime() - timezone.timedelta(hours=2),
|
||||
end=timezone.localtime() - timezone.timedelta(hours=1, minutes=30),
|
||||
type='breast milk',
|
||||
method='left breast',
|
||||
amount=2.5
|
||||
type="breast milk",
|
||||
method="left breast",
|
||||
amount=2.5,
|
||||
)
|
||||
|
||||
def test_add(self):
|
||||
end = timezone.localtime()
|
||||
start = end - timezone.timedelta(minutes=30)
|
||||
params = {
|
||||
'child': self.child.id,
|
||||
'start': self.localtime_string(start),
|
||||
'end': self.localtime_string(end),
|
||||
'type': 'formula',
|
||||
'method': 'bottle',
|
||||
'amount': 0
|
||||
"child": self.child.id,
|
||||
"start": self.localtime_string(start),
|
||||
"end": self.localtime_string(end),
|
||||
"type": "formula",
|
||||
"method": "bottle",
|
||||
"amount": 0,
|
||||
}
|
||||
page = self.c.post('/feedings/add/', params, follow=True)
|
||||
page = self.c.post("/feedings/add/", params, follow=True)
|
||||
self.assertEqual(page.status_code, 200)
|
||||
self.assertContains(
|
||||
page,
|
||||
'Feeding entry for {} added'.format(str(self.child))
|
||||
)
|
||||
self.assertContains(page, "Feeding entry for {} added".format(str(self.child)))
|
||||
|
||||
def test_edit(self):
|
||||
end = timezone.localtime()
|
||||
start = end - timezone.timedelta(minutes=30)
|
||||
params = {
|
||||
'child': self.feeding.child.id,
|
||||
'start': self.localtime_string(start),
|
||||
'end': self.localtime_string(end),
|
||||
'type': self.feeding.type,
|
||||
'method': self.feeding.method,
|
||||
'amount': 100
|
||||
"child": self.feeding.child.id,
|
||||
"start": self.localtime_string(start),
|
||||
"end": self.localtime_string(end),
|
||||
"type": self.feeding.type,
|
||||
"method": self.feeding.method,
|
||||
"amount": 100,
|
||||
}
|
||||
page = self.c.post('/feedings/{}/'.format(self.feeding.id),
|
||||
params, follow=True)
|
||||
page = self.c.post("/feedings/{}/".format(self.feeding.id), params, follow=True)
|
||||
self.assertEqual(page.status_code, 200)
|
||||
self.feeding.refresh_from_db()
|
||||
self.assertEqual(self.feeding.amount, params['amount'])
|
||||
self.assertEqual(self.feeding.amount, params["amount"])
|
||||
self.assertContains(
|
||||
page,
|
||||
'Feeding entry for {} updated'.format(str(self.feeding.child))
|
||||
page, "Feeding entry for {} updated".format(str(self.feeding.child))
|
||||
)
|
||||
|
||||
def test_delete(self):
|
||||
page = self.c.post('/feedings/{}/delete/'.format(self.feeding.id),
|
||||
follow=True)
|
||||
page = self.c.post("/feedings/{}/delete/".format(self.feeding.id), follow=True)
|
||||
self.assertEqual(page.status_code, 200)
|
||||
self.assertContains(page, 'Feeding entry deleted')
|
||||
self.assertContains(page, "Feeding entry deleted")
|
||||
|
||||
|
||||
class SleepFormsTestCase(FormsTestCaseBase):
|
||||
|
@ -330,7 +306,7 @@ class SleepFormsTestCase(FormsTestCaseBase):
|
|||
cls.sleep = models.Sleep.objects.create(
|
||||
child=cls.child,
|
||||
start=timezone.localtime() - timezone.timedelta(hours=6),
|
||||
end=timezone.localtime() - timezone.timedelta(hours=4)
|
||||
end=timezone.localtime() - timezone.timedelta(hours=4),
|
||||
)
|
||||
|
||||
def test_add(self):
|
||||
|
@ -340,44 +316,35 @@ class SleepFormsTestCase(FormsTestCaseBase):
|
|||
end = timezone.localtime()
|
||||
start = end - timezone.timedelta(minutes=2)
|
||||
params = {
|
||||
'child': self.child.id,
|
||||
'start': self.localtime_string(start),
|
||||
'end': self.localtime_string(end),
|
||||
"child": self.child.id,
|
||||
"start": self.localtime_string(start),
|
||||
"end": self.localtime_string(end),
|
||||
}
|
||||
|
||||
page = self.c.post('/sleep/add/', params, follow=True)
|
||||
page = self.c.post("/sleep/add/", params, follow=True)
|
||||
self.assertEqual(page.status_code, 200)
|
||||
self.assertContains(
|
||||
page,
|
||||
'Sleep entry for {} added'.format(str(self.child))
|
||||
)
|
||||
self.assertContains(page, "Sleep entry for {} added".format(str(self.child)))
|
||||
|
||||
def test_edit(self):
|
||||
end = timezone.localtime()
|
||||
start = end - timezone.timedelta(minutes=2)
|
||||
params = {
|
||||
'child': self.sleep.child.id,
|
||||
'start': self.localtime_string(start),
|
||||
'end': self.localtime_string(end),
|
||||
"child": self.sleep.child.id,
|
||||
"start": self.localtime_string(start),
|
||||
"end": self.localtime_string(end),
|
||||
}
|
||||
page = self.c.post('/sleep/{}/'.format(self.sleep.id),
|
||||
params, follow=True)
|
||||
page = self.c.post("/sleep/{}/".format(self.sleep.id), params, follow=True)
|
||||
self.assertEqual(page.status_code, 200)
|
||||
self.sleep.refresh_from_db()
|
||||
self.assertEqual(
|
||||
self.localtime_string(self.sleep.end),
|
||||
params['end']
|
||||
)
|
||||
self.assertEqual(self.localtime_string(self.sleep.end), params["end"])
|
||||
self.assertContains(
|
||||
page,
|
||||
'Sleep entry for {} updated'.format(str(self.sleep.child))
|
||||
page, "Sleep entry for {} updated".format(str(self.sleep.child))
|
||||
)
|
||||
|
||||
def test_delete(self):
|
||||
page = self.c.post('/sleep/{}/delete/'.format(self.sleep.id),
|
||||
follow=True)
|
||||
page = self.c.post("/sleep/{}/delete/".format(self.sleep.id), follow=True)
|
||||
self.assertEqual(page.status_code, 200)
|
||||
self.assertContains(page, 'Sleep entry deleted')
|
||||
self.assertContains(page, "Sleep entry deleted")
|
||||
|
||||
|
||||
class TemperatureFormsTestCase(FormsTestCaseBase):
|
||||
|
@ -387,44 +354,40 @@ class TemperatureFormsTestCase(FormsTestCaseBase):
|
|||
cls.temp = models.Temperature.objects.create(
|
||||
child=cls.child,
|
||||
temperature=98.6,
|
||||
time=timezone.localtime() - timezone.timedelta(days=1)
|
||||
time=timezone.localtime() - timezone.timedelta(days=1),
|
||||
)
|
||||
|
||||
def test_add(self):
|
||||
params = {
|
||||
'child': self.child.id,
|
||||
'temperature': '98.6',
|
||||
'time': self.localtime_string()
|
||||
"child": self.child.id,
|
||||
"temperature": "98.6",
|
||||
"time": self.localtime_string(),
|
||||
}
|
||||
|
||||
page = self.c.post('/temperature/add/', params, follow=True)
|
||||
page = self.c.post("/temperature/add/", params, follow=True)
|
||||
self.assertEqual(page.status_code, 200)
|
||||
self.assertContains(
|
||||
page,
|
||||
'Temperature entry for {} added'.format(str(self.child))
|
||||
page, "Temperature entry for {} added".format(str(self.child))
|
||||
)
|
||||
|
||||
def test_edit(self):
|
||||
params = {
|
||||
'child': self.temp.child.id,
|
||||
'temperature': self.temp.temperature + 2,
|
||||
'time': self.localtime_string()
|
||||
"child": self.temp.child.id,
|
||||
"temperature": self.temp.temperature + 2,
|
||||
"time": self.localtime_string(),
|
||||
}
|
||||
page = self.c.post('/temperature/{}/'.format(self.temp.id),
|
||||
params, follow=True)
|
||||
page = self.c.post("/temperature/{}/".format(self.temp.id), params, follow=True)
|
||||
self.assertEqual(page.status_code, 200)
|
||||
self.temp.refresh_from_db()
|
||||
self.assertEqual(self.temp.temperature, params['temperature'])
|
||||
self.assertEqual(self.temp.temperature, params["temperature"])
|
||||
self.assertContains(
|
||||
page,
|
||||
'Temperature entry for {} updated'.format(str(self.temp.child))
|
||||
page, "Temperature entry for {} updated".format(str(self.temp.child))
|
||||
)
|
||||
|
||||
def test_delete(self):
|
||||
page = self.c.post('/temperature/{}/delete/'.format(self.temp.id),
|
||||
follow=True)
|
||||
page = self.c.post("/temperature/{}/delete/".format(self.temp.id), follow=True)
|
||||
self.assertEqual(page.status_code, 200)
|
||||
self.assertContains(page, 'Temperature entry deleted')
|
||||
self.assertContains(page, "Temperature entry deleted")
|
||||
|
||||
|
||||
class TummyTimeFormsTestCase(FormsTestCaseBase):
|
||||
|
@ -434,50 +397,46 @@ class TummyTimeFormsTestCase(FormsTestCaseBase):
|
|||
cls.tt = models.TummyTime.objects.create(
|
||||
child=cls.child,
|
||||
start=timezone.localtime() - timezone.timedelta(hours=2),
|
||||
end=timezone.localtime() - timezone.timedelta(hours=1, minutes=50)
|
||||
end=timezone.localtime() - timezone.timedelta(hours=1, minutes=50),
|
||||
)
|
||||
|
||||
def test_add(self):
|
||||
end = timezone.localtime()
|
||||
start = end - timezone.timedelta(minutes=2)
|
||||
params = {
|
||||
'child': self.child.id,
|
||||
'start': self.localtime_string(start),
|
||||
'end': self.localtime_string(end),
|
||||
'milestone': ''
|
||||
"child": self.child.id,
|
||||
"start": self.localtime_string(start),
|
||||
"end": self.localtime_string(end),
|
||||
"milestone": "",
|
||||
}
|
||||
|
||||
page = self.c.post('/tummy-time/add/', params, follow=True)
|
||||
page = self.c.post("/tummy-time/add/", params, follow=True)
|
||||
self.assertEqual(page.status_code, 200)
|
||||
self.assertContains(
|
||||
page,
|
||||
'Tummy Time entry for {} added'.format(str(self.child))
|
||||
page, "Tummy Time entry for {} added".format(str(self.child))
|
||||
)
|
||||
|
||||
def test_edit(self):
|
||||
end = timezone.localtime()
|
||||
start = end - timezone.timedelta(minutes=1, seconds=32)
|
||||
params = {
|
||||
'child': self.tt.child.id,
|
||||
'start': self.localtime_string(start),
|
||||
'end': self.localtime_string(end),
|
||||
'milestone': 'Moved head!'
|
||||
"child": self.tt.child.id,
|
||||
"start": self.localtime_string(start),
|
||||
"end": self.localtime_string(end),
|
||||
"milestone": "Moved head!",
|
||||
}
|
||||
page = self.c.post('/tummy-time/{}/'.format(self.tt.id),
|
||||
params, follow=True)
|
||||
page = self.c.post("/tummy-time/{}/".format(self.tt.id), params, follow=True)
|
||||
self.assertEqual(page.status_code, 200)
|
||||
self.tt.refresh_from_db()
|
||||
self.assertEqual(self.tt.milestone, params['milestone'])
|
||||
self.assertEqual(self.tt.milestone, params["milestone"])
|
||||
self.assertContains(
|
||||
page,
|
||||
'Tummy Time entry for {} updated'.format(str(self.tt.child))
|
||||
page, "Tummy Time entry for {} updated".format(str(self.tt.child))
|
||||
)
|
||||
|
||||
def test_delete(self):
|
||||
page = self.c.post('/tummy-time/{}/delete/'.format(self.tt.id),
|
||||
follow=True)
|
||||
page = self.c.post("/tummy-time/{}/delete/".format(self.tt.id), follow=True)
|
||||
self.assertEqual(page.status_code, 200)
|
||||
self.assertContains(page, 'Tummy Time entry deleted')
|
||||
self.assertContains(page, "Tummy Time entry deleted")
|
||||
|
||||
|
||||
class TimerFormsTestCase(FormsTestCaseBase):
|
||||
|
@ -488,49 +447,47 @@ class TimerFormsTestCase(FormsTestCaseBase):
|
|||
|
||||
def test_add(self):
|
||||
params = {
|
||||
'child': self.child.id,
|
||||
'name': 'Test Timer',
|
||||
'start': self.localtime_string()
|
||||
"child": self.child.id,
|
||||
"name": "Test Timer",
|
||||
"start": self.localtime_string(),
|
||||
}
|
||||
page = self.c.post('/timers/add/', params, follow=True)
|
||||
page = self.c.post("/timers/add/", params, follow=True)
|
||||
self.assertEqual(page.status_code, 200)
|
||||
self.assertContains(page, params['name'])
|
||||
self.assertContains(page, params['child'])
|
||||
self.assertContains(page, params["name"])
|
||||
self.assertContains(page, params["child"])
|
||||
|
||||
def test_edit(self):
|
||||
start_time = self.timer.start - timezone.timedelta(hours=1)
|
||||
params = {
|
||||
'name': 'New Timer Name',
|
||||
'start': self.localtime_string(start_time)
|
||||
}
|
||||
page = self.c.post('/timers/{}/edit/'.format(self.timer.id), params,
|
||||
follow=True)
|
||||
params = {"name": "New Timer Name", "start": self.localtime_string(start_time)}
|
||||
page = self.c.post(
|
||||
"/timers/{}/edit/".format(self.timer.id), params, follow=True
|
||||
)
|
||||
self.assertEqual(page.status_code, 200)
|
||||
self.assertContains(page, params['name'])
|
||||
self.assertContains(page, params["name"])
|
||||
self.timer.refresh_from_db()
|
||||
self.assertEqual(
|
||||
self.localtime_string(self.timer.start), params['start'])
|
||||
self.assertEqual(self.localtime_string(self.timer.start), params["start"])
|
||||
|
||||
def test_edit_stopped(self):
|
||||
self.timer.stop()
|
||||
params = {
|
||||
'name': 'Edit stopped timer',
|
||||
'start': self.localtime_string(self.timer.start),
|
||||
'end': self.localtime_string(self.timer.end),
|
||||
"name": "Edit stopped timer",
|
||||
"start": self.localtime_string(self.timer.start),
|
||||
"end": self.localtime_string(self.timer.end),
|
||||
}
|
||||
page = self.c.post('/timers/{}/edit/'.format(self.timer.id), params,
|
||||
follow=True)
|
||||
page = self.c.post(
|
||||
"/timers/{}/edit/".format(self.timer.id), params, follow=True
|
||||
)
|
||||
self.assertEqual(page.status_code, 200)
|
||||
|
||||
def test_delete_inactive(self):
|
||||
models.Timer.objects.create(user=self.user)
|
||||
self.assertEqual(models.Timer.objects.count(), 2)
|
||||
self.timer.stop()
|
||||
page = self.c.post('/timers/delete-inactive/', follow=True)
|
||||
page = self.c.post("/timers/delete-inactive/", follow=True)
|
||||
self.assertEqual(page.status_code, 200)
|
||||
messages = list(page.context['messages'])
|
||||
messages = list(page.context["messages"])
|
||||
self.assertEqual(len(messages), 1)
|
||||
self.assertEqual(str(messages[0]), 'All inactive timers deleted.')
|
||||
self.assertEqual(str(messages[0]), "All inactive timers deleted.")
|
||||
self.assertEqual(models.Timer.objects.count(), 1)
|
||||
|
||||
|
||||
|
@ -538,51 +495,50 @@ class ValidationsTestCase(FormsTestCaseBase):
|
|||
def test_validate_date(self):
|
||||
future = timezone.localtime() + timezone.timedelta(days=1)
|
||||
params = {
|
||||
'child': self.child,
|
||||
'weight': '8.5',
|
||||
'date': self.localdate_string(future)
|
||||
"child": self.child,
|
||||
"weight": "8.5",
|
||||
"date": self.localdate_string(future),
|
||||
}
|
||||
entry = models.Weight.objects.create(**params)
|
||||
|
||||
page = self.c.post('/weight/{}/'.format(entry.id), params, follow=True)
|
||||
page = self.c.post("/weight/{}/".format(entry.id), params, follow=True)
|
||||
self.assertEqual(page.status_code, 200)
|
||||
self.assertFormError(page, 'form', 'date',
|
||||
'Date can not be in the future.')
|
||||
self.assertFormError(page, "form", "date", "Date can not be in the future.")
|
||||
|
||||
def test_validate_duration(self):
|
||||
end = timezone.localtime() - timezone.timedelta(minutes=10)
|
||||
start = end + timezone.timedelta(minutes=5)
|
||||
params = {
|
||||
'child': self.child,
|
||||
'start': self.localtime_string(start),
|
||||
'end': self.localtime_string(end),
|
||||
'milestone': ''
|
||||
"child": self.child,
|
||||
"start": self.localtime_string(start),
|
||||
"end": self.localtime_string(end),
|
||||
"milestone": "",
|
||||
}
|
||||
|
||||
page = self.c.post('/tummy-time/add/', params, follow=True)
|
||||
page = self.c.post("/tummy-time/add/", params, follow=True)
|
||||
self.assertEqual(page.status_code, 200)
|
||||
self.assertFormError(page, 'form', None,
|
||||
'Start time must come before end time.')
|
||||
self.assertFormError(
|
||||
page, "form", None, "Start time must come before end time."
|
||||
)
|
||||
|
||||
start = end - timezone.timedelta(weeks=53)
|
||||
params['start'] = self.localtime_string(start)
|
||||
page = self.c.post('/tummy-time/add/', params, follow=True)
|
||||
params["start"] = self.localtime_string(start)
|
||||
page = self.c.post("/tummy-time/add/", params, follow=True)
|
||||
self.assertEqual(page.status_code, 200)
|
||||
self.assertFormError(page, 'form', None, 'Duration too long.')
|
||||
self.assertFormError(page, "form", None, "Duration too long.")
|
||||
|
||||
def test_validate_time(self):
|
||||
future = timezone.localtime() + timezone.timedelta(hours=1)
|
||||
params = {
|
||||
'child': self.child,
|
||||
'start': self.localtime_string(),
|
||||
'end': self.localtime_string(future),
|
||||
'milestone': ''
|
||||
"child": self.child,
|
||||
"start": self.localtime_string(),
|
||||
"end": self.localtime_string(future),
|
||||
"milestone": "",
|
||||
}
|
||||
|
||||
page = self.c.post('/tummy-time/add/', params, follow=True)
|
||||
page = self.c.post("/tummy-time/add/", params, follow=True)
|
||||
self.assertEqual(page.status_code, 200)
|
||||
self.assertFormError(page, 'form', 'end',
|
||||
'Date/time can not be in the future.')
|
||||
self.assertFormError(page, "form", "end", "Date/time can not be in the future.")
|
||||
|
||||
def test_validate_unique_period(self):
|
||||
entry = models.TummyTime.objects.create(
|
||||
|
@ -594,19 +550,17 @@ class ValidationsTestCase(FormsTestCaseBase):
|
|||
start = entry.start - timezone.timedelta(minutes=2)
|
||||
end = entry.end + timezone.timedelta(minutes=2)
|
||||
params = {
|
||||
'child': entry.child.id,
|
||||
'start': self.localtime_string(start),
|
||||
'end': self.localtime_string(end),
|
||||
'milestone': ''
|
||||
"child": entry.child.id,
|
||||
"start": self.localtime_string(start),
|
||||
"end": self.localtime_string(end),
|
||||
"milestone": "",
|
||||
}
|
||||
|
||||
page = self.c.post('/tummy-time/add/', params, follow=True)
|
||||
page = self.c.post("/tummy-time/add/", params, follow=True)
|
||||
self.assertEqual(page.status_code, 200)
|
||||
self.assertFormError(
|
||||
page,
|
||||
'form',
|
||||
None,
|
||||
'Another entry intersects the specified time period.')
|
||||
page, "form", None, "Another entry intersects the specified time period."
|
||||
)
|
||||
|
||||
|
||||
class WeightFormsTest(FormsTestCaseBase):
|
||||
|
@ -616,41 +570,35 @@ class WeightFormsTest(FormsTestCaseBase):
|
|||
cls.weight = models.Weight.objects.create(
|
||||
child=cls.child,
|
||||
weight=8,
|
||||
date=timezone.localdate() - timezone.timedelta(days=2)
|
||||
date=timezone.localdate() - timezone.timedelta(days=2),
|
||||
)
|
||||
|
||||
def test_add(self):
|
||||
params = {
|
||||
'child': self.child.id,
|
||||
'weight': 8.5,
|
||||
'date': self.localdate_string()
|
||||
"child": self.child.id,
|
||||
"weight": 8.5,
|
||||
"date": self.localdate_string(),
|
||||
}
|
||||
|
||||
page = self.c.post('/weight/add/', params, follow=True)
|
||||
page = self.c.post("/weight/add/", params, follow=True)
|
||||
self.assertEqual(page.status_code, 200)
|
||||
self.assertContains(
|
||||
page,
|
||||
'Weight entry for {} added'.format(str(self.child))
|
||||
)
|
||||
self.assertContains(page, "Weight entry for {} added".format(str(self.child)))
|
||||
|
||||
def test_edit(self):
|
||||
params = {
|
||||
'child': self.weight.child.id,
|
||||
'weight': self.weight.weight + 1,
|
||||
'date': self.localdate_string()
|
||||
"child": self.weight.child.id,
|
||||
"weight": self.weight.weight + 1,
|
||||
"date": self.localdate_string(),
|
||||
}
|
||||
page = self.c.post('/weight/{}/'.format(self.weight.id),
|
||||
params, follow=True)
|
||||
page = self.c.post("/weight/{}/".format(self.weight.id), params, follow=True)
|
||||
self.assertEqual(page.status_code, 200)
|
||||
self.weight.refresh_from_db()
|
||||
self.assertEqual(self.weight.weight, params['weight'])
|
||||
self.assertEqual(self.weight.weight, params["weight"])
|
||||
self.assertContains(
|
||||
page,
|
||||
'Weight entry for {} updated'.format(str(self.weight.child))
|
||||
page, "Weight entry for {} updated".format(str(self.weight.child))
|
||||
)
|
||||
|
||||
def test_delete(self):
|
||||
page = self.c.post('/weight/{}/delete/'.format(self.weight.id),
|
||||
follow=True)
|
||||
page = self.c.post("/weight/{}/delete/".format(self.weight.id), follow=True)
|
||||
self.assertEqual(page.status_code, 200)
|
||||
self.assertContains(page, 'Weight entry deleted')
|
||||
self.assertContains(page, "Weight entry deleted")
|
||||
|
|
|
@ -11,25 +11,27 @@ from core import admin, models
|
|||
|
||||
|
||||
class ImportTestCase(TestCase):
|
||||
base_path = os.path.dirname(__file__) + '/import/'
|
||||
admin_module = importlib.import_module('core.admin')
|
||||
model_module = importlib.import_module('core.models')
|
||||
base_path = os.path.dirname(__file__) + "/import/"
|
||||
admin_module = importlib.import_module("core.admin")
|
||||
model_module = importlib.import_module("core.models")
|
||||
|
||||
def setUp(self):
|
||||
call_command('migrate', verbosity=0)
|
||||
call_command("migrate", verbosity=0)
|
||||
# The data to be imported uses 2020-02-10 as a basis and Child ID 1.
|
||||
birth_date = datetime.date(year=2020, month=2, day=10)
|
||||
models.Child.objects.create(
|
||||
first_name='Child', last_name='One', birth_date=birth_date).save()
|
||||
first_name="Child", last_name="One", birth_date=birth_date
|
||||
).save()
|
||||
|
||||
def get_dataset(self, model_name):
|
||||
file = open(self.base_path + model_name + '.csv')
|
||||
file = open(self.base_path + model_name + ".csv")
|
||||
return tablib.Dataset().load(file.read())
|
||||
|
||||
def import_data(self, model, count):
|
||||
dataset = self.get_dataset(model._meta.model_name)
|
||||
resource_class = getattr(
|
||||
self.admin_module, model.__name__ + 'ImportExportResource')
|
||||
self.admin_module, model.__name__ + "ImportExportResource"
|
||||
)
|
||||
resource = resource_class()
|
||||
result = resource.import_data(dataset, dry_run=False)
|
||||
self.assertFalse(result.has_validation_errors())
|
||||
|
@ -61,7 +63,7 @@ class ImportTestCase(TestCase):
|
|||
self.import_data(models.Weight, 5)
|
||||
|
||||
def test_invalid_child(self):
|
||||
dataset = self.get_dataset('diaperchange-invalid-child')
|
||||
dataset = self.get_dataset("diaperchange-invalid-child")
|
||||
resource = admin.DiaperChangeImportExportResource()
|
||||
result = resource.import_data(dataset, dry_run=False)
|
||||
self.assertTrue(result.has_validation_errors())
|
||||
|
|
|
@ -9,32 +9,26 @@ from core import models
|
|||
|
||||
class ChildTestCase(TestCase):
|
||||
def setUp(self):
|
||||
call_command('migrate', verbosity=0)
|
||||
call_command("migrate", verbosity=0)
|
||||
|
||||
def test_child_create(self):
|
||||
child = models.Child.objects.create(
|
||||
first_name='First',
|
||||
last_name='Last',
|
||||
birth_date=timezone.localdate()
|
||||
first_name="First", last_name="Last", birth_date=timezone.localdate()
|
||||
)
|
||||
self.assertEqual(child, models.Child.objects.get(first_name='First'))
|
||||
self.assertEqual(child.slug, 'first-last')
|
||||
self.assertEqual(str(child), 'First Last')
|
||||
self.assertEqual(child.name(), 'First Last')
|
||||
self.assertEqual(child.name(reverse=True), 'Last, First')
|
||||
self.assertEqual(child, models.Child.objects.get(first_name="First"))
|
||||
self.assertEqual(child.slug, "first-last")
|
||||
self.assertEqual(str(child), "First Last")
|
||||
self.assertEqual(child.name(), "First Last")
|
||||
self.assertEqual(child.name(reverse=True), "Last, First")
|
||||
|
||||
def test_child_count(self):
|
||||
self.assertEqual(models.Child.count(), 0)
|
||||
models.Child.objects.create(
|
||||
first_name='First 1',
|
||||
last_name='Last 1',
|
||||
birth_date=timezone.localdate()
|
||||
first_name="First 1", last_name="Last 1", birth_date=timezone.localdate()
|
||||
)
|
||||
self.assertEqual(models.Child.count(), 1)
|
||||
child = models.Child.objects.create(
|
||||
first_name='First 2',
|
||||
last_name='Last 2',
|
||||
birth_date=timezone.localdate()
|
||||
first_name="First 2", last_name="Last 2", birth_date=timezone.localdate()
|
||||
)
|
||||
self.assertEqual(models.Child.count(), 2)
|
||||
child.delete()
|
||||
|
@ -43,42 +37,37 @@ class ChildTestCase(TestCase):
|
|||
|
||||
class DiaperChangeTestCase(TestCase):
|
||||
def setUp(self):
|
||||
call_command('migrate', verbosity=0)
|
||||
call_command("migrate", verbosity=0)
|
||||
self.child = models.Child.objects.create(
|
||||
first_name='First',
|
||||
last_name='Last',
|
||||
birth_date=timezone.localdate()
|
||||
first_name="First", last_name="Last", birth_date=timezone.localdate()
|
||||
)
|
||||
self.change = models.DiaperChange.objects.create(
|
||||
child=self.child,
|
||||
time=timezone.localtime() - timezone.timedelta(days=1),
|
||||
wet=1,
|
||||
solid=1,
|
||||
color='black',
|
||||
amount=1.25
|
||||
color="black",
|
||||
amount=1.25,
|
||||
)
|
||||
|
||||
def test_diaperchange_create(self):
|
||||
self.assertEqual(self.change, models.DiaperChange.objects.first())
|
||||
self.assertEqual(str(self.change), 'Diaper Change')
|
||||
self.assertEqual(str(self.change), "Diaper Change")
|
||||
self.assertEqual(self.change.child, self.child)
|
||||
self.assertTrue(self.change.wet)
|
||||
self.assertTrue(self.change.solid)
|
||||
self.assertEqual(self.change.color, 'black')
|
||||
self.assertEqual(self.change.color, "black")
|
||||
self.assertEqual(self.change.amount, 1.25)
|
||||
|
||||
def test_diaperchange_attributes(self):
|
||||
self.assertListEqual(
|
||||
self.change.attributes(), ['Wet', 'Solid', 'Black'])
|
||||
self.assertListEqual(self.change.attributes(), ["Wet", "Solid", "Black"])
|
||||
|
||||
|
||||
class FeedingTestCase(TestCase):
|
||||
def setUp(self):
|
||||
call_command('migrate', verbosity=0)
|
||||
call_command("migrate", verbosity=0)
|
||||
self.child = models.Child.objects.create(
|
||||
first_name='First',
|
||||
last_name='Last',
|
||||
birth_date=timezone.localdate()
|
||||
first_name="First", last_name="Last", birth_date=timezone.localdate()
|
||||
)
|
||||
|
||||
def test_feeding_create(self):
|
||||
|
@ -86,12 +75,12 @@ class FeedingTestCase(TestCase):
|
|||
child=self.child,
|
||||
start=timezone.localtime() - timezone.timedelta(minutes=30),
|
||||
end=timezone.localtime(),
|
||||
type='formula',
|
||||
method='bottle',
|
||||
amount=2
|
||||
type="formula",
|
||||
method="bottle",
|
||||
amount=2,
|
||||
)
|
||||
self.assertEqual(feeding, models.Feeding.objects.first())
|
||||
self.assertEqual(str(feeding), 'Feeding')
|
||||
self.assertEqual(str(feeding), "Feeding")
|
||||
self.assertEqual(feeding.duration, feeding.end - feeding.start)
|
||||
|
||||
def test_method_both_breasts(self):
|
||||
|
@ -99,37 +88,34 @@ class FeedingTestCase(TestCase):
|
|||
child=self.child,
|
||||
start=timezone.localtime() - timezone.timedelta(minutes=30),
|
||||
end=timezone.localtime(),
|
||||
type='breast milk',
|
||||
method='both breasts'
|
||||
type="breast milk",
|
||||
method="both breasts",
|
||||
)
|
||||
self.assertEqual(feeding, models.Feeding.objects.first())
|
||||
self.assertEqual(str(feeding), 'Feeding')
|
||||
self.assertEqual(feeding.method, 'both breasts')
|
||||
self.assertEqual(str(feeding), "Feeding")
|
||||
self.assertEqual(feeding.method, "both breasts")
|
||||
|
||||
|
||||
class NoteTestCase(TestCase):
|
||||
def setUp(self):
|
||||
call_command('migrate', verbosity=0)
|
||||
call_command("migrate", verbosity=0)
|
||||
self.child = models.Child.objects.create(
|
||||
first_name='First',
|
||||
last_name='Last',
|
||||
birth_date=timezone.localdate()
|
||||
first_name="First", last_name="Last", birth_date=timezone.localdate()
|
||||
)
|
||||
|
||||
def test_note_create(self):
|
||||
note = models.Note.objects.create(
|
||||
child=self.child, note='Note', time=timezone.localtime())
|
||||
child=self.child, note="Note", time=timezone.localtime()
|
||||
)
|
||||
self.assertEqual(note, models.Note.objects.first())
|
||||
self.assertEqual(str(note), 'Note')
|
||||
self.assertEqual(str(note), "Note")
|
||||
|
||||
|
||||
class SleepTestCase(TestCase):
|
||||
def setUp(self):
|
||||
call_command('migrate', verbosity=0)
|
||||
call_command("migrate", verbosity=0)
|
||||
self.child = models.Child.objects.create(
|
||||
first_name='First',
|
||||
last_name='Last',
|
||||
birth_date=timezone.localdate()
|
||||
first_name="First", last_name="Last", birth_date=timezone.localdate()
|
||||
)
|
||||
|
||||
def test_sleep_create(self):
|
||||
|
@ -139,74 +125,63 @@ class SleepTestCase(TestCase):
|
|||
end=timezone.localtime(),
|
||||
)
|
||||
self.assertEqual(sleep, models.Sleep.objects.first())
|
||||
self.assertEqual(str(sleep), 'Sleep')
|
||||
self.assertEqual(str(sleep), "Sleep")
|
||||
self.assertEqual(sleep.duration, sleep.end - sleep.start)
|
||||
|
||||
|
||||
class TemperatureTestCase(TestCase):
|
||||
def setUp(self):
|
||||
call_command('migrate', verbosity=0)
|
||||
call_command("migrate", verbosity=0)
|
||||
self.child = models.Child.objects.create(
|
||||
first_name='First',
|
||||
last_name='Last',
|
||||
birth_date=timezone.localdate()
|
||||
first_name="First", last_name="Last", birth_date=timezone.localdate()
|
||||
)
|
||||
self.temp = models.Temperature.objects.create(
|
||||
child=self.child,
|
||||
time=timezone.localtime() - timezone.timedelta(days=1),
|
||||
temperature=98.6
|
||||
temperature=98.6,
|
||||
)
|
||||
|
||||
def test_temperature_create(self):
|
||||
self.assertEqual(self.temp, models.Temperature.objects.first())
|
||||
self.assertEqual(str(self.temp), 'Temperature')
|
||||
self.assertEqual(str(self.temp), "Temperature")
|
||||
self.assertEqual(self.temp.temperature, 98.6)
|
||||
|
||||
|
||||
class TimerTestCase(TestCase):
|
||||
def setUp(self):
|
||||
call_command('migrate', verbosity=0)
|
||||
call_command("migrate", verbosity=0)
|
||||
child = models.Child.objects.create(
|
||||
first_name='First',
|
||||
last_name='Last',
|
||||
birth_date=timezone.localdate()
|
||||
first_name="First", last_name="Last", birth_date=timezone.localdate()
|
||||
)
|
||||
self.user = User.objects.first()
|
||||
self.named = models.Timer.objects.create(
|
||||
name='Named',
|
||||
end=timezone.localtime(),
|
||||
user=self.user,
|
||||
child=child
|
||||
name="Named", end=timezone.localtime(), user=self.user, child=child
|
||||
)
|
||||
self.unnamed = models.Timer.objects.create(
|
||||
end=timezone.localtime(),
|
||||
user=self.user
|
||||
end=timezone.localtime(), user=self.user
|
||||
)
|
||||
|
||||
def test_timer_create(self):
|
||||
self.assertEqual(self.named, models.Timer.objects.get(name='Named'))
|
||||
self.assertEqual(str(self.named), 'Named')
|
||||
self.assertEqual(self.named, models.Timer.objects.get(name="Named"))
|
||||
self.assertEqual(str(self.named), "Named")
|
||||
self.assertEqual(self.unnamed, models.Timer.objects.get(name=None))
|
||||
self.assertEqual(
|
||||
str(self.unnamed), 'Timer #{}'.format(self.unnamed.id))
|
||||
self.assertEqual(str(self.unnamed), "Timer #{}".format(self.unnamed.id))
|
||||
|
||||
def test_timer_title_with_child(self):
|
||||
self.assertEqual(self.named.title_with_child, str(self.named))
|
||||
|
||||
models.Child.objects.create(
|
||||
first_name='Child',
|
||||
last_name='Two',
|
||||
birth_date=timezone.localdate()
|
||||
first_name="Child", last_name="Two", birth_date=timezone.localdate()
|
||||
)
|
||||
self.assertEqual(
|
||||
self.named.title_with_child,
|
||||
'{} ({})'.format(str(self.named), str(self.named.child))
|
||||
"{} ({})".format(str(self.named), str(self.named.child)),
|
||||
)
|
||||
|
||||
def test_timer_user_username(self):
|
||||
self.assertEqual(self.named.user_username, self.user.get_username())
|
||||
self.user.first_name = 'User'
|
||||
self.user.last_name = 'Name'
|
||||
self.user.first_name = "User"
|
||||
self.user.last_name = "Name"
|
||||
self.user.save()
|
||||
self.assertEqual(self.named.user_username, self.user.get_full_name())
|
||||
|
||||
|
@ -222,7 +197,8 @@ class TimerTestCase(TestCase):
|
|||
self.assertEqual(self.unnamed.end, stop_time)
|
||||
self.assertEqual(
|
||||
self.unnamed.duration.seconds,
|
||||
(self.unnamed.end - self.unnamed.start).seconds)
|
||||
(self.unnamed.end - self.unnamed.start).seconds,
|
||||
)
|
||||
self.assertFalse(self.unnamed.active)
|
||||
|
||||
def test_timer_duration(self):
|
||||
|
@ -232,22 +208,16 @@ class TimerTestCase(TestCase):
|
|||
timer.save()
|
||||
timer.refresh_from_db()
|
||||
|
||||
self.assertEqual(
|
||||
timer.duration.seconds,
|
||||
timezone.timedelta(minutes=30).seconds)
|
||||
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):
|
||||
def setUp(self):
|
||||
call_command('migrate', verbosity=0)
|
||||
call_command("migrate", verbosity=0)
|
||||
self.child = models.Child.objects.create(
|
||||
first_name='First',
|
||||
last_name='Last',
|
||||
birth_date=timezone.localdate()
|
||||
first_name="First", last_name="Last", birth_date=timezone.localdate()
|
||||
)
|
||||
|
||||
def test_tummytime_create(self):
|
||||
|
@ -257,6 +227,5 @@ class TummyTimeTestCase(TestCase):
|
|||
end=timezone.localtime(),
|
||||
)
|
||||
self.assertEqual(tummy_time, models.TummyTime.objects.first())
|
||||
self.assertEqual(str(tummy_time), 'Tummy Time')
|
||||
self.assertEqual(
|
||||
tummy_time.duration, tummy_time.end - tummy_time.start)
|
||||
self.assertEqual(str(tummy_time), "Tummy Time")
|
||||
self.assertEqual(tummy_time.duration, tummy_time.end - tummy_time.start)
|
||||
|
|
|
@ -16,113 +16,119 @@ class TemplateTagsTestCase(TestCase):
|
|||
def test_bootstrap_bool_icon(self):
|
||||
self.assertEqual(
|
||||
bootstrap.bool_icon(True),
|
||||
'<i class="icon-true text-success" aria-hidden="true"></i>')
|
||||
'<i class="icon-true text-success" aria-hidden="true"></i>',
|
||||
)
|
||||
self.assertEqual(
|
||||
bootstrap.bool_icon(False),
|
||||
'<i class="icon-false text-danger" aria-hidden="true"></i>')
|
||||
'<i class="icon-false text-danger" aria-hidden="true"></i>',
|
||||
)
|
||||
|
||||
def test_child_age_string(self):
|
||||
date = timezone.localdate() - timezone.timedelta(days=0, hours=6)
|
||||
self.assertEqual('0 days', duration.child_age_string(date))
|
||||
self.assertEqual("0 days", duration.child_age_string(date))
|
||||
date = timezone.localdate() - timezone.timedelta(days=1, hours=6)
|
||||
self.assertEqual('1\xa0day', duration.child_age_string(date))
|
||||
self.assertEqual("1\xa0day", duration.child_age_string(date))
|
||||
date = timezone.localdate() - timezone.timedelta(days=45)
|
||||
self.assertEqual('1\xa0month', duration.child_age_string(date))
|
||||
self.assertEqual("1\xa0month", duration.child_age_string(date))
|
||||
date = timezone.localdate() - timezone.timedelta(days=95)
|
||||
self.assertEqual('3\xa0months', duration.child_age_string(date))
|
||||
self.assertEqual("3\xa0months", duration.child_age_string(date))
|
||||
|
||||
def test_duration_duration_string(self):
|
||||
delta = timezone.timedelta(hours=1, minutes=30, seconds=15)
|
||||
self.assertEqual(
|
||||
duration.duration_string(delta),
|
||||
'1 hour, 30 minutes, 15 seconds')
|
||||
self.assertEqual(
|
||||
duration.duration_string(delta, 'm'),
|
||||
'1 hour, 30 minutes')
|
||||
self.assertEqual(duration.duration_string(delta, 'h'), '1 hour')
|
||||
duration.duration_string(delta), "1 hour, 30 minutes, 15 seconds"
|
||||
)
|
||||
self.assertEqual(duration.duration_string(delta, "m"), "1 hour, 30 minutes")
|
||||
self.assertEqual(duration.duration_string(delta, "h"), "1 hour")
|
||||
|
||||
self.assertEqual(duration.duration_string(''), '')
|
||||
self.assertRaises(TypeError, duration.duration_string('not a delta'))
|
||||
self.assertEqual(duration.duration_string(""), "")
|
||||
self.assertRaises(TypeError, duration.duration_string("not a delta"))
|
||||
|
||||
def test_duration_hours(self):
|
||||
delta = timezone.timedelta(hours=1)
|
||||
self.assertEqual(duration.hours(delta), 1)
|
||||
self.assertEqual(duration.hours(''), 0)
|
||||
self.assertRaises(TypeError, duration.hours('not a delta'))
|
||||
self.assertEqual(duration.hours(""), 0)
|
||||
self.assertRaises(TypeError, duration.hours("not a delta"))
|
||||
|
||||
def test_duration_minutes(self):
|
||||
delta = timezone.timedelta(minutes=45)
|
||||
self.assertEqual(duration.minutes(delta), 45)
|
||||
self.assertEqual(duration.minutes(''), 0)
|
||||
self.assertRaises(TypeError, duration.minutes('not a delta'))
|
||||
self.assertEqual(duration.minutes(""), 0)
|
||||
self.assertRaises(TypeError, duration.minutes("not a delta"))
|
||||
|
||||
def test_duration_seconds(self):
|
||||
delta = timezone.timedelta(seconds=20)
|
||||
self.assertEqual(duration.seconds(delta), 20)
|
||||
self.assertEqual(duration.seconds(''), 0)
|
||||
self.assertRaises(TypeError, duration.seconds('not a delta'))
|
||||
self.assertEqual(duration.seconds(""), 0)
|
||||
self.assertRaises(TypeError, duration.seconds("not a delta"))
|
||||
|
||||
def test_instance_add_url(self):
|
||||
child = Child.objects.create(first_name='Test', last_name='Child',
|
||||
birth_date=timezone.localdate())
|
||||
user = User.objects.create_user(username='timer')
|
||||
child = Child.objects.create(
|
||||
first_name="Test", last_name="Child", birth_date=timezone.localdate()
|
||||
)
|
||||
user = User.objects.create_user(username="timer")
|
||||
timer = Timer.objects.create(user=user)
|
||||
|
||||
url = timers.instance_add_url({'timer': timer}, 'core:sleep-add')
|
||||
self.assertEqual(url, '/sleep/add/?timer={}'.format(timer.id))
|
||||
url = timers.instance_add_url({"timer": timer}, "core:sleep-add")
|
||||
self.assertEqual(url, "/sleep/add/?timer={}".format(timer.id))
|
||||
|
||||
timer = Timer.objects.create(user=user, child=child)
|
||||
url = timers.instance_add_url({'timer': timer}, 'core:sleep-add')
|
||||
self.assertEqual(url, '/sleep/add/?timer={}&child={}'.format(
|
||||
timer.id, child.slug))
|
||||
url = timers.instance_add_url({"timer": timer}, "core:sleep-add")
|
||||
self.assertEqual(
|
||||
url, "/sleep/add/?timer={}&child={}".format(timer.id, child.slug)
|
||||
)
|
||||
|
||||
def test_datetimepicker_format(self):
|
||||
request = MockUserRequest(User.objects.first())
|
||||
request.user.settings.dashboard_hide_empty = True
|
||||
context = {'request': request}
|
||||
context = {"request": request}
|
||||
|
||||
with self.settings(USE_24_HOUR_TIME_FORMAT=False):
|
||||
self.assertEqual(datetime.datetimepicker_format(context), 'L LT')
|
||||
self.assertEqual(
|
||||
datetime.datetimepicker_format(context, 'L LT'), 'L LT')
|
||||
self.assertEqual(
|
||||
datetime.datetimepicker_format(context, 'L LTS'), 'L LTS')
|
||||
self.assertEqual(datetime.datetimepicker_format(context), "L LT")
|
||||
self.assertEqual(datetime.datetimepicker_format(context, "L LT"), "L LT")
|
||||
self.assertEqual(datetime.datetimepicker_format(context, "L LTS"), "L LTS")
|
||||
|
||||
with self.settings(USE_24_HOUR_TIME_FORMAT=True):
|
||||
self.assertEqual(datetime.datetimepicker_format(context), "L HH:mm")
|
||||
self.assertEqual(datetime.datetimepicker_format(context, "L LT"), "L HH:mm")
|
||||
self.assertEqual(
|
||||
datetime.datetimepicker_format(context), 'L HH:mm')
|
||||
self.assertEqual(
|
||||
datetime.datetimepicker_format(context, 'L LT'), 'L HH:mm')
|
||||
self.assertEqual(
|
||||
datetime.datetimepicker_format(context, 'L LTS'), 'L HH:mm:ss')
|
||||
datetime.datetimepicker_format(context, "L LTS"), "L HH:mm:ss"
|
||||
)
|
||||
|
||||
@override_settings(USE_24_HOUR_TIME_FORMAT=False)
|
||||
def test_datetimepicker_format_en_gb(self):
|
||||
user = User.objects.first()
|
||||
user.settings.language = 'en-GB'
|
||||
user.settings.language = "en-GB"
|
||||
user.save()
|
||||
|
||||
request = MockUserRequest(user)
|
||||
request.user.settings.dashboard_hide_empty = True
|
||||
context = {'request': request}
|
||||
context = {"request": request}
|
||||
|
||||
self.assertEqual(datetime.datetimepicker_format(context), "L h:mm a")
|
||||
self.assertEqual(datetime.datetimepicker_format(context, "L LT"), "L h:mm a")
|
||||
self.assertEqual(
|
||||
datetime.datetimepicker_format(context), 'L h:mm a')
|
||||
self.assertEqual(
|
||||
datetime.datetimepicker_format(context, 'L LT'), 'L h:mm a')
|
||||
self.assertEqual(
|
||||
datetime.datetimepicker_format(context, 'L LTS'), 'L h:mm:ss a')
|
||||
datetime.datetimepicker_format(context, "L LTS"), "L h:mm:ss a"
|
||||
)
|
||||
|
||||
def test_datetime_short(self):
|
||||
date = timezone.localtime()
|
||||
self.assertEqual(datetime.datetime_short(date), "Today, {}".format(
|
||||
formats.date_format(date, format='TIME_FORMAT')))
|
||||
self.assertEqual(
|
||||
datetime.datetime_short(date),
|
||||
"Today, {}".format(formats.date_format(date, format="TIME_FORMAT")),
|
||||
)
|
||||
|
||||
date = timezone.localtime() - timezone.timedelta(days=1, hours=6)
|
||||
self.assertEqual(datetime.datetime_short(date), "{}, {}".format(
|
||||
formats.date_format(date, format='SHORT_MONTH_DAY_FORMAT'),
|
||||
formats.date_format(date, format='TIME_FORMAT')))
|
||||
self.assertEqual(
|
||||
datetime.datetime_short(date),
|
||||
"{}, {}".format(
|
||||
formats.date_format(date, format="SHORT_MONTH_DAY_FORMAT"),
|
||||
formats.date_format(date, format="TIME_FORMAT"),
|
||||
),
|
||||
)
|
||||
|
||||
date = timezone.localtime() - timezone.timedelta(days=500)
|
||||
self.assertEqual(datetime.datetime_short(date), formats.date_format(
|
||||
date, format='SHORT_DATETIME_FORMAT'))
|
||||
self.assertEqual(
|
||||
datetime.datetime_short(date),
|
||||
formats.date_format(date, format="SHORT_DATETIME_FORMAT"),
|
||||
)
|
||||
|
|
|
@ -8,14 +8,12 @@ from core.utils import duration_string, duration_parts
|
|||
class UtilsTestCase(TestCase):
|
||||
def test_duration_string(self):
|
||||
duration = timezone.timedelta(hours=1, minutes=30, seconds=45)
|
||||
self.assertEqual(
|
||||
duration_string(duration),
|
||||
'1 hour, 30 minutes, 45 seconds')
|
||||
self.assertEqual(duration_string(duration, 'm'), '1 hour, 30 minutes')
|
||||
self.assertEqual(duration_string(duration, 'h'), '1 hour')
|
||||
self.assertRaises(TypeError, lambda: duration_string('1 hour'))
|
||||
self.assertEqual(duration_string(duration), "1 hour, 30 minutes, 45 seconds")
|
||||
self.assertEqual(duration_string(duration, "m"), "1 hour, 30 minutes")
|
||||
self.assertEqual(duration_string(duration, "h"), "1 hour")
|
||||
self.assertRaises(TypeError, lambda: duration_string("1 hour"))
|
||||
|
||||
def test_duration_parts(self):
|
||||
duration = timezone.timedelta(hours=1, minutes=30, seconds=45)
|
||||
self.assertEqual(duration_parts(duration), (1, 30, 45))
|
||||
self.assertRaises(TypeError, lambda: duration_parts('1 hour'))
|
||||
self.assertRaises(TypeError, lambda: duration_parts("1 hour"))
|
||||
|
|
|
@ -15,174 +15,172 @@ class ViewsTestCase(TestCase):
|
|||
def setUpClass(cls):
|
||||
super(ViewsTestCase, cls).setUpClass()
|
||||
fake = Factory.create()
|
||||
call_command('migrate', verbosity=0)
|
||||
call_command('fake', verbosity=0)
|
||||
call_command("migrate", verbosity=0)
|
||||
call_command("fake", verbosity=0)
|
||||
|
||||
cls.c = HttpClient()
|
||||
|
||||
fake_user = fake.simple_profile()
|
||||
cls.credentials = {
|
||||
'username': fake_user['username'],
|
||||
'password': fake.password()
|
||||
"username": fake_user["username"],
|
||||
"password": fake.password(),
|
||||
}
|
||||
cls.user = User.objects.create_user(
|
||||
is_superuser=True, **cls.credentials)
|
||||
cls.user = User.objects.create_user(is_superuser=True, **cls.credentials)
|
||||
|
||||
cls.c.login(**cls.credentials)
|
||||
|
||||
def test_child_views(self):
|
||||
page = self.c.get('/children/')
|
||||
page = self.c.get("/children/")
|
||||
self.assertEqual(page.status_code, 200)
|
||||
page = self.c.get('/children/add/')
|
||||
page = self.c.get("/children/add/")
|
||||
self.assertEqual(page.status_code, 200)
|
||||
|
||||
entry = models.Child.objects.first()
|
||||
page = self.c.get('/children/{}/'.format(entry.slug))
|
||||
page = self.c.get("/children/{}/".format(entry.slug))
|
||||
self.assertEqual(page.status_code, 200)
|
||||
page = self.c.get(
|
||||
'/children/{}/'.format(entry.slug),
|
||||
{'date': timezone.localdate() - timezone.timedelta(days=1)})
|
||||
"/children/{}/".format(entry.slug),
|
||||
{"date": timezone.localdate() - timezone.timedelta(days=1)},
|
||||
)
|
||||
self.assertEqual(page.status_code, 200)
|
||||
|
||||
page = self.c.get('/children/{}/edit/'.format(entry.slug))
|
||||
page = self.c.get("/children/{}/edit/".format(entry.slug))
|
||||
self.assertEqual(page.status_code, 200)
|
||||
page = self.c.get('/children/{}/delete/'.format(entry.slug))
|
||||
page = self.c.get("/children/{}/delete/".format(entry.slug))
|
||||
self.assertEqual(page.status_code, 200)
|
||||
|
||||
def test_diaperchange_views(self):
|
||||
page = self.c.get('/changes/')
|
||||
page = self.c.get("/changes/")
|
||||
self.assertEqual(page.status_code, 200)
|
||||
page = self.c.get('/changes/add/')
|
||||
page = self.c.get("/changes/add/")
|
||||
self.assertEqual(page.status_code, 200)
|
||||
|
||||
entry = models.DiaperChange.objects.first()
|
||||
page = self.c.get('/changes/{}/'.format(entry.id))
|
||||
page = self.c.get("/changes/{}/".format(entry.id))
|
||||
self.assertEqual(page.status_code, 200)
|
||||
page = self.c.get('/changes/{}/delete/'.format(entry.id))
|
||||
page = self.c.get("/changes/{}/delete/".format(entry.id))
|
||||
self.assertEqual(page.status_code, 200)
|
||||
|
||||
def test_feeding_views(self):
|
||||
page = self.c.get('/feedings/')
|
||||
page = self.c.get("/feedings/")
|
||||
self.assertEqual(page.status_code, 200)
|
||||
page = self.c.get('/feedings/add/')
|
||||
page = self.c.get("/feedings/add/")
|
||||
self.assertEqual(page.status_code, 200)
|
||||
|
||||
entry = models.Feeding.objects.first()
|
||||
page = self.c.get('/feedings/{}/'.format(entry.id))
|
||||
page = self.c.get("/feedings/{}/".format(entry.id))
|
||||
self.assertEqual(page.status_code, 200)
|
||||
page = self.c.get('/feedings/{}/delete/'.format(entry.id))
|
||||
page = self.c.get("/feedings/{}/delete/".format(entry.id))
|
||||
self.assertEqual(page.status_code, 200)
|
||||
|
||||
def test_note_views(self):
|
||||
page = self.c.get('/notes/')
|
||||
page = self.c.get("/notes/")
|
||||
self.assertEqual(page.status_code, 200)
|
||||
page = self.c.get('/notes/add/')
|
||||
page = self.c.get("/notes/add/")
|
||||
self.assertEqual(page.status_code, 200)
|
||||
|
||||
entry = models.Note.objects.first()
|
||||
page = self.c.get('/notes/{}/'.format(entry.id))
|
||||
page = self.c.get("/notes/{}/".format(entry.id))
|
||||
self.assertEqual(page.status_code, 200)
|
||||
page = self.c.get('/notes/{}/delete/'.format(entry.id))
|
||||
page = self.c.get("/notes/{}/delete/".format(entry.id))
|
||||
self.assertEqual(page.status_code, 200)
|
||||
|
||||
def test_sleep_views(self):
|
||||
page = self.c.get('/sleep/')
|
||||
page = self.c.get("/sleep/")
|
||||
self.assertEqual(page.status_code, 200)
|
||||
page = self.c.get('/sleep/add/')
|
||||
page = self.c.get("/sleep/add/")
|
||||
self.assertEqual(page.status_code, 200)
|
||||
|
||||
entry = models.Sleep.objects.first()
|
||||
page = self.c.get('/sleep/{}/'.format(entry.id))
|
||||
page = self.c.get("/sleep/{}/".format(entry.id))
|
||||
self.assertEqual(page.status_code, 200)
|
||||
page = self.c.get('/sleep/{}/delete/'.format(entry.id))
|
||||
page = self.c.get("/sleep/{}/delete/".format(entry.id))
|
||||
self.assertEqual(page.status_code, 200)
|
||||
|
||||
def test_temperature_views(self):
|
||||
page = self.c.get('/temperature/')
|
||||
page = self.c.get("/temperature/")
|
||||
self.assertEqual(page.status_code, 200)
|
||||
page = self.c.get('/temperature/add/')
|
||||
page = self.c.get("/temperature/add/")
|
||||
self.assertEqual(page.status_code, 200)
|
||||
|
||||
entry = models.Temperature.objects.first()
|
||||
page = self.c.get('/temperature/{}/'.format(entry.id))
|
||||
page = self.c.get("/temperature/{}/".format(entry.id))
|
||||
self.assertEqual(page.status_code, 200)
|
||||
page = self.c.get('/temperature/{}/delete/'.format(entry.id))
|
||||
page = self.c.get("/temperature/{}/delete/".format(entry.id))
|
||||
self.assertEqual(page.status_code, 200)
|
||||
|
||||
def test_timer_views(self):
|
||||
page = self.c.get('/timers/')
|
||||
page = self.c.get("/timers/")
|
||||
self.assertEqual(page.status_code, 200)
|
||||
page = self.c.get('/timers/add/')
|
||||
page = self.c.get("/timers/add/")
|
||||
self.assertEqual(page.status_code, 200)
|
||||
|
||||
page = self.c.get('/timers/add/quick/')
|
||||
page = self.c.get("/timers/add/quick/")
|
||||
self.assertEqual(page.status_code, 405)
|
||||
page = self.c.post('/timers/add/quick/', follow=True)
|
||||
page = self.c.post("/timers/add/quick/", follow=True)
|
||||
self.assertEqual(page.status_code, 200)
|
||||
|
||||
entry = models.Timer.objects.first()
|
||||
page = self.c.get('/timers/{}/'.format(entry.id))
|
||||
page = self.c.get("/timers/{}/".format(entry.id))
|
||||
self.assertEqual(page.status_code, 200)
|
||||
page = self.c.get('/timers/{}/edit/'.format(entry.id))
|
||||
page = self.c.get("/timers/{}/edit/".format(entry.id))
|
||||
self.assertEqual(page.status_code, 200)
|
||||
page = self.c.get('/timers/{}/delete/'.format(entry.id))
|
||||
page = self.c.get("/timers/{}/delete/".format(entry.id))
|
||||
self.assertEqual(page.status_code, 200)
|
||||
|
||||
page = self.c.get('/timers/{}/stop/'.format(entry.id))
|
||||
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)
|
||||
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))
|
||||
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)
|
||||
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)
|
||||
page = self.c.get("/timers/delete-inactive/", follow=True)
|
||||
self.assertEqual(page.status_code, 200)
|
||||
messages = list(page.context['messages'])
|
||||
messages = list(page.context["messages"])
|
||||
self.assertEqual(len(messages), 1)
|
||||
self.assertEqual(str(messages[0]), 'No inactive timers exist.')
|
||||
self.assertEqual(str(messages[0]), "No inactive timers exist.")
|
||||
|
||||
entry = models.Timer.objects.first()
|
||||
entry.stop()
|
||||
page = self.c.get('/timers/delete-inactive/')
|
||||
page = self.c.get("/timers/delete-inactive/")
|
||||
self.assertEqual(page.status_code, 200)
|
||||
self.assertEqual(page.context['timer_count'], 1)
|
||||
self.assertEqual(page.context["timer_count"], 1)
|
||||
|
||||
def test_timeline_views(self):
|
||||
child = models.Child.objects.first()
|
||||
response = self.c.get('/timeline/')
|
||||
self.assertRedirects(response, '/children/{}/'.format(child.slug))
|
||||
response = self.c.get("/timeline/")
|
||||
self.assertRedirects(response, "/children/{}/".format(child.slug))
|
||||
|
||||
models.Child.objects.create(
|
||||
first_name='Second',
|
||||
last_name='Child',
|
||||
birth_date='2000-01-01'
|
||||
first_name="Second", last_name="Child", birth_date="2000-01-01"
|
||||
)
|
||||
response = self.c.get('/timeline/')
|
||||
response = self.c.get("/timeline/")
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_tummytime_views(self):
|
||||
page = self.c.get('/tummy-time/')
|
||||
page = self.c.get("/tummy-time/")
|
||||
self.assertEqual(page.status_code, 200)
|
||||
page = self.c.get('/tummy-time/add/')
|
||||
page = self.c.get("/tummy-time/add/")
|
||||
self.assertEqual(page.status_code, 200)
|
||||
|
||||
entry = models.TummyTime.objects.first()
|
||||
page = self.c.get('/tummy-time/{}/'.format(entry.id))
|
||||
page = self.c.get("/tummy-time/{}/".format(entry.id))
|
||||
self.assertEqual(page.status_code, 200)
|
||||
page = self.c.get('/tummy-time/{}/delete/'.format(entry.id))
|
||||
page = self.c.get("/tummy-time/{}/delete/".format(entry.id))
|
||||
self.assertEqual(page.status_code, 200)
|
||||
|
||||
def test_weight_views(self):
|
||||
page = self.c.get('/weight/')
|
||||
page = self.c.get("/weight/")
|
||||
self.assertEqual(page.status_code, 200)
|
||||
page = self.c.get('/weight/add/')
|
||||
page = self.c.get("/weight/add/")
|
||||
self.assertEqual(page.status_code, 200)
|
||||
|
||||
entry = models.Weight.objects.first()
|
||||
page = self.c.get('/weight/{}/'.format(entry.id))
|
||||
page = self.c.get("/weight/{}/".format(entry.id))
|
||||
self.assertEqual(page.status_code, 200)
|
||||
page = self.c.get('/weight/{}/delete/'.format(entry.id))
|
||||
page = self.c.get("/weight/{}/delete/".format(entry.id))
|
||||
self.assertEqual(page.status_code, 200)
|
||||
|
|
219
core/timeline.py
219
core/timeline.py
|
@ -24,11 +24,11 @@ def get_objects(date, child=None):
|
|||
_add_tummy_times(min_date, max_date, events, child)
|
||||
_add_notes(min_date, max_date, events, child)
|
||||
|
||||
explicit_type_ordering = {'start': 0, 'end': 1}
|
||||
explicit_type_ordering = {"start": 0, "end": 1}
|
||||
events.sort(
|
||||
key=lambda x: (
|
||||
x['time'],
|
||||
explicit_type_ordering.get(x.get('type'), -1),
|
||||
x["time"],
|
||||
explicit_type_ordering.get(x.get("type"), -1),
|
||||
),
|
||||
reverse=True,
|
||||
)
|
||||
|
@ -37,69 +37,74 @@ def get_objects(date, child=None):
|
|||
|
||||
|
||||
def _add_tummy_times(min_date, max_date, events, child=None):
|
||||
instances = TummyTime.objects.filter(
|
||||
start__range=(min_date, max_date)).order_by('-start')
|
||||
instances = TummyTime.objects.filter(start__range=(min_date, max_date)).order_by(
|
||||
"-start"
|
||||
)
|
||||
if child:
|
||||
instances = instances.filter(child=child)
|
||||
for instance in instances:
|
||||
details = []
|
||||
if instance.milestone:
|
||||
details.append(instance.milestone)
|
||||
edit_link = reverse('core:tummytime-update', args=[instance.id])
|
||||
events.append({
|
||||
'time': timezone.localtime(instance.start),
|
||||
'event': _('%(child)s started tummy time!') % {
|
||||
'child': instance.child.first_name
|
||||
},
|
||||
'details': details,
|
||||
'edit_link': edit_link,
|
||||
'model_name': instance.model_name,
|
||||
'type': 'start'
|
||||
})
|
||||
events.append({
|
||||
'time': timezone.localtime(instance.end),
|
||||
'event': _('%(child)s finished tummy time.') % {
|
||||
'child': instance.child.first_name
|
||||
},
|
||||
'details': details,
|
||||
'edit_link': edit_link,
|
||||
'duration': timesince.timesince(instance.start, now=instance.end),
|
||||
'model_name': instance.model_name,
|
||||
'type': 'end'
|
||||
})
|
||||
edit_link = reverse("core:tummytime-update", args=[instance.id])
|
||||
events.append(
|
||||
{
|
||||
"time": timezone.localtime(instance.start),
|
||||
"event": _("%(child)s started tummy time!")
|
||||
% {"child": instance.child.first_name},
|
||||
"details": details,
|
||||
"edit_link": edit_link,
|
||||
"model_name": instance.model_name,
|
||||
"type": "start",
|
||||
}
|
||||
)
|
||||
events.append(
|
||||
{
|
||||
"time": timezone.localtime(instance.end),
|
||||
"event": _("%(child)s finished tummy time.")
|
||||
% {"child": instance.child.first_name},
|
||||
"details": details,
|
||||
"edit_link": edit_link,
|
||||
"duration": timesince.timesince(instance.start, now=instance.end),
|
||||
"model_name": instance.model_name,
|
||||
"type": "end",
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def _add_sleeps(min_date, max_date, events, child=None):
|
||||
instances = Sleep.objects.filter(
|
||||
start__range=(min_date, max_date)).order_by('-start')
|
||||
instances = Sleep.objects.filter(start__range=(min_date, max_date)).order_by(
|
||||
"-start"
|
||||
)
|
||||
if child:
|
||||
instances = instances.filter(child=child)
|
||||
for instance in instances:
|
||||
details = []
|
||||
if instance.notes:
|
||||
details.append(instance.notes)
|
||||
edit_link = reverse('core:sleep-update', args=[instance.id])
|
||||
events.append({
|
||||
'time': timezone.localtime(instance.start),
|
||||
'event': _('%(child)s fell asleep.') % {
|
||||
'child': instance.child.first_name
|
||||
},
|
||||
'details': details,
|
||||
'edit_link': edit_link,
|
||||
'model_name': instance.model_name,
|
||||
'type': 'start'
|
||||
})
|
||||
events.append({
|
||||
'time': timezone.localtime(instance.end),
|
||||
'event': _('%(child)s woke up.') % {
|
||||
'child': instance.child.first_name
|
||||
},
|
||||
'details': details,
|
||||
'edit_link': edit_link,
|
||||
'duration': timesince.timesince(instance.start, now=instance.end),
|
||||
'model_name': instance.model_name,
|
||||
'type': 'end'
|
||||
})
|
||||
edit_link = reverse("core:sleep-update", args=[instance.id])
|
||||
events.append(
|
||||
{
|
||||
"time": timezone.localtime(instance.start),
|
||||
"event": _("%(child)s fell asleep.")
|
||||
% {"child": instance.child.first_name},
|
||||
"details": details,
|
||||
"edit_link": edit_link,
|
||||
"model_name": instance.model_name,
|
||||
"type": "start",
|
||||
}
|
||||
)
|
||||
events.append(
|
||||
{
|
||||
"time": timezone.localtime(instance.end),
|
||||
"event": _("%(child)s woke up.") % {"child": instance.child.first_name},
|
||||
"details": details,
|
||||
"edit_link": edit_link,
|
||||
"duration": timesince.timesince(instance.start, now=instance.end),
|
||||
"model_name": instance.model_name,
|
||||
"type": "end",
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def _add_feedings(min_date, max_date, events, child=None):
|
||||
|
@ -107,8 +112,9 @@ def _add_feedings(min_date, max_date, events, child=None):
|
|||
yesterday = min_date - timedelta(days=1)
|
||||
prev_start = None
|
||||
|
||||
instances = Feeding.objects.filter(
|
||||
start__range=(yesterday, max_date)).order_by('start')
|
||||
instances = Feeding.objects.filter(start__range=(yesterday, max_date)).order_by(
|
||||
"start"
|
||||
)
|
||||
if child:
|
||||
instances = instances.filter(child=child)
|
||||
for instance in instances:
|
||||
|
@ -117,73 +123,80 @@ def _add_feedings(min_date, max_date, events, child=None):
|
|||
details.append(instance.notes)
|
||||
time_since_prev = None
|
||||
if prev_start:
|
||||
time_since_prev = \
|
||||
timesince.timesince(prev_start, now=instance.start)
|
||||
time_since_prev = timesince.timesince(prev_start, now=instance.start)
|
||||
prev_start = instance.start
|
||||
if instance.start < min_date:
|
||||
continue
|
||||
edit_link = reverse('core:feeding-update', args=[instance.id])
|
||||
edit_link = reverse("core:feeding-update", args=[instance.id])
|
||||
if instance.amount:
|
||||
details.append(_('Amount: %(amount).0f') % {
|
||||
'amount': instance.amount,
|
||||
})
|
||||
events.append({
|
||||
'time': timezone.localtime(instance.start),
|
||||
'event': _('%(child)s started feeding.') % {
|
||||
'child': instance.child.first_name
|
||||
},
|
||||
'details': details,
|
||||
'edit_link': edit_link,
|
||||
'time_since_prev': time_since_prev,
|
||||
'model_name': instance.model_name,
|
||||
'type': 'start'
|
||||
})
|
||||
events.append({
|
||||
'time': timezone.localtime(instance.end),
|
||||
'event': _('%(child)s finished feeding.') % {
|
||||
'child': instance.child.first_name
|
||||
},
|
||||
'details': details,
|
||||
'edit_link': edit_link,
|
||||
'duration': timesince.timesince(instance.start, now=instance.end),
|
||||
'model_name': instance.model_name,
|
||||
'type': 'end'
|
||||
})
|
||||
details.append(
|
||||
_("Amount: %(amount).0f")
|
||||
% {
|
||||
"amount": instance.amount,
|
||||
}
|
||||
)
|
||||
events.append(
|
||||
{
|
||||
"time": timezone.localtime(instance.start),
|
||||
"event": _("%(child)s started feeding.")
|
||||
% {"child": instance.child.first_name},
|
||||
"details": details,
|
||||
"edit_link": edit_link,
|
||||
"time_since_prev": time_since_prev,
|
||||
"model_name": instance.model_name,
|
||||
"type": "start",
|
||||
}
|
||||
)
|
||||
events.append(
|
||||
{
|
||||
"time": timezone.localtime(instance.end),
|
||||
"event": _("%(child)s finished feeding.")
|
||||
% {"child": instance.child.first_name},
|
||||
"details": details,
|
||||
"edit_link": edit_link,
|
||||
"duration": timesince.timesince(instance.start, now=instance.end),
|
||||
"model_name": instance.model_name,
|
||||
"type": "end",
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def _add_diaper_changes(min_date, max_date, events, child):
|
||||
instances = DiaperChange.objects.filter(
|
||||
time__range=(min_date, max_date)).order_by('-time')
|
||||
instances = DiaperChange.objects.filter(time__range=(min_date, max_date)).order_by(
|
||||
"-time"
|
||||
)
|
||||
if child:
|
||||
instances = instances.filter(child=child)
|
||||
for instance in instances:
|
||||
contents = []
|
||||
if instance.wet:
|
||||
contents.append('💧')
|
||||
contents.append("💧")
|
||||
if instance.solid:
|
||||
contents.append('💩')
|
||||
events.append({
|
||||
'time': timezone.localtime(instance.time),
|
||||
'event': _('%(child)s had a %(type)s diaper change.') % {
|
||||
'child': instance.child.first_name,
|
||||
'type': ''.join(contents),
|
||||
contents.append("💩")
|
||||
events.append(
|
||||
{
|
||||
"time": timezone.localtime(instance.time),
|
||||
"event": _("%(child)s had a %(type)s diaper change.")
|
||||
% {
|
||||
"child": instance.child.first_name,
|
||||
"type": "".join(contents),
|
||||
},
|
||||
'edit_link': reverse('core:diaperchange-update',
|
||||
args=[instance.id]),
|
||||
'model_name': instance.model_name
|
||||
})
|
||||
"edit_link": reverse("core:diaperchange-update", args=[instance.id]),
|
||||
"model_name": instance.model_name,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def _add_notes(min_date, max_date, events, child):
|
||||
instances = Note.objects.filter(
|
||||
time__range=(min_date, max_date)).order_by('-time')
|
||||
instances = Note.objects.filter(time__range=(min_date, max_date)).order_by("-time")
|
||||
if child:
|
||||
instances = instances.filter(child=child)
|
||||
for instance in instances:
|
||||
events.append({
|
||||
'time': timezone.localtime(instance.time),
|
||||
'details': [instance.note],
|
||||
'edit_link': reverse('core:note-update',
|
||||
args=[instance.id]),
|
||||
'model_name': instance.model_name
|
||||
})
|
||||
events.append(
|
||||
{
|
||||
"time": timezone.localtime(instance.time),
|
||||
"details": [instance.note],
|
||||
"edit_link": reverse("core:note-update", args=[instance.id]),
|
||||
"model_name": instance.model_name,
|
||||
}
|
||||
)
|
||||
|
|
228
core/urls.py
228
core/urls.py
|
@ -3,206 +3,112 @@ from django.urls import path
|
|||
|
||||
from . import views
|
||||
|
||||
app_name = 'core'
|
||||
app_name = "core"
|
||||
|
||||
urlpatterns = [
|
||||
path('children/', views.ChildList.as_view(), name='child-list'),
|
||||
path('children/add/', views.ChildAdd.as_view(), name='child-add'),
|
||||
path('children/<str:slug>/', views.ChildDetail.as_view(), name='child'),
|
||||
path("children/", views.ChildList.as_view(), name="child-list"),
|
||||
path("children/add/", views.ChildAdd.as_view(), name="child-add"),
|
||||
path("children/<str:slug>/", views.ChildDetail.as_view(), name="child"),
|
||||
path("children/<str:slug>/edit/", views.ChildUpdate.as_view(), name="child-update"),
|
||||
path(
|
||||
'children/<str:slug>/edit/',
|
||||
views.ChildUpdate.as_view(),
|
||||
name='child-update'
|
||||
"children/<str:slug>/delete/", views.ChildDelete.as_view(), name="child-delete"
|
||||
),
|
||||
path("timeline/", views.Timeline.as_view(), name="timeline"),
|
||||
path("changes/", views.DiaperChangeList.as_view(), name="diaperchange-list"),
|
||||
path("changes/add/", views.DiaperChangeAdd.as_view(), name="diaperchange-add"),
|
||||
path(
|
||||
'children/<str:slug>/delete/',
|
||||
views.ChildDelete.as_view(),
|
||||
name='child-delete'
|
||||
),
|
||||
|
||||
path('timeline/', views.Timeline.as_view(), name='timeline'),
|
||||
|
||||
path(
|
||||
'changes/',
|
||||
views.DiaperChangeList.as_view(),
|
||||
name='diaperchange-list'
|
||||
),
|
||||
path(
|
||||
'changes/add/',
|
||||
views.DiaperChangeAdd.as_view(),
|
||||
name='diaperchange-add'
|
||||
),
|
||||
path(
|
||||
'changes/<int:pk>/',
|
||||
"changes/<int:pk>/",
|
||||
views.DiaperChangeUpdate.as_view(),
|
||||
name='diaperchange-update'
|
||||
name="diaperchange-update",
|
||||
),
|
||||
path(
|
||||
'changes/<int:pk>/delete/',
|
||||
"changes/<int:pk>/delete/",
|
||||
views.DiaperChangeDelete.as_view(),
|
||||
name='diaperchange-delete'
|
||||
name="diaperchange-delete",
|
||||
),
|
||||
|
||||
path('feedings/', views.FeedingList.as_view(), name='feeding-list'),
|
||||
path('feedings/add/', views.FeedingAdd.as_view(), name='feeding-add'),
|
||||
path("feedings/", views.FeedingList.as_view(), name="feeding-list"),
|
||||
path("feedings/add/", views.FeedingAdd.as_view(), name="feeding-add"),
|
||||
path("feedings/<int:pk>/", views.FeedingUpdate.as_view(), name="feeding-update"),
|
||||
path(
|
||||
'feedings/<int:pk>/',
|
||||
views.FeedingUpdate.as_view(),
|
||||
name='feeding-update'
|
||||
),
|
||||
path(
|
||||
'feedings/<int:pk>/delete/',
|
||||
"feedings/<int:pk>/delete/",
|
||||
views.FeedingDelete.as_view(),
|
||||
name='feeding-delete'
|
||||
name="feeding-delete",
|
||||
),
|
||||
|
||||
path('notes/', views.NoteList.as_view(), name='note-list'),
|
||||
path('notes/add/', views.NoteAdd.as_view(), name='note-add'),
|
||||
path('notes/<int:pk>/', views.NoteUpdate.as_view(), name='note-update'),
|
||||
path("notes/", views.NoteList.as_view(), name="note-list"),
|
||||
path("notes/add/", views.NoteAdd.as_view(), name="note-add"),
|
||||
path("notes/<int:pk>/", views.NoteUpdate.as_view(), name="note-update"),
|
||||
path("notes/<int:pk>/delete/", views.NoteDelete.as_view(), name="note-delete"),
|
||||
path("sleep/", views.SleepList.as_view(), name="sleep-list"),
|
||||
path("sleep/add/", views.SleepAdd.as_view(), name="sleep-add"),
|
||||
path("sleep/<int:pk>/", views.SleepUpdate.as_view(), name="sleep-update"),
|
||||
path("sleep/<int:pk>/delete/", views.SleepDelete.as_view(), name="sleep-delete"),
|
||||
path("temperature/", views.TemperatureList.as_view(), name="temperature-list"),
|
||||
path("temperature/add/", views.TemperatureAdd.as_view(), name="temperature-add"),
|
||||
path(
|
||||
'notes/<int:pk>/delete/',
|
||||
views.NoteDelete.as_view(),
|
||||
name='note-delete'
|
||||
),
|
||||
|
||||
path('sleep/', views.SleepList.as_view(), name='sleep-list'),
|
||||
path('sleep/add/', views.SleepAdd.as_view(), name='sleep-add'),
|
||||
path('sleep/<int:pk>/', views.SleepUpdate.as_view(), name='sleep-update'),
|
||||
path(
|
||||
'sleep/<int:pk>/delete/',
|
||||
views.SleepDelete.as_view(),
|
||||
name='sleep-delete'
|
||||
),
|
||||
|
||||
path(
|
||||
'temperature/',
|
||||
views.TemperatureList.as_view(),
|
||||
name='temperature-list'
|
||||
),
|
||||
path(
|
||||
'temperature/add/',
|
||||
views.TemperatureAdd.as_view(),
|
||||
name='temperature-add'
|
||||
),
|
||||
path(
|
||||
'temperature/<int:pk>/',
|
||||
"temperature/<int:pk>/",
|
||||
views.TemperatureUpdate.as_view(),
|
||||
name='temperature-update'
|
||||
name="temperature-update",
|
||||
),
|
||||
path(
|
||||
'temperature/<int:pk>/delete/',
|
||||
"temperature/<int:pk>/delete/",
|
||||
views.TemperatureDelete.as_view(),
|
||||
name='temperature-delete'
|
||||
name="temperature-delete",
|
||||
),
|
||||
|
||||
path('timers/', views.TimerList.as_view(), name='timer-list'),
|
||||
path('timers/add/', views.TimerAdd.as_view(), name='timer-add'),
|
||||
path("timers/", views.TimerList.as_view(), name="timer-list"),
|
||||
path("timers/add/", views.TimerAdd.as_view(), name="timer-add"),
|
||||
path("timers/add/quick/", views.TimerAddQuick.as_view(), name="timer-add-quick"),
|
||||
path("timers/<int:pk>/", views.TimerDetail.as_view(), name="timer-detail"),
|
||||
path("timers/<int:pk>/edit/", views.TimerUpdate.as_view(), name="timer-update"),
|
||||
path("timers/<int:pk>/delete/", views.TimerDelete.as_view(), name="timer-delete"),
|
||||
path("timers/<int:pk>/stop/", views.TimerStop.as_view(), name="timer-stop"),
|
||||
path(
|
||||
'timers/add/quick/',
|
||||
views.TimerAddQuick.as_view(),
|
||||
name='timer-add-quick'
|
||||
),
|
||||
path('timers/<int:pk>/', views.TimerDetail.as_view(), name='timer-detail'),
|
||||
path(
|
||||
'timers/<int:pk>/edit/',
|
||||
views.TimerUpdate.as_view(),
|
||||
name='timer-update'
|
||||
"timers/<int:pk>/restart/", views.TimerRestart.as_view(), name="timer-restart"
|
||||
),
|
||||
path(
|
||||
'timers/<int:pk>/delete/',
|
||||
views.TimerDelete.as_view(),
|
||||
name='timer-delete'
|
||||
),
|
||||
path(
|
||||
'timers/<int:pk>/stop/',
|
||||
views.TimerStop.as_view(),
|
||||
name='timer-stop'
|
||||
),
|
||||
path(
|
||||
'timers/<int:pk>/restart/',
|
||||
views.TimerRestart.as_view(),
|
||||
name='timer-restart'
|
||||
),
|
||||
path(
|
||||
'timers/delete-inactive/',
|
||||
"timers/delete-inactive/",
|
||||
views.TimerDeleteInactive.as_view(),
|
||||
name='timer-delete-inactive'
|
||||
name="timer-delete-inactive",
|
||||
),
|
||||
|
||||
path('tummy-time/', views.TummyTimeList.as_view(), name='tummytime-list'),
|
||||
path("tummy-time/", views.TummyTimeList.as_view(), name="tummytime-list"),
|
||||
path("tummy-time/add/", views.TummyTimeAdd.as_view(), name="tummytime-add"),
|
||||
path(
|
||||
'tummy-time/add/',
|
||||
views.TummyTimeAdd.as_view(),
|
||||
name='tummytime-add'
|
||||
"tummy-time/<int:pk>/", views.TummyTimeUpdate.as_view(), name="tummytime-update"
|
||||
),
|
||||
path(
|
||||
'tummy-time/<int:pk>/',
|
||||
views.TummyTimeUpdate.as_view(),
|
||||
name='tummytime-update'
|
||||
),
|
||||
path(
|
||||
'tummy-time/<int:pk>/delete/',
|
||||
"tummy-time/<int:pk>/delete/",
|
||||
views.TummyTimeDelete.as_view(),
|
||||
name='tummytime-delete'
|
||||
name="tummytime-delete",
|
||||
),
|
||||
|
||||
path('weight/', views.WeightList.as_view(), name='weight-list'),
|
||||
path('weight/add/', views.WeightAdd.as_view(), name='weight-add'),
|
||||
path("weight/", views.WeightList.as_view(), name="weight-list"),
|
||||
path("weight/add/", views.WeightAdd.as_view(), name="weight-add"),
|
||||
path("weight/<int:pk>/", views.WeightUpdate.as_view(), name="weight-update"),
|
||||
path("weight/<int:pk>/delete/", views.WeightDelete.as_view(), name="weight-delete"),
|
||||
path("height/", views.HeightList.as_view(), name="height-list"),
|
||||
path("height/add/", views.HeightAdd.as_view(), name="height-add"),
|
||||
path("height/<int:pk>/", views.HeightUpdate.as_view(), name="height-update"),
|
||||
path("height/<int:pk>/delete/", views.HeightDelete.as_view(), name="height-delete"),
|
||||
path(
|
||||
'weight/<int:pk>/',
|
||||
views.WeightUpdate.as_view(),
|
||||
name='weight-update'
|
||||
),
|
||||
path(
|
||||
'weight/<int:pk>/delete/',
|
||||
views.WeightDelete.as_view(),
|
||||
name='weight-delete'
|
||||
),
|
||||
|
||||
path('height/', views.HeightList.as_view(), name='height-list'),
|
||||
path('height/add/', views.HeightAdd.as_view(), name='height-add'),
|
||||
path(
|
||||
'height/<int:pk>/',
|
||||
views.HeightUpdate.as_view(),
|
||||
name='height-update'
|
||||
),
|
||||
path(
|
||||
'height/<int:pk>/delete/',
|
||||
views.HeightDelete.as_view(),
|
||||
name='height-delete'
|
||||
),
|
||||
|
||||
path(
|
||||
'head-circumference/',
|
||||
"head-circumference/",
|
||||
views.HeadCircumferenceList.as_view(),
|
||||
name='head-circumference-list'
|
||||
name="head-circumference-list",
|
||||
),
|
||||
path(
|
||||
'head-circumference/add/',
|
||||
"head-circumference/add/",
|
||||
views.HeadCircumferenceAdd.as_view(),
|
||||
name='head-circumference-add'
|
||||
name="head-circumference-add",
|
||||
),
|
||||
path(
|
||||
'head-circumference/<int:pk>/',
|
||||
"head-circumference/<int:pk>/",
|
||||
views.HeadCircumferenceUpdate.as_view(),
|
||||
name='head-circumference-update'
|
||||
name="head-circumference-update",
|
||||
),
|
||||
path(
|
||||
'head-circumference/<int:pk>/delete/',
|
||||
"head-circumference/<int:pk>/delete/",
|
||||
views.HeadCircumferenceDelete.as_view(),
|
||||
name='head-circumference-delete'
|
||||
),
|
||||
|
||||
path('bmi/', views.BMIList.as_view(), name='bmi-list'),
|
||||
path('bmi/add/', views.BMIAdd.as_view(), name='bmi-add'),
|
||||
path(
|
||||
'bmi/<int:pk>/',
|
||||
views.BMIUpdate.as_view(),
|
||||
name='bmi-update'
|
||||
),
|
||||
path(
|
||||
'bmi/<int:pk>/delete/',
|
||||
views.BMIDelete.as_view(),
|
||||
name='bmi-delete'
|
||||
name="head-circumference-delete",
|
||||
),
|
||||
path("bmi/", views.BMIList.as_view(), name="bmi-list"),
|
||||
path("bmi/add/", views.BMIAdd.as_view(), name="bmi-add"),
|
||||
path("bmi/<int:pk>/", views.BMIUpdate.as_view(), name="bmi-update"),
|
||||
path("bmi/<int:pk>/delete/", views.BMIDelete.as_view(), name="bmi-delete"),
|
||||
]
|
||||
|
|
|
@ -3,43 +3,36 @@ from django.utils import timezone
|
|||
from django.utils.translation import ngettext
|
||||
|
||||
|
||||
def duration_string(duration, precision='s'):
|
||||
def duration_string(duration, precision="s"):
|
||||
"""Format hours, minutes and seconds as a human-friendly string (e.g. "2
|
||||
hours, 25 minutes, 31 seconds") with precision to h = hours, m = minutes or
|
||||
s = seconds.
|
||||
"""
|
||||
h, m, s = duration_parts(duration)
|
||||
|
||||
duration = ''
|
||||
duration = ""
|
||||
if h > 0:
|
||||
duration = ngettext('%(hours)s hour', '%(hours)s hours', h) % {
|
||||
'hours': h
|
||||
duration = ngettext("%(hours)s hour", "%(hours)s hours", h) % {"hours": h}
|
||||
if m > 0 and precision != "h":
|
||||
if duration != "":
|
||||
duration += ", "
|
||||
duration += ngettext("%(minutes)s minute", "%(minutes)s minutes", m) % {
|
||||
"minutes": m
|
||||
}
|
||||
if s > 0 and precision != "h" and precision != "m":
|
||||
if duration != "":
|
||||
duration += ", "
|
||||
duration += ngettext("%(seconds)s second", "%(seconds)s seconds", s) % {
|
||||
"seconds": s
|
||||
}
|
||||
if m > 0 and precision != 'h':
|
||||
if duration != '':
|
||||
duration += ', '
|
||||
duration += ngettext(
|
||||
'%(minutes)s minute',
|
||||
'%(minutes)s minutes',
|
||||
m
|
||||
) % {'minutes': m}
|
||||
if s > 0 and precision != 'h' and precision != 'm':
|
||||
if duration != '':
|
||||
duration += ', '
|
||||
duration += ngettext(
|
||||
'%(seconds)s second',
|
||||
'%(seconds)s seconds',
|
||||
s
|
||||
) % {'seconds': s}
|
||||
|
||||
return duration
|
||||
|
||||
|
||||
def duration_parts(duration):
|
||||
"""Get hours, minutes and seconds from a timedelta.
|
||||
"""
|
||||
"""Get hours, minutes and seconds from a timedelta."""
|
||||
if not isinstance(duration, timezone.timedelta):
|
||||
raise TypeError('Duration provided must be a timedetla')
|
||||
raise TypeError("Duration provided must be a timedetla")
|
||||
h, remainder = divmod(duration.seconds, 3600)
|
||||
h += duration.days * 24
|
||||
m, s = divmod(remainder, 60)
|
||||
|
|
339
core/views.py
339
core/views.py
|
@ -8,8 +8,7 @@ from django.utils import timezone
|
|||
from django.utils.translation import gettext as _
|
||||
from django.views.generic.base import RedirectView, TemplateView
|
||||
from django.views.generic.detail import DetailView
|
||||
from django.views.generic.edit import CreateView, UpdateView, DeleteView, \
|
||||
FormView
|
||||
from django.views.generic.edit import CreateView, UpdateView, DeleteView, FormView
|
||||
|
||||
from babybuddy.mixins import LoginRequiredMixin, PermissionRequiredMixin
|
||||
from babybuddy.views import BabyBuddyFilterView
|
||||
|
@ -17,23 +16,23 @@ from core import forms, models, timeline
|
|||
|
||||
|
||||
def _prepare_timeline_context_data(context, date, child=None):
|
||||
date = timezone.datetime.strptime(date, '%Y-%m-%d')
|
||||
date = timezone.datetime.strptime(date, "%Y-%m-%d")
|
||||
date = timezone.localtime(timezone.make_aware(date))
|
||||
context['timeline_objects'] = timeline.get_objects(date, child)
|
||||
context['date'] = date
|
||||
context['date_previous'] = date - timezone.timedelta(days=1)
|
||||
context["timeline_objects"] = timeline.get_objects(date, child)
|
||||
context["date"] = date
|
||||
context["date_previous"] = date - timezone.timedelta(days=1)
|
||||
if date.date() < timezone.localdate():
|
||||
context['date_next'] = date + timezone.timedelta(days=1)
|
||||
context["date_next"] = date + timezone.timedelta(days=1)
|
||||
pass
|
||||
|
||||
|
||||
class CoreAddView(PermissionRequiredMixin, SuccessMessageMixin, CreateView):
|
||||
def get_success_message(self, cleaned_data):
|
||||
cleaned_data['model'] = self.model._meta.verbose_name.title()
|
||||
if 'child' in cleaned_data:
|
||||
self.success_message = _('%(model)s entry for %(child)s added!')
|
||||
cleaned_data["model"] = self.model._meta.verbose_name.title()
|
||||
if "child" in cleaned_data:
|
||||
self.success_message = _("%(model)s entry for %(child)s added!")
|
||||
else:
|
||||
self.success_message = _('%(model)s entry added!')
|
||||
self.success_message = _("%(model)s entry added!")
|
||||
return self.success_message % cleaned_data
|
||||
|
||||
def get_form_kwargs(self):
|
||||
|
@ -48,291 +47,287 @@ class CoreAddView(PermissionRequiredMixin, SuccessMessageMixin, CreateView):
|
|||
:return: Updated keyword arguments.
|
||||
"""
|
||||
kwargs = super(CoreAddView, self).get_form_kwargs()
|
||||
for parameter in ['child', 'timer']:
|
||||
for parameter in ["child", "timer"]:
|
||||
value = self.request.GET.get(parameter, None)
|
||||
if value:
|
||||
kwargs.update({parameter: value})
|
||||
return kwargs
|
||||
|
||||
|
||||
class CoreUpdateView(PermissionRequiredMixin, SuccessMessageMixin,
|
||||
UpdateView):
|
||||
class CoreUpdateView(PermissionRequiredMixin, SuccessMessageMixin, UpdateView):
|
||||
def get_success_message(self, cleaned_data):
|
||||
cleaned_data['model'] = self.model._meta.verbose_name.title()
|
||||
if 'child' in cleaned_data:
|
||||
self.success_message = _('%(model)s entry for %(child)s updated.')
|
||||
cleaned_data["model"] = self.model._meta.verbose_name.title()
|
||||
if "child" in cleaned_data:
|
||||
self.success_message = _("%(model)s entry for %(child)s updated.")
|
||||
else:
|
||||
self.success_message = _('%(model)s entry updated.')
|
||||
self.success_message = _("%(model)s entry updated.")
|
||||
return self.success_message % cleaned_data
|
||||
|
||||
|
||||
class CoreDeleteView(PermissionRequiredMixin, SuccessMessageMixin, DeleteView):
|
||||
def get_success_message(self, cleaned_data):
|
||||
return _('%(model)s entry deleted.') % {
|
||||
'model': self.model._meta.verbose_name.title()
|
||||
return _("%(model)s entry deleted.") % {
|
||||
"model": self.model._meta.verbose_name.title()
|
||||
}
|
||||
|
||||
|
||||
class ChildList(PermissionRequiredMixin, BabyBuddyFilterView):
|
||||
model = models.Child
|
||||
template_name = 'core/child_list.html'
|
||||
permission_required = ('core.view_child',)
|
||||
template_name = "core/child_list.html"
|
||||
permission_required = ("core.view_child",)
|
||||
paginate_by = 10
|
||||
filterset_fields = ('first_name', 'last_name')
|
||||
filterset_fields = ("first_name", "last_name")
|
||||
|
||||
|
||||
class ChildAdd(CoreAddView):
|
||||
model = models.Child
|
||||
permission_required = ('core.add_child',)
|
||||
permission_required = ("core.add_child",)
|
||||
form_class = forms.ChildForm
|
||||
success_url = reverse_lazy('core:child-list')
|
||||
success_message = _('%(first_name)s %(last_name)s added!')
|
||||
success_url = reverse_lazy("core:child-list")
|
||||
success_message = _("%(first_name)s %(last_name)s added!")
|
||||
|
||||
|
||||
class ChildDetail(PermissionRequiredMixin, DetailView):
|
||||
model = models.Child
|
||||
permission_required = ('core.view_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 = self.request.GET.get("date", str(timezone.localdate()))
|
||||
_prepare_timeline_context_data(context, date, self.object)
|
||||
return context
|
||||
|
||||
|
||||
class ChildUpdate(CoreUpdateView):
|
||||
model = models.Child
|
||||
permission_required = ('core.change_child',)
|
||||
permission_required = ("core.change_child",)
|
||||
form_class = forms.ChildForm
|
||||
success_url = reverse_lazy('core:child-list')
|
||||
success_url = reverse_lazy("core:child-list")
|
||||
|
||||
|
||||
class ChildDelete(CoreUpdateView):
|
||||
model = models.Child
|
||||
form_class = forms.ChildDeleteForm
|
||||
template_name = 'core/child_confirm_delete.html'
|
||||
permission_required = ('core.delete_child',)
|
||||
success_url = reverse_lazy('core:child-list')
|
||||
template_name = "core/child_confirm_delete.html"
|
||||
permission_required = ("core.delete_child",)
|
||||
success_url = reverse_lazy("core:child-list")
|
||||
|
||||
def get_success_message(self, cleaned_data):
|
||||
""" This class cannot use `CoreDeleteView` because of the confirmation
|
||||
"""This class cannot use `CoreDeleteView` because of the confirmation
|
||||
step required so the success message must be overridden."""
|
||||
success_message = _('%(model)s entry deleted.') % {
|
||||
'model': self.model._meta.verbose_name.title()
|
||||
success_message = _("%(model)s entry deleted.") % {
|
||||
"model": self.model._meta.verbose_name.title()
|
||||
}
|
||||
return success_message % cleaned_data
|
||||
|
||||
|
||||
class DiaperChangeList(PermissionRequiredMixin, BabyBuddyFilterView):
|
||||
model = models.DiaperChange
|
||||
template_name = 'core/diaperchange_list.html'
|
||||
permission_required = ('core.view_diaperchange',)
|
||||
template_name = "core/diaperchange_list.html"
|
||||
permission_required = ("core.view_diaperchange",)
|
||||
paginate_by = 10
|
||||
filterset_fields = ('child', 'wet', 'solid', 'color')
|
||||
filterset_fields = ("child", "wet", "solid", "color")
|
||||
|
||||
|
||||
class DiaperChangeAdd(CoreAddView):
|
||||
model = models.DiaperChange
|
||||
permission_required = ('core.add_diaperchange',)
|
||||
permission_required = ("core.add_diaperchange",)
|
||||
form_class = forms.DiaperChangeForm
|
||||
success_url = reverse_lazy('core:diaperchange-list')
|
||||
success_url = reverse_lazy("core:diaperchange-list")
|
||||
|
||||
|
||||
class DiaperChangeUpdate(CoreUpdateView):
|
||||
model = models.DiaperChange
|
||||
permission_required = ('core.change_diaperchange',)
|
||||
permission_required = ("core.change_diaperchange",)
|
||||
form_class = forms.DiaperChangeForm
|
||||
success_url = reverse_lazy('core:diaperchange-list')
|
||||
success_url = reverse_lazy("core:diaperchange-list")
|
||||
|
||||
|
||||
class DiaperChangeDelete(CoreDeleteView):
|
||||
model = models.DiaperChange
|
||||
permission_required = ('core.delete_diaperchange',)
|
||||
success_url = reverse_lazy('core:diaperchange-list')
|
||||
permission_required = ("core.delete_diaperchange",)
|
||||
success_url = reverse_lazy("core:diaperchange-list")
|
||||
|
||||
|
||||
class FeedingList(PermissionRequiredMixin, BabyBuddyFilterView):
|
||||
model = models.Feeding
|
||||
template_name = 'core/feeding_list.html'
|
||||
permission_required = ('core.view_feeding',)
|
||||
template_name = "core/feeding_list.html"
|
||||
permission_required = ("core.view_feeding",)
|
||||
paginate_by = 10
|
||||
filterset_fields = ('child', 'type', 'method')
|
||||
filterset_fields = ("child", "type", "method")
|
||||
|
||||
|
||||
class FeedingAdd(CoreAddView):
|
||||
model = models.Feeding
|
||||
permission_required = ('core.add_feeding',)
|
||||
permission_required = ("core.add_feeding",)
|
||||
form_class = forms.FeedingForm
|
||||
success_url = reverse_lazy('core:feeding-list')
|
||||
success_url = reverse_lazy("core:feeding-list")
|
||||
|
||||
|
||||
class FeedingUpdate(CoreUpdateView):
|
||||
model = models.Feeding
|
||||
permission_required = ('core.change_feeding',)
|
||||
permission_required = ("core.change_feeding",)
|
||||
form_class = forms.FeedingForm
|
||||
success_url = reverse_lazy('core:feeding-list')
|
||||
success_url = reverse_lazy("core:feeding-list")
|
||||
|
||||
|
||||
class FeedingDelete(CoreDeleteView):
|
||||
model = models.Feeding
|
||||
permission_required = ('core.delete_feeding',)
|
||||
success_url = reverse_lazy('core:feeding-list')
|
||||
permission_required = ("core.delete_feeding",)
|
||||
success_url = reverse_lazy("core:feeding-list")
|
||||
|
||||
|
||||
class NoteList(PermissionRequiredMixin, BabyBuddyFilterView):
|
||||
model = models.Note
|
||||
template_name = 'core/note_list.html'
|
||||
permission_required = ('core.view_note',)
|
||||
template_name = "core/note_list.html"
|
||||
permission_required = ("core.view_note",)
|
||||
paginate_by = 10
|
||||
filterset_fields = ('child',)
|
||||
filterset_fields = ("child",)
|
||||
|
||||
|
||||
class NoteAdd(CoreAddView):
|
||||
model = models.Note
|
||||
permission_required = ('core.add_note',)
|
||||
permission_required = ("core.add_note",)
|
||||
form_class = forms.NoteForm
|
||||
success_url = reverse_lazy('core:note-list')
|
||||
success_url = reverse_lazy("core:note-list")
|
||||
|
||||
|
||||
class NoteUpdate(CoreUpdateView):
|
||||
model = models.Note
|
||||
permission_required = ('core.change_note',)
|
||||
permission_required = ("core.change_note",)
|
||||
form_class = forms.NoteForm
|
||||
success_url = reverse_lazy('core:note-list')
|
||||
success_url = reverse_lazy("core:note-list")
|
||||
|
||||
|
||||
class NoteDelete(CoreDeleteView):
|
||||
model = models.Note
|
||||
permission_required = ('core.delete_note',)
|
||||
success_url = reverse_lazy('core:note-list')
|
||||
permission_required = ("core.delete_note",)
|
||||
success_url = reverse_lazy("core:note-list")
|
||||
|
||||
|
||||
class SleepList(PermissionRequiredMixin, BabyBuddyFilterView):
|
||||
model = models.Sleep
|
||||
template_name = 'core/sleep_list.html'
|
||||
permission_required = ('core.view_sleep',)
|
||||
template_name = "core/sleep_list.html"
|
||||
permission_required = ("core.view_sleep",)
|
||||
paginate_by = 10
|
||||
filterset_fields = ('child',)
|
||||
filterset_fields = ("child",)
|
||||
|
||||
|
||||
class SleepAdd(CoreAddView):
|
||||
model = models.Sleep
|
||||
permission_required = ('core.add_sleep',)
|
||||
permission_required = ("core.add_sleep",)
|
||||
form_class = forms.SleepForm
|
||||
success_url = reverse_lazy('core:sleep-list')
|
||||
success_url = reverse_lazy("core:sleep-list")
|
||||
|
||||
|
||||
class SleepUpdate(CoreUpdateView):
|
||||
model = models.Sleep
|
||||
permission_required = ('core.change_sleep',)
|
||||
permission_required = ("core.change_sleep",)
|
||||
form_class = forms.SleepForm
|
||||
success_url = reverse_lazy('core:sleep-list')
|
||||
success_url = reverse_lazy("core:sleep-list")
|
||||
|
||||
|
||||
class SleepDelete(CoreDeleteView):
|
||||
model = models.Sleep
|
||||
permission_required = ('core.delete_sleep',)
|
||||
success_url = reverse_lazy('core:sleep-list')
|
||||
permission_required = ("core.delete_sleep",)
|
||||
success_url = reverse_lazy("core:sleep-list")
|
||||
|
||||
|
||||
class TemperatureList(PermissionRequiredMixin, BabyBuddyFilterView):
|
||||
model = models.Temperature
|
||||
template_name = 'core/temperature_list.html'
|
||||
permission_required = ('core.view_temperature',)
|
||||
template_name = "core/temperature_list.html"
|
||||
permission_required = ("core.view_temperature",)
|
||||
paginate_by = 10
|
||||
filterset_fields = ('child',)
|
||||
filterset_fields = ("child",)
|
||||
|
||||
|
||||
class TemperatureAdd(CoreAddView):
|
||||
model = models.Temperature
|
||||
permission_required = ('core.add_temperature',)
|
||||
permission_required = ("core.add_temperature",)
|
||||
form_class = forms.TemperatureForm
|
||||
success_url = reverse_lazy('core:temperature-list')
|
||||
success_message = _('%(model)s reading added!')
|
||||
success_url = reverse_lazy("core:temperature-list")
|
||||
success_message = _("%(model)s reading added!")
|
||||
|
||||
|
||||
class TemperatureUpdate(CoreUpdateView):
|
||||
model = models.Temperature
|
||||
permission_required = ('core.change_temperature',)
|
||||
permission_required = ("core.change_temperature",)
|
||||
form_class = forms.TemperatureForm
|
||||
success_url = reverse_lazy('core:temperature-list')
|
||||
success_message = _('%(model)s reading for %(child)s updated.')
|
||||
success_url = reverse_lazy("core:temperature-list")
|
||||
success_message = _("%(model)s reading for %(child)s updated.")
|
||||
|
||||
|
||||
class TemperatureDelete(CoreDeleteView):
|
||||
model = models.Temperature
|
||||
permission_required = ('core.delete_temperature',)
|
||||
success_url = reverse_lazy('core:temperature-list')
|
||||
permission_required = ("core.delete_temperature",)
|
||||
success_url = reverse_lazy("core:temperature-list")
|
||||
|
||||
|
||||
class Timeline(LoginRequiredMixin, TemplateView):
|
||||
template_name = 'timeline/timeline.html'
|
||||
template_name = "timeline/timeline.html"
|
||||
|
||||
# Show the overall timeline or a child timeline if one Child instance.
|
||||
def get(self, request, *args, **kwargs):
|
||||
children = models.Child.objects.count()
|
||||
if children == 1:
|
||||
return HttpResponseRedirect(
|
||||
reverse(
|
||||
'core:child',
|
||||
args={models.Child.objects.first().slug}
|
||||
)
|
||||
reverse("core:child", args={models.Child.objects.first().slug})
|
||||
)
|
||||
return super(Timeline, self).get(request, *args, **kwargs)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(Timeline, self).get_context_data(**kwargs)
|
||||
date = self.request.GET.get('date', str(timezone.localdate()))
|
||||
date = self.request.GET.get("date", str(timezone.localdate()))
|
||||
_prepare_timeline_context_data(context, date)
|
||||
return context
|
||||
|
||||
|
||||
class TimerList(PermissionRequiredMixin, BabyBuddyFilterView):
|
||||
model = models.Timer
|
||||
template_name = 'core/timer_list.html'
|
||||
permission_required = ('core.view_timer',)
|
||||
template_name = "core/timer_list.html"
|
||||
permission_required = ("core.view_timer",)
|
||||
paginate_by = 10
|
||||
filterset_fields = ('active', 'user')
|
||||
filterset_fields = ("active", "user")
|
||||
|
||||
|
||||
class TimerDetail(PermissionRequiredMixin, DetailView):
|
||||
model = models.Timer
|
||||
permission_required = ('core.view_timer',)
|
||||
permission_required = ("core.view_timer",)
|
||||
|
||||
|
||||
class TimerAdd(PermissionRequiredMixin, CreateView):
|
||||
model = models.Timer
|
||||
permission_required = ('core.add_timer',)
|
||||
permission_required = ("core.add_timer",)
|
||||
form_class = forms.TimerForm
|
||||
|
||||
def get_form_kwargs(self):
|
||||
kwargs = super(TimerAdd, self).get_form_kwargs()
|
||||
kwargs.update({'user': self.request.user})
|
||||
kwargs.update({"user": self.request.user})
|
||||
return kwargs
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('core:timer-detail', kwargs={'pk': self.object.pk})
|
||||
return reverse("core:timer-detail", kwargs={"pk": self.object.pk})
|
||||
|
||||
|
||||
class TimerUpdate(CoreUpdateView):
|
||||
model = models.Timer
|
||||
permission_required = ('core.change_timer',)
|
||||
permission_required = ("core.change_timer",)
|
||||
form_class = forms.TimerForm
|
||||
success_url = reverse_lazy('core:timer-list')
|
||||
success_url = reverse_lazy("core:timer-list")
|
||||
|
||||
def get_form_kwargs(self):
|
||||
kwargs = super(TimerUpdate, self).get_form_kwargs()
|
||||
kwargs.update({'user': self.request.user})
|
||||
kwargs.update({"user": self.request.user})
|
||||
return kwargs
|
||||
|
||||
def get_success_url(self):
|
||||
instance = self.get_object()
|
||||
return reverse('core:timer-detail', kwargs={'pk': instance.pk})
|
||||
return reverse("core:timer-detail", kwargs={"pk": instance.pk})
|
||||
|
||||
|
||||
class TimerAddQuick(PermissionRequiredMixin, RedirectView):
|
||||
http_method_names = ['post']
|
||||
permission_required = ('core.add_timer',)
|
||||
http_method_names = ["post"]
|
||||
permission_required = ("core.add_timer",)
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
instance = models.Timer.objects.create(user=request.user)
|
||||
|
@ -341,62 +336,62 @@ class TimerAddQuick(PermissionRequiredMixin, RedirectView):
|
|||
instance.child = models.Child.objects.first()
|
||||
instance.save()
|
||||
self.url = request.GET.get(
|
||||
'next', reverse('core:timer-detail', args={instance.id}))
|
||||
"next", reverse("core:timer-detail", args={instance.id})
|
||||
)
|
||||
return super(TimerAddQuick, self).get(request, *args, **kwargs)
|
||||
|
||||
|
||||
class TimerRestart(PermissionRequiredMixin, RedirectView):
|
||||
http_method_names = ['post']
|
||||
permission_required = ('core.change_timer',)
|
||||
http_method_names = ["post"]
|
||||
permission_required = ("core.change_timer",)
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
instance = models.Timer.objects.get(id=kwargs['pk'])
|
||||
instance = models.Timer.objects.get(id=kwargs["pk"])
|
||||
instance.restart()
|
||||
messages.success(request, '{} restarted.'.format(instance))
|
||||
messages.success(request, "{} restarted.".format(instance))
|
||||
return super(TimerRestart, self).get(request, *args, **kwargs)
|
||||
|
||||
def get_redirect_url(self, *args, **kwargs):
|
||||
return reverse('core:timer-detail', kwargs={'pk': kwargs['pk']})
|
||||
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.')
|
||||
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 = models.Timer.objects.get(id=kwargs["pk"])
|
||||
instance.stop()
|
||||
messages.success(request, '{} stopped.'.format(instance))
|
||||
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']})
|
||||
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')
|
||||
permission_required = ("core.delete_timer",)
|
||||
success_url = reverse_lazy("core:timer-list")
|
||||
|
||||
|
||||
class TimerDeleteInactive(PermissionRequiredMixin, SuccessMessageMixin,
|
||||
FormView):
|
||||
permission_required = ('core.delete_timer',)
|
||||
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.')
|
||||
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()
|
||||
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.'))
|
||||
messages.warning(request, _("No inactive timers exist."))
|
||||
return HttpResponseRedirect(self.success_url)
|
||||
return super().get(request, *args, **kwargs)
|
||||
|
||||
|
@ -411,142 +406,142 @@ class TimerDeleteInactive(PermissionRequiredMixin, SuccessMessageMixin,
|
|||
|
||||
class TummyTimeList(PermissionRequiredMixin, BabyBuddyFilterView):
|
||||
model = models.TummyTime
|
||||
template_name = 'core/tummytime_list.html'
|
||||
permission_required = ('core.view_tummytime',)
|
||||
template_name = "core/tummytime_list.html"
|
||||
permission_required = ("core.view_tummytime",)
|
||||
paginate_by = 10
|
||||
filterset_fields = ('child',)
|
||||
filterset_fields = ("child",)
|
||||
|
||||
|
||||
class TummyTimeAdd(CoreAddView):
|
||||
model = models.TummyTime
|
||||
permission_required = ('core.add_tummytime',)
|
||||
permission_required = ("core.add_tummytime",)
|
||||
form_class = forms.TummyTimeForm
|
||||
success_url = reverse_lazy('core:tummytime-list')
|
||||
success_url = reverse_lazy("core:tummytime-list")
|
||||
|
||||
|
||||
class TummyTimeUpdate(CoreUpdateView):
|
||||
model = models.TummyTime
|
||||
permission_required = ('core.change_tummytime',)
|
||||
permission_required = ("core.change_tummytime",)
|
||||
form_class = forms.TummyTimeForm
|
||||
success_url = reverse_lazy('core:tummytime-list')
|
||||
success_url = reverse_lazy("core:tummytime-list")
|
||||
|
||||
|
||||
class TummyTimeDelete(CoreDeleteView):
|
||||
model = models.TummyTime
|
||||
permission_required = ('core.delete_tummytime',)
|
||||
success_url = reverse_lazy('core:tummytime-list')
|
||||
permission_required = ("core.delete_tummytime",)
|
||||
success_url = reverse_lazy("core:tummytime-list")
|
||||
|
||||
|
||||
class WeightList(PermissionRequiredMixin, BabyBuddyFilterView):
|
||||
model = models.Weight
|
||||
template_name = 'core/weight_list.html'
|
||||
permission_required = ('core.view_weight',)
|
||||
template_name = "core/weight_list.html"
|
||||
permission_required = ("core.view_weight",)
|
||||
paginate_by = 10
|
||||
filterset_fields = ('child',)
|
||||
filterset_fields = ("child",)
|
||||
|
||||
|
||||
class WeightAdd(CoreAddView):
|
||||
model = models.Weight
|
||||
permission_required = ('core.add_weight',)
|
||||
permission_required = ("core.add_weight",)
|
||||
form_class = forms.WeightForm
|
||||
success_url = reverse_lazy('core:weight-list')
|
||||
success_url = reverse_lazy("core:weight-list")
|
||||
|
||||
|
||||
class WeightUpdate(CoreUpdateView):
|
||||
model = models.Weight
|
||||
permission_required = ('core.change_weight',)
|
||||
permission_required = ("core.change_weight",)
|
||||
form_class = forms.WeightForm
|
||||
success_url = reverse_lazy('core:weight-list')
|
||||
success_url = reverse_lazy("core:weight-list")
|
||||
|
||||
|
||||
class WeightDelete(CoreDeleteView):
|
||||
model = models.Weight
|
||||
permission_required = ('core.delete_weight',)
|
||||
success_url = reverse_lazy('core:weight-list')
|
||||
permission_required = ("core.delete_weight",)
|
||||
success_url = reverse_lazy("core:weight-list")
|
||||
|
||||
|
||||
class HeightList(PermissionRequiredMixin, BabyBuddyFilterView):
|
||||
model = models.Height
|
||||
template_name = 'core/height_list.html'
|
||||
permission_required = ('core.view_height',)
|
||||
template_name = "core/height_list.html"
|
||||
permission_required = ("core.view_height",)
|
||||
paginate_by = 10
|
||||
filterset_fields = ('child',)
|
||||
filterset_fields = ("child",)
|
||||
|
||||
|
||||
class HeightAdd(CoreAddView):
|
||||
model = models.Height
|
||||
permission_required = ('core.add_height',)
|
||||
permission_required = ("core.add_height",)
|
||||
form_class = forms.HeightForm
|
||||
success_url = reverse_lazy('core:height-list')
|
||||
success_url = reverse_lazy("core:height-list")
|
||||
|
||||
|
||||
class HeightUpdate(CoreUpdateView):
|
||||
model = models.Height
|
||||
permission_required = ('core.change_height',)
|
||||
permission_required = ("core.change_height",)
|
||||
form_class = forms.HeightForm
|
||||
success_url = reverse_lazy('core:height-list')
|
||||
success_url = reverse_lazy("core:height-list")
|
||||
|
||||
|
||||
class HeightDelete(CoreDeleteView):
|
||||
model = models.Height
|
||||
permission_required = ('core.delete_height',)
|
||||
success_url = reverse_lazy('core:height-list')
|
||||
permission_required = ("core.delete_height",)
|
||||
success_url = reverse_lazy("core:height-list")
|
||||
|
||||
|
||||
class HeadCircumferenceList(PermissionRequiredMixin, BabyBuddyFilterView):
|
||||
model = models.HeadCircumference
|
||||
template_name = 'core/head_circumference_list.html'
|
||||
permission_required = ('core.view_head_circumference',)
|
||||
template_name = "core/head_circumference_list.html"
|
||||
permission_required = ("core.view_head_circumference",)
|
||||
paginate_by = 10
|
||||
filterset_fields = ('child',)
|
||||
filterset_fields = ("child",)
|
||||
|
||||
|
||||
class HeadCircumferenceAdd(CoreAddView):
|
||||
model = models.HeadCircumference
|
||||
template_name = 'core/head_circumference_form.html'
|
||||
permission_required = ('core.add_head_circumference',)
|
||||
template_name = "core/head_circumference_form.html"
|
||||
permission_required = ("core.add_head_circumference",)
|
||||
form_class = forms.HeadCircumferenceForm
|
||||
success_url = reverse_lazy('core:head-circumference-list')
|
||||
success_url = reverse_lazy("core:head-circumference-list")
|
||||
|
||||
|
||||
class HeadCircumferenceUpdate(CoreUpdateView):
|
||||
model = models.HeadCircumference
|
||||
template_name = 'core/head_circumference_form.html'
|
||||
permission_required = ('core.change_head_circumference',)
|
||||
template_name = "core/head_circumference_form.html"
|
||||
permission_required = ("core.change_head_circumference",)
|
||||
form_class = forms.HeadCircumferenceForm
|
||||
success_url = reverse_lazy('core:head-circumference-list')
|
||||
success_url = reverse_lazy("core:head-circumference-list")
|
||||
|
||||
|
||||
class HeadCircumferenceDelete(CoreDeleteView):
|
||||
model = models.HeadCircumference
|
||||
template_name = 'core/head_circumference_delete.html'
|
||||
permission_required = ('core.delete_head_circumference',)
|
||||
success_url = reverse_lazy('core:head-circumference-list')
|
||||
template_name = "core/head_circumference_delete.html"
|
||||
permission_required = ("core.delete_head_circumference",)
|
||||
success_url = reverse_lazy("core:head-circumference-list")
|
||||
|
||||
|
||||
class BMIList(PermissionRequiredMixin, BabyBuddyFilterView):
|
||||
model = models.BMI
|
||||
template_name = 'core/bmi_list.html'
|
||||
permission_required = ('core.view_bmi',)
|
||||
template_name = "core/bmi_list.html"
|
||||
permission_required = ("core.view_bmi",)
|
||||
paginate_by = 10
|
||||
filterset_fields = ('child',)
|
||||
filterset_fields = ("child",)
|
||||
|
||||
|
||||
class BMIAdd(CoreAddView):
|
||||
model = models.BMI
|
||||
permission_required = ('core.add_bmi',)
|
||||
permission_required = ("core.add_bmi",)
|
||||
form_class = forms.BMIForm
|
||||
success_url = reverse_lazy('core:bmi-list')
|
||||
success_url = reverse_lazy("core:bmi-list")
|
||||
|
||||
|
||||
class BMIUpdate(CoreUpdateView):
|
||||
model = models.BMI
|
||||
permission_required = ('core.change_bmi',)
|
||||
permission_required = ("core.change_bmi",)
|
||||
form_class = forms.BMIForm
|
||||
success_url = reverse_lazy('core:bmi-list')
|
||||
success_url = reverse_lazy("core:bmi-list")
|
||||
|
||||
|
||||
class BMIDelete(CoreDeleteView):
|
||||
model = models.BMI
|
||||
permission_required = ('core.delete_bmi',)
|
||||
success_url = reverse_lazy('core:bmi-list')
|
||||
permission_required = ("core.delete_bmi",)
|
||||
success_url = reverse_lazy("core:bmi-list")
|
||||
|
|
|
@ -13,39 +13,42 @@ register = template.Library()
|
|||
|
||||
|
||||
def _hide_empty(context):
|
||||
return context['request'].user.settings.dashboard_hide_empty
|
||||
return context["request"].user.settings.dashboard_hide_empty
|
||||
|
||||
|
||||
def _filter_data_age(context, keyword="end"):
|
||||
filter = {}
|
||||
if context['request'].user.settings.dashboard_hide_age:
|
||||
if context["request"].user.settings.dashboard_hide_age:
|
||||
now = timezone.localtime()
|
||||
start_time = now - context['request'].user.settings.dashboard_hide_age
|
||||
start_time = now - context["request"].user.settings.dashboard_hide_age
|
||||
filter[keyword + "__range"] = (start_time, now)
|
||||
return filter
|
||||
|
||||
|
||||
@register.inclusion_tag('cards/diaperchange_last.html', takes_context=True)
|
||||
@register.inclusion_tag("cards/diaperchange_last.html", takes_context=True)
|
||||
def card_diaperchange_last(context, child):
|
||||
"""
|
||||
Information about the most recent diaper change.
|
||||
:param child: an instance of the Child model.
|
||||
:returns: a dictionary with the most recent Diaper Change instance.
|
||||
"""
|
||||
instance = models.DiaperChange.objects.filter(child=child) \
|
||||
.filter(**_filter_data_age(context, "time")) \
|
||||
.order_by('-time').first()
|
||||
instance = (
|
||||
models.DiaperChange.objects.filter(child=child)
|
||||
.filter(**_filter_data_age(context, "time"))
|
||||
.order_by("-time")
|
||||
.first()
|
||||
)
|
||||
empty = not instance
|
||||
|
||||
return {
|
||||
'type': 'diaperchange',
|
||||
'change': instance,
|
||||
'empty': empty,
|
||||
'hide_empty': _hide_empty(context)
|
||||
"type": "diaperchange",
|
||||
"change": instance,
|
||||
"empty": empty,
|
||||
"hide_empty": _hide_empty(context),
|
||||
}
|
||||
|
||||
|
||||
@register.inclusion_tag('cards/diaperchange_types.html', takes_context=True)
|
||||
@register.inclusion_tag("cards/diaperchange_types.html", takes_context=True)
|
||||
def card_diaperchange_types(context, child, date=None):
|
||||
"""
|
||||
Creates a break down of wet and solid Diaper Change instances for the past
|
||||
|
@ -61,42 +64,46 @@ def card_diaperchange_types(context, child, date=None):
|
|||
time = timezone.make_aware(time)
|
||||
stats = {}
|
||||
week_total = 0
|
||||
max_date = (time + timezone.timedelta(days=1)).replace(
|
||||
hour=0, minute=0, second=0)
|
||||
max_date = (time + timezone.timedelta(days=1)).replace(hour=0, minute=0, second=0)
|
||||
min_date = (max_date - timezone.timedelta(days=7)).replace(
|
||||
hour=0, minute=0, second=0)
|
||||
hour=0, minute=0, second=0
|
||||
)
|
||||
|
||||
for x in range(7):
|
||||
stats[x] = {'wet': 0.0, 'solid': 0.0}
|
||||
stats[x] = {"wet": 0.0, "solid": 0.0}
|
||||
|
||||
instances = models.DiaperChange.objects.filter(child=child) \
|
||||
.filter(time__gt=min_date).filter(time__lt=max_date).order_by('-time')
|
||||
instances = (
|
||||
models.DiaperChange.objects.filter(child=child)
|
||||
.filter(time__gt=min_date)
|
||||
.filter(time__lt=max_date)
|
||||
.order_by("-time")
|
||||
)
|
||||
empty = len(instances) == 0
|
||||
|
||||
for instance in instances:
|
||||
key = (max_date - instance.time).days
|
||||
if instance.wet:
|
||||
stats[key]['wet'] += 1
|
||||
stats[key]["wet"] += 1
|
||||
if instance.solid:
|
||||
stats[key]['solid'] += 1
|
||||
stats[key]["solid"] += 1
|
||||
|
||||
for key, info in stats.items():
|
||||
total = info['wet'] + info['solid']
|
||||
total = info["wet"] + info["solid"]
|
||||
week_total += total
|
||||
if total > 0:
|
||||
stats[key]['wet_pct'] = info['wet'] / total * 100
|
||||
stats[key]['solid_pct'] = info['solid'] / total * 100
|
||||
stats[key]["wet_pct"] = info["wet"] / total * 100
|
||||
stats[key]["solid_pct"] = info["solid"] / total * 100
|
||||
|
||||
return {
|
||||
'type': 'diaperchange',
|
||||
'stats': stats,
|
||||
'total': week_total,
|
||||
'empty': empty,
|
||||
'hide_empty': _hide_empty(context)
|
||||
"type": "diaperchange",
|
||||
"stats": stats,
|
||||
"total": week_total,
|
||||
"empty": empty,
|
||||
"hide_empty": _hide_empty(context),
|
||||
}
|
||||
|
||||
|
||||
@register.inclusion_tag('cards/feeding_day.html', takes_context=True)
|
||||
@register.inclusion_tag("cards/feeding_day.html", takes_context=True)
|
||||
def card_feeding_day(context, child, date=None):
|
||||
"""
|
||||
Filters Feeding instances to get total amount for a specific date.
|
||||
|
@ -108,90 +115,95 @@ def card_feeding_day(context, child, date=None):
|
|||
date = timezone.localtime().date()
|
||||
|
||||
instances = models.Feeding.objects.filter(child=child).filter(
|
||||
start__year=date.year,
|
||||
start__month=date.month,
|
||||
start__day=date.day) \
|
||||
| models.Feeding.objects.filter(child=child).filter(
|
||||
end__year=date.year,
|
||||
end__month=date.month,
|
||||
end__day=date.day)
|
||||
start__year=date.year, start__month=date.month, start__day=date.day
|
||||
) | models.Feeding.objects.filter(child=child).filter(
|
||||
end__year=date.year, end__month=date.month, end__day=date.day
|
||||
)
|
||||
|
||||
total = sum([instance.amount for instance in instances if instance.amount])
|
||||
count = len(instances)
|
||||
empty = len(instances) == 0 or total == 0
|
||||
|
||||
return {
|
||||
'type': 'feeding',
|
||||
'total': total,
|
||||
'count': count,
|
||||
'empty': empty,
|
||||
'hide_empty': _hide_empty(context)
|
||||
"type": "feeding",
|
||||
"total": total,
|
||||
"count": count,
|
||||
"empty": empty,
|
||||
"hide_empty": _hide_empty(context),
|
||||
}
|
||||
|
||||
|
||||
@register.inclusion_tag('cards/feeding_last.html', takes_context=True)
|
||||
@register.inclusion_tag("cards/feeding_last.html", takes_context=True)
|
||||
def card_feeding_last(context, child):
|
||||
"""
|
||||
Information about the most recent feeding.
|
||||
:param child: an instance of the Child model.
|
||||
:returns: a dictionary with the most recent Feeding instance.
|
||||
"""
|
||||
instance = models.Feeding.objects.filter(child=child) \
|
||||
.filter(**_filter_data_age(context)) \
|
||||
.order_by('-end').first()
|
||||
instance = (
|
||||
models.Feeding.objects.filter(child=child)
|
||||
.filter(**_filter_data_age(context))
|
||||
.order_by("-end")
|
||||
.first()
|
||||
)
|
||||
empty = not instance
|
||||
|
||||
return {
|
||||
'type': 'feeding',
|
||||
'feeding': instance,
|
||||
'empty': empty,
|
||||
'hide_empty': _hide_empty(context)
|
||||
"type": "feeding",
|
||||
"feeding": instance,
|
||||
"empty": empty,
|
||||
"hide_empty": _hide_empty(context),
|
||||
}
|
||||
|
||||
|
||||
@register.inclusion_tag('cards/feeding_last_method.html', takes_context=True)
|
||||
@register.inclusion_tag("cards/feeding_last_method.html", takes_context=True)
|
||||
def card_feeding_last_method(context, child):
|
||||
"""
|
||||
Information about the three most recent feeding methods.
|
||||
:param child: an instance of the Child model.
|
||||
:returns: a dictionary with the most recent Feeding instances.
|
||||
"""
|
||||
instances = models.Feeding.objects.filter(child=child) \
|
||||
.filter(**_filter_data_age(context)) \
|
||||
.order_by('-end')[:3]
|
||||
instances = (
|
||||
models.Feeding.objects.filter(child=child)
|
||||
.filter(**_filter_data_age(context))
|
||||
.order_by("-end")[:3]
|
||||
)
|
||||
num_unique_methods = len({i.method for i in instances})
|
||||
empty = num_unique_methods <= 1
|
||||
|
||||
# Results are reversed for carousel forward/back behavior.
|
||||
return {
|
||||
'type': 'feeding',
|
||||
'feedings': list(reversed(instances)),
|
||||
'empty': empty,
|
||||
'hide_empty': _hide_empty(context)
|
||||
"type": "feeding",
|
||||
"feedings": list(reversed(instances)),
|
||||
"empty": empty,
|
||||
"hide_empty": _hide_empty(context),
|
||||
}
|
||||
|
||||
|
||||
@register.inclusion_tag('cards/sleep_last.html', takes_context=True)
|
||||
@register.inclusion_tag("cards/sleep_last.html", takes_context=True)
|
||||
def card_sleep_last(context, child):
|
||||
"""
|
||||
Information about the most recent sleep entry.
|
||||
:param child: an instance of the Child model.
|
||||
:returns: a dictionary with the most recent Sleep instance.
|
||||
"""
|
||||
instance = models.Sleep.objects.filter(child=child) \
|
||||
.filter(**_filter_data_age(context)) \
|
||||
.order_by('-end').first()
|
||||
instance = (
|
||||
models.Sleep.objects.filter(child=child)
|
||||
.filter(**_filter_data_age(context))
|
||||
.order_by("-end")
|
||||
.first()
|
||||
)
|
||||
empty = not instance
|
||||
|
||||
return {
|
||||
'type': 'sleep',
|
||||
'sleep': instance,
|
||||
'empty': empty,
|
||||
'hide_empty': _hide_empty(context)
|
||||
"type": "sleep",
|
||||
"sleep": instance,
|
||||
"empty": empty,
|
||||
"hide_empty": _hide_empty(context),
|
||||
}
|
||||
|
||||
|
||||
@register.inclusion_tag('cards/sleep_day.html', takes_context=True)
|
||||
@register.inclusion_tag("cards/sleep_day.html", takes_context=True)
|
||||
def card_sleep_day(context, child, date=None):
|
||||
"""
|
||||
Filters Sleep instances to get count and total values for a specific date.
|
||||
|
@ -202,12 +214,10 @@ def card_sleep_day(context, child, date=None):
|
|||
if not date:
|
||||
date = timezone.localtime().date()
|
||||
instances = models.Sleep.objects.filter(child=child).filter(
|
||||
start__year=date.year,
|
||||
start__month=date.month,
|
||||
start__day=date.day) | models.Sleep.objects.filter(child=child).filter(
|
||||
end__year=date.year,
|
||||
end__month=date.month,
|
||||
end__day=date.day)
|
||||
start__year=date.year, start__month=date.month, start__day=date.day
|
||||
) | models.Sleep.objects.filter(child=child).filter(
|
||||
end__year=date.year, end__month=date.month, end__day=date.day
|
||||
)
|
||||
empty = len(instances) == 0
|
||||
|
||||
total = timezone.timedelta(seconds=0)
|
||||
|
@ -216,23 +226,24 @@ def card_sleep_day(context, child, date=None):
|
|||
end = timezone.localtime(instance.end)
|
||||
# Account for dates crossing midnight.
|
||||
if start.date() != date:
|
||||
start = start.replace(year=end.year, month=end.month, day=end.day,
|
||||
hour=0, minute=0, second=0)
|
||||
start = start.replace(
|
||||
year=end.year, month=end.month, day=end.day, hour=0, minute=0, second=0
|
||||
)
|
||||
|
||||
total += end - start
|
||||
|
||||
count = len(instances)
|
||||
|
||||
return {
|
||||
'type': 'sleep',
|
||||
'total': total,
|
||||
'count': count,
|
||||
'empty': empty,
|
||||
'hide_empty': _hide_empty(context)
|
||||
"type": "sleep",
|
||||
"total": total,
|
||||
"count": count,
|
||||
"empty": empty,
|
||||
"hide_empty": _hide_empty(context),
|
||||
}
|
||||
|
||||
|
||||
@register.inclusion_tag('cards/sleep_naps_day.html', takes_context=True)
|
||||
@register.inclusion_tag("cards/sleep_naps_day.html", takes_context=True)
|
||||
def card_sleep_naps_day(context, child, date=None):
|
||||
"""
|
||||
Filters Sleep instances categorized as naps and generates statistics for a
|
||||
|
@ -244,23 +255,22 @@ def card_sleep_naps_day(context, child, date=None):
|
|||
if not date:
|
||||
date = timezone.localtime().date()
|
||||
instances = models.Sleep.naps.filter(child=child).filter(
|
||||
start__year=date.year,
|
||||
start__month=date.month,
|
||||
start__day=date.day) | models.Sleep.naps.filter(child=child).filter(
|
||||
end__year=date.year,
|
||||
end__month=date.month,
|
||||
end__day=date.day)
|
||||
start__year=date.year, start__month=date.month, start__day=date.day
|
||||
) | models.Sleep.naps.filter(child=child).filter(
|
||||
end__year=date.year, end__month=date.month, end__day=date.day
|
||||
)
|
||||
empty = len(instances) == 0
|
||||
|
||||
return {
|
||||
'type': 'sleep',
|
||||
'total': instances.aggregate(Sum('duration'))['duration__sum'],
|
||||
'count': len(instances),
|
||||
'empty': empty, 'hide_empty': _hide_empty(context)
|
||||
"type": "sleep",
|
||||
"total": instances.aggregate(Sum("duration"))["duration__sum"],
|
||||
"count": len(instances),
|
||||
"empty": empty,
|
||||
"hide_empty": _hide_empty(context),
|
||||
}
|
||||
|
||||
|
||||
@register.inclusion_tag('cards/statistics.html', takes_context=True)
|
||||
@register.inclusion_tag("cards/statistics.html", takes_context=True)
|
||||
def card_statistics(context, child):
|
||||
"""
|
||||
Statistics data for all models.
|
||||
|
@ -271,76 +281,102 @@ def card_statistics(context, child):
|
|||
|
||||
changes = _diaperchange_statistics(child)
|
||||
if changes:
|
||||
stats.append({
|
||||
'type': 'duration',
|
||||
'stat': changes['btwn_average'],
|
||||
'title': _('Diaper change frequency')})
|
||||
stats.append(
|
||||
{
|
||||
"type": "duration",
|
||||
"stat": changes["btwn_average"],
|
||||
"title": _("Diaper change frequency"),
|
||||
}
|
||||
)
|
||||
|
||||
feedings = _feeding_statistics(child)
|
||||
if feedings:
|
||||
for item in feedings:
|
||||
stats.append({
|
||||
'type': 'duration',
|
||||
'stat': item['btwn_average'],
|
||||
'title': item['title']})
|
||||
stats.append(
|
||||
{
|
||||
"type": "duration",
|
||||
"stat": item["btwn_average"],
|
||||
"title": item["title"],
|
||||
}
|
||||
)
|
||||
|
||||
naps = _nap_statistics(child)
|
||||
if naps:
|
||||
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')})
|
||||
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_statistics(child)
|
||||
if sleep:
|
||||
stats.append({
|
||||
'type': 'duration',
|
||||
'stat': sleep['average'],
|
||||
'title': _('Average sleep duration')})
|
||||
stats.append({
|
||||
'type': 'duration',
|
||||
'stat': sleep['btwn_average'],
|
||||
'title': _('Average awake duration')})
|
||||
stats.append(
|
||||
{
|
||||
"type": "duration",
|
||||
"stat": sleep["average"],
|
||||
"title": _("Average sleep duration"),
|
||||
}
|
||||
)
|
||||
stats.append(
|
||||
{
|
||||
"type": "duration",
|
||||
"stat": sleep["btwn_average"],
|
||||
"title": _("Average awake duration"),
|
||||
}
|
||||
)
|
||||
|
||||
weight = _weight_statistics(child)
|
||||
if weight:
|
||||
stats.append({
|
||||
'type': 'float',
|
||||
'stat': weight['change_weekly'],
|
||||
'title': _('Weight change per week')})
|
||||
stats.append(
|
||||
{
|
||||
"type": "float",
|
||||
"stat": weight["change_weekly"],
|
||||
"title": _("Weight change per week"),
|
||||
}
|
||||
)
|
||||
|
||||
height = _height_statistics(child)
|
||||
if height:
|
||||
stats.append({
|
||||
'type': 'float',
|
||||
'stat': height['change_weekly'],
|
||||
'title': _('Height change per week')})
|
||||
stats.append(
|
||||
{
|
||||
"type": "float",
|
||||
"stat": height["change_weekly"],
|
||||
"title": _("Height change per week"),
|
||||
}
|
||||
)
|
||||
|
||||
head_circumference = _head_circumference_statistics(child)
|
||||
if head_circumference:
|
||||
stats.append({
|
||||
'type': 'float',
|
||||
'stat': head_circumference['change_weekly'],
|
||||
'title': _('Head circumference change per week')})
|
||||
stats.append(
|
||||
{
|
||||
"type": "float",
|
||||
"stat": head_circumference["change_weekly"],
|
||||
"title": _("Head circumference change per week"),
|
||||
}
|
||||
)
|
||||
|
||||
bmi = _bmi_statistics(child)
|
||||
if bmi:
|
||||
stats.append({
|
||||
'type': 'float',
|
||||
'stat': bmi['change_weekly'],
|
||||
'title': _('BMI change per week')})
|
||||
stats.append(
|
||||
{
|
||||
"type": "float",
|
||||
"stat": bmi["change_weekly"],
|
||||
"title": _("BMI change per week"),
|
||||
}
|
||||
)
|
||||
|
||||
empty = len(stats) == 0
|
||||
|
||||
return {
|
||||
'stats': stats,
|
||||
'empty': empty,
|
||||
'hide_empty': _hide_empty(context)
|
||||
}
|
||||
return {"stats": stats, "empty": empty, "hide_empty": _hide_empty(context)}
|
||||
|
||||
|
||||
def _diaperchange_statistics(child):
|
||||
|
@ -349,23 +385,23 @@ def _diaperchange_statistics(child):
|
|||
:param child: an instance of the Child model.
|
||||
:returns: a dictionary of statistics.
|
||||
"""
|
||||
instances = models.DiaperChange.objects.filter(child=child) \
|
||||
.order_by('time')
|
||||
instances = models.DiaperChange.objects.filter(child=child).order_by("time")
|
||||
if len(instances) == 0:
|
||||
return False
|
||||
changes = {
|
||||
'btwn_total': timezone.timedelta(0),
|
||||
'btwn_count': instances.count() - 1,
|
||||
'btwn_average': 0.0}
|
||||
"btwn_total": timezone.timedelta(0),
|
||||
"btwn_count": instances.count() - 1,
|
||||
"btwn_average": 0.0,
|
||||
}
|
||||
last_instance = None
|
||||
|
||||
for instance in instances:
|
||||
if last_instance:
|
||||
changes['btwn_total'] += instance.time - last_instance.time
|
||||
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']
|
||||
if changes["btwn_count"] > 0:
|
||||
changes["btwn_average"] = changes["btwn_total"] / changes["btwn_count"]
|
||||
|
||||
return changes
|
||||
|
||||
|
@ -378,26 +414,26 @@ def _feeding_statistics(child):
|
|||
"""
|
||||
feedings = [
|
||||
{
|
||||
'start': timezone.now() - timezone.timedelta(days=3),
|
||||
'title': _('Feeding frequency (past 3 days)')
|
||||
"start": timezone.now() - timezone.timedelta(days=3),
|
||||
"title": _("Feeding frequency (past 3 days)"),
|
||||
},
|
||||
{
|
||||
'start': timezone.now() - timezone.timedelta(weeks=2),
|
||||
'title': _('Feeding frequency (past 2 weeks)')
|
||||
"start": timezone.now() - timezone.timedelta(weeks=2),
|
||||
"title": _("Feeding frequency (past 2 weeks)"),
|
||||
},
|
||||
{
|
||||
'start': timezone.make_aware(
|
||||
datetime.combine(date.min, time(0, 0))
|
||||
+ timezone.timedelta(days=1)),
|
||||
'title': _('Feeding frequency')
|
||||
}
|
||||
"start": timezone.make_aware(
|
||||
datetime.combine(date.min, time(0, 0)) + timezone.timedelta(days=1)
|
||||
),
|
||||
"title": _("Feeding frequency"),
|
||||
},
|
||||
]
|
||||
for timespan in feedings:
|
||||
timespan['btwn_total'] = timezone.timedelta(0)
|
||||
timespan['btwn_count'] = 0
|
||||
timespan['btwn_average'] = 0.0
|
||||
timespan["btwn_total"] = timezone.timedelta(0)
|
||||
timespan["btwn_count"] = 0
|
||||
timespan["btwn_average"] = 0.0
|
||||
|
||||
instances = models.Feeding.objects.filter(child=child).order_by('start')
|
||||
instances = models.Feeding.objects.filter(child=child).order_by("start")
|
||||
if len(instances) == 0:
|
||||
return False
|
||||
last_instance = None
|
||||
|
@ -405,16 +441,14 @@ def _feeding_statistics(child):
|
|||
for instance in instances:
|
||||
if last_instance:
|
||||
for timespan in feedings:
|
||||
if last_instance.start > timespan['start']:
|
||||
timespan['btwn_total'] += (instance.start
|
||||
- last_instance.end)
|
||||
timespan['btwn_count'] += 1
|
||||
if last_instance.start > timespan["start"]:
|
||||
timespan["btwn_total"] += instance.start - last_instance.end
|
||||
timespan["btwn_count"] += 1
|
||||
last_instance = instance
|
||||
|
||||
for timespan in feedings:
|
||||
if timespan['btwn_count'] > 0:
|
||||
timespan['btwn_average'] = \
|
||||
timespan['btwn_total'] / timespan['btwn_count']
|
||||
if timespan["btwn_count"] > 0:
|
||||
timespan["btwn_average"] = timespan["btwn_total"] / timespan["btwn_count"]
|
||||
return feedings
|
||||
|
||||
|
||||
|
@ -424,21 +458,26 @@ def _nap_statistics(child):
|
|||
:param child: an instance of the Child model.
|
||||
:returns: a dictionary of statistics.
|
||||
"""
|
||||
instances = models.Sleep.naps.filter(child=child).order_by('start')
|
||||
instances = models.Sleep.naps.filter(child=child).order_by("start")
|
||||
if len(instances) == 0:
|
||||
return False
|
||||
naps = {
|
||||
'total': instances.aggregate(Sum('duration'))['duration__sum'],
|
||||
'count': instances.count(),
|
||||
'average': 0.0,
|
||||
'avg_per_day': 0.0}
|
||||
if naps['count'] > 0:
|
||||
naps['average'] = naps['total'] / naps['count']
|
||||
"total": instances.aggregate(Sum("duration"))["duration__sum"],
|
||||
"count": instances.count(),
|
||||
"average": 0.0,
|
||||
"avg_per_day": 0.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']
|
||||
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
|
||||
|
||||
|
@ -449,28 +488,29 @@ def _sleep_statistics(child):
|
|||
:param child: an instance of the Child model.
|
||||
:returns: a dictionary of statistics.
|
||||
"""
|
||||
instances = models.Sleep.objects.filter(child=child).order_by('start')
|
||||
instances = models.Sleep.objects.filter(child=child).order_by("start")
|
||||
if len(instances) == 0:
|
||||
return False
|
||||
|
||||
sleep = {
|
||||
'total': instances.aggregate(Sum('duration'))['duration__sum'],
|
||||
'count': instances.count(),
|
||||
'average': 0.0,
|
||||
'btwn_total': timezone.timedelta(0),
|
||||
'btwn_count': instances.count() - 1,
|
||||
'btwn_average': 0.0}
|
||||
"total": instances.aggregate(Sum("duration"))["duration__sum"],
|
||||
"count": instances.count(),
|
||||
"average": 0.0,
|
||||
"btwn_total": timezone.timedelta(0),
|
||||
"btwn_count": instances.count() - 1,
|
||||
"btwn_average": 0.0,
|
||||
}
|
||||
|
||||
last_instance = None
|
||||
for instance in instances:
|
||||
if last_instance:
|
||||
sleep['btwn_total'] += instance.start - last_instance.end
|
||||
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']
|
||||
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
|
||||
|
||||
|
@ -481,9 +521,9 @@ def _weight_statistics(child):
|
|||
:param child: an instance of the Child model.
|
||||
:returns: a dictionary of statistics.
|
||||
"""
|
||||
weight = {'change_weekly': 0.0}
|
||||
weight = {"change_weekly": 0.0}
|
||||
|
||||
instances = models.Weight.objects.filter(child=child).order_by('-date')
|
||||
instances = models.Weight.objects.filter(child=child).order_by("-date")
|
||||
if len(instances) == 0:
|
||||
return False
|
||||
|
||||
|
@ -492,8 +532,8 @@ def _weight_statistics(child):
|
|||
|
||||
if newest != oldest:
|
||||
weight_change = newest.weight - oldest.weight
|
||||
weeks = (newest.date - oldest.date).days/7
|
||||
weight['change_weekly'] = weight_change/weeks
|
||||
weeks = (newest.date - oldest.date).days / 7
|
||||
weight["change_weekly"] = weight_change / weeks
|
||||
|
||||
return weight
|
||||
|
||||
|
@ -504,9 +544,9 @@ def _height_statistics(child):
|
|||
:param child: an instance of the Child model.
|
||||
:returns: a dictionary of statistics.
|
||||
"""
|
||||
height = {'change_weekly': 0.0}
|
||||
height = {"change_weekly": 0.0}
|
||||
|
||||
instances = models.Height.objects.filter(child=child).order_by('-date')
|
||||
instances = models.Height.objects.filter(child=child).order_by("-date")
|
||||
if len(instances) == 0:
|
||||
return False
|
||||
|
||||
|
@ -515,8 +555,8 @@ def _height_statistics(child):
|
|||
|
||||
if newest != oldest:
|
||||
height_change = newest.height - oldest.height
|
||||
weeks = (newest.date - oldest.date).days/7
|
||||
height['change_weekly'] = height_change/weeks
|
||||
weeks = (newest.date - oldest.date).days / 7
|
||||
height["change_weekly"] = height_change / weeks
|
||||
|
||||
return height
|
||||
|
||||
|
@ -527,11 +567,9 @@ def _head_circumference_statistics(child):
|
|||
:param child: an instance of the Child model.
|
||||
:returns: a dictionary of statistics.
|
||||
"""
|
||||
head_circumference = {'change_weekly': 0.0}
|
||||
head_circumference = {"change_weekly": 0.0}
|
||||
|
||||
instances = models.HeadCircumference.objects.filter(
|
||||
child=child
|
||||
).order_by('-date')
|
||||
instances = models.HeadCircumference.objects.filter(child=child).order_by("-date")
|
||||
if len(instances) == 0:
|
||||
return False
|
||||
|
||||
|
@ -540,8 +578,8 @@ def _head_circumference_statistics(child):
|
|||
|
||||
if newest != oldest:
|
||||
hc_change = newest.head_circumference - oldest.head_circumference
|
||||
weeks = (newest.date - oldest.date).days/7
|
||||
head_circumference['change_weekly'] = hc_change/weeks
|
||||
weeks = (newest.date - oldest.date).days / 7
|
||||
head_circumference["change_weekly"] = hc_change / weeks
|
||||
|
||||
return head_circumference
|
||||
|
||||
|
@ -552,9 +590,9 @@ def _bmi_statistics(child):
|
|||
:param child: an instance of the Child model.
|
||||
:returns: a dictionary of statistics.
|
||||
"""
|
||||
bmi = {'change_weekly': 0.0}
|
||||
bmi = {"change_weekly": 0.0}
|
||||
|
||||
instances = models.BMI.objects.filter(child=child).order_by('-date')
|
||||
instances = models.BMI.objects.filter(child=child).order_by("-date")
|
||||
if len(instances) == 0:
|
||||
return False
|
||||
|
||||
|
@ -563,13 +601,13 @@ def _bmi_statistics(child):
|
|||
|
||||
if newest != oldest:
|
||||
bmi_change = newest.bmi - oldest.bmi
|
||||
weeks = (newest.date - oldest.date).days/7
|
||||
bmi['change_weekly'] = bmi_change/weeks
|
||||
weeks = (newest.date - oldest.date).days / 7
|
||||
bmi["change_weekly"] = bmi_change / weeks
|
||||
|
||||
return bmi
|
||||
|
||||
|
||||
@register.inclusion_tag('cards/timer_list.html', takes_context=True)
|
||||
@register.inclusion_tag("cards/timer_list.html", takes_context=True)
|
||||
def card_timer_list(context, child=None):
|
||||
"""
|
||||
Filters for currently active Timer instances, optionally by child.
|
||||
|
@ -579,42 +617,44 @@ 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)
|
||||
).order_by('-start')
|
||||
Q(active=True), Q(child=child) | Q(child=None)
|
||||
).order_by("-start")
|
||||
else:
|
||||
instances = models.Timer.objects.filter(active=True).order_by('-start')
|
||||
instances = models.Timer.objects.filter(active=True).order_by("-start")
|
||||
empty = len(instances) == 0
|
||||
|
||||
return {
|
||||
'type': 'timer',
|
||||
'instances': list(instances),
|
||||
'empty': empty,
|
||||
'hide_empty': _hide_empty(context)
|
||||
"type": "timer",
|
||||
"instances": list(instances),
|
||||
"empty": empty,
|
||||
"hide_empty": _hide_empty(context),
|
||||
}
|
||||
|
||||
|
||||
@register.inclusion_tag('cards/tummytime_last.html', takes_context=True)
|
||||
@register.inclusion_tag("cards/tummytime_last.html", takes_context=True)
|
||||
def card_tummytime_last(context, child):
|
||||
"""
|
||||
Filters the most recent tummy time.
|
||||
:param child: an instance of the Child model.
|
||||
:returns: a dictionary with the most recent Tummy Time instance.
|
||||
"""
|
||||
instance = models.TummyTime.objects.filter(child=child) \
|
||||
.filter(**_filter_data_age(context)) \
|
||||
.order_by('-end').first()
|
||||
instance = (
|
||||
models.TummyTime.objects.filter(child=child)
|
||||
.filter(**_filter_data_age(context))
|
||||
.order_by("-end")
|
||||
.first()
|
||||
)
|
||||
empty = not instance
|
||||
|
||||
return {
|
||||
'type': 'tummytime',
|
||||
'tummytime': instance,
|
||||
'empty': empty,
|
||||
'hide_empty': _hide_empty(context)
|
||||
"type": "tummytime",
|
||||
"tummytime": instance,
|
||||
"empty": empty,
|
||||
"hide_empty": _hide_empty(context),
|
||||
}
|
||||
|
||||
|
||||
@register.inclusion_tag('cards/tummytime_day.html', takes_context=True)
|
||||
@register.inclusion_tag("cards/tummytime_day.html", takes_context=True)
|
||||
def card_tummytime_day(context, child, date=None):
|
||||
"""
|
||||
Filters Tummy Time instances and generates statistics for a specific date.
|
||||
|
@ -625,22 +665,19 @@ def card_tummytime_day(context, child, date=None):
|
|||
if not date:
|
||||
date = timezone.localtime().date()
|
||||
instances = models.TummyTime.objects.filter(
|
||||
child=child, end__year=date.year, end__month=date.month,
|
||||
end__day=date.day).order_by('-end')
|
||||
child=child, end__year=date.year, end__month=date.month, end__day=date.day
|
||||
).order_by("-end")
|
||||
empty = len(instances) == 0
|
||||
|
||||
stats = {
|
||||
'total': timezone.timedelta(seconds=0),
|
||||
'count': instances.count()
|
||||
}
|
||||
stats = {"total": timezone.timedelta(seconds=0), "count": instances.count()}
|
||||
for instance in instances:
|
||||
stats['total'] += timezone.timedelta(seconds=instance.duration.seconds)
|
||||
stats["total"] += timezone.timedelta(seconds=instance.duration.seconds)
|
||||
|
||||
return {
|
||||
'type': 'tummytime',
|
||||
'stats': stats,
|
||||
'instances': instances,
|
||||
'last': instances.first(),
|
||||
'empty': empty,
|
||||
'hide_empty': _hide_empty(context)
|
||||
"type": "tummytime",
|
||||
"stats": stats,
|
||||
"instances": instances,
|
||||
"last": instances.first(),
|
||||
"empty": empty,
|
||||
"hide_empty": _hide_empty(context),
|
||||
}
|
||||
|
|
|
@ -18,275 +18,257 @@ class MockUserRequest:
|
|||
|
||||
|
||||
class TemplateTagsTestCase(TestCase):
|
||||
fixtures = ['tests.json']
|
||||
fixtures = ["tests.json"]
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TemplateTagsTestCase, cls).setUpClass()
|
||||
cls.child = models.Child.objects.first()
|
||||
cls.context = {'request': MockUserRequest(User.objects.first())}
|
||||
cls.context = {"request": MockUserRequest(User.objects.first())}
|
||||
|
||||
# Ensure timezone matches the one defined by fixtures.
|
||||
user_timezone = Settings.objects.first().timezone
|
||||
timezone.activate(pytz.timezone(user_timezone))
|
||||
|
||||
# Test file data uses a basis date of 2017-11-18.
|
||||
date = timezone.localtime().strptime('2017-11-18', '%Y-%m-%d')
|
||||
date = timezone.localtime().strptime("2017-11-18", "%Y-%m-%d")
|
||||
cls.date = timezone.make_aware(date)
|
||||
|
||||
def test_hide_empty(self):
|
||||
request = MockUserRequest(User.objects.first())
|
||||
request.user.settings.dashboard_hide_empty = True
|
||||
context = {'request': request}
|
||||
context = {"request": request}
|
||||
hide_empty = cards._hide_empty(context)
|
||||
self.assertTrue(hide_empty)
|
||||
|
||||
def test_filter_data_age_none(self):
|
||||
request = MockUserRequest(User.objects.first())
|
||||
request.user.settings.dashboard_hide_age = None
|
||||
context = {'request': request}
|
||||
context = {"request": request}
|
||||
filter_data_age = cards._filter_data_age(context)
|
||||
self.assertFalse(len(filter_data_age))
|
||||
|
||||
@mock.patch('dashboard.templatetags.cards.timezone')
|
||||
@mock.patch("dashboard.templatetags.cards.timezone")
|
||||
def test_filter_data_age_one_day(self, mocked_timezone):
|
||||
request = MockUserRequest(User.objects.first())
|
||||
request.user.settings.dashboard_hide_age = timezone.timedelta(days=1)
|
||||
context = {'request': request}
|
||||
mocked_timezone.localtime.return_value = \
|
||||
timezone.localtime().strptime('2017-11-18', '%Y-%m-%d')
|
||||
context = {"request": request}
|
||||
mocked_timezone.localtime.return_value = timezone.localtime().strptime(
|
||||
"2017-11-18", "%Y-%m-%d"
|
||||
)
|
||||
|
||||
filter_data_age = cards._filter_data_age(context, keyword="time")
|
||||
|
||||
self.assertIn("time__range", filter_data_age)
|
||||
self.assertEqual(
|
||||
filter_data_age["time__range"][0],
|
||||
timezone.localtime().strptime('2017-11-17', '%Y-%m-%d'))
|
||||
timezone.localtime().strptime("2017-11-17", "%Y-%m-%d"),
|
||||
)
|
||||
self.assertEqual(
|
||||
filter_data_age["time__range"][1],
|
||||
timezone.localtime().strptime('2017-11-18', '%Y-%m-%d'))
|
||||
timezone.localtime().strptime("2017-11-18", "%Y-%m-%d"),
|
||||
)
|
||||
|
||||
def test_card_diaperchange_last(self):
|
||||
data = cards.card_diaperchange_last(self.context, self.child)
|
||||
self.assertEqual(data['type'], 'diaperchange')
|
||||
self.assertFalse(data['empty'])
|
||||
self.assertFalse(data['hide_empty'])
|
||||
self.assertIsInstance(data['change'], models.DiaperChange)
|
||||
self.assertEqual(data['change'], models.DiaperChange.objects.first())
|
||||
self.assertEqual(data["type"], "diaperchange")
|
||||
self.assertFalse(data["empty"])
|
||||
self.assertFalse(data["hide_empty"])
|
||||
self.assertIsInstance(data["change"], models.DiaperChange)
|
||||
self.assertEqual(data["change"], models.DiaperChange.objects.first())
|
||||
|
||||
@mock.patch('dashboard.templatetags.cards.timezone')
|
||||
@mock.patch("dashboard.templatetags.cards.timezone")
|
||||
def test_card_diaperchange_last_filter_age(self, mocked_timezone):
|
||||
request = MockUserRequest(User.objects.first())
|
||||
request.user.settings.dashboard_hide_age = timezone.timedelta(days=1)
|
||||
context = {'request': request}
|
||||
time = timezone.localtime().strptime('2017-11-10', '%Y-%m-%d')
|
||||
context = {"request": request}
|
||||
time = timezone.localtime().strptime("2017-11-10", "%Y-%m-%d")
|
||||
mocked_timezone.localtime.return_value = timezone.make_aware(time)
|
||||
|
||||
data = cards.card_diaperchange_last(context, self.child)
|
||||
self.assertTrue(data['empty'])
|
||||
self.assertTrue(data["empty"])
|
||||
|
||||
def test_card_diaperchange_types(self):
|
||||
data = cards.card_diaperchange_types(
|
||||
self.context,
|
||||
self.child,
|
||||
self.date)
|
||||
self.assertEqual(data['type'], 'diaperchange')
|
||||
data = cards.card_diaperchange_types(self.context, self.child, self.date)
|
||||
self.assertEqual(data["type"], "diaperchange")
|
||||
stats = {
|
||||
0: {'wet_pct': 50.0, 'solid_pct': 50.0, 'solid': 1, 'wet': 1},
|
||||
1: {'wet_pct': 0.0, 'solid_pct': 100.0, 'solid': 2, 'wet': 0},
|
||||
2: {'wet_pct': 100.0, 'solid_pct': 0.0, 'solid': 0, 'wet': 2},
|
||||
3: {'wet_pct': 75.0, 'solid_pct': 25.0, 'solid': 1, 'wet': 3},
|
||||
4: {'wet_pct': 100.0, 'solid_pct': 0.0, 'solid': 0, 'wet': 1},
|
||||
5: {'wet_pct': 100.0, 'solid_pct': 0.0, 'solid': 0, 'wet': 2},
|
||||
6: {'wet_pct': 100.0, 'solid_pct': 0.0, 'solid': 0, 'wet': 1}
|
||||
0: {"wet_pct": 50.0, "solid_pct": 50.0, "solid": 1, "wet": 1},
|
||||
1: {"wet_pct": 0.0, "solid_pct": 100.0, "solid": 2, "wet": 0},
|
||||
2: {"wet_pct": 100.0, "solid_pct": 0.0, "solid": 0, "wet": 2},
|
||||
3: {"wet_pct": 75.0, "solid_pct": 25.0, "solid": 1, "wet": 3},
|
||||
4: {"wet_pct": 100.0, "solid_pct": 0.0, "solid": 0, "wet": 1},
|
||||
5: {"wet_pct": 100.0, "solid_pct": 0.0, "solid": 0, "wet": 2},
|
||||
6: {"wet_pct": 100.0, "solid_pct": 0.0, "solid": 0, "wet": 1},
|
||||
}
|
||||
self.assertEqual(data['stats'], stats)
|
||||
self.assertEqual(data["stats"], stats)
|
||||
|
||||
def test_card_feeding_day(self):
|
||||
data = cards.card_feeding_day(self.context, self.child, self.date)
|
||||
self.assertEqual(data['type'], 'feeding')
|
||||
self.assertFalse(data['empty'])
|
||||
self.assertFalse(data['hide_empty'])
|
||||
self.assertEqual(data['total'], 2.5)
|
||||
self.assertEqual(data['count'], 3)
|
||||
self.assertEqual(data["type"], "feeding")
|
||||
self.assertFalse(data["empty"])
|
||||
self.assertFalse(data["hide_empty"])
|
||||
self.assertEqual(data["total"], 2.5)
|
||||
self.assertEqual(data["count"], 3)
|
||||
|
||||
def test_card_feeding_last(self):
|
||||
data = cards.card_feeding_last(self.context, self.child)
|
||||
self.assertEqual(data['type'], 'feeding')
|
||||
self.assertFalse(data['empty'])
|
||||
self.assertFalse(data['hide_empty'])
|
||||
self.assertIsInstance(data['feeding'], models.Feeding)
|
||||
self.assertEqual(data['feeding'], models.Feeding.objects.first())
|
||||
self.assertEqual(data["type"], "feeding")
|
||||
self.assertFalse(data["empty"])
|
||||
self.assertFalse(data["hide_empty"])
|
||||
self.assertIsInstance(data["feeding"], models.Feeding)
|
||||
self.assertEqual(data["feeding"], models.Feeding.objects.first())
|
||||
|
||||
def test_card_feeding_last_method(self):
|
||||
data = cards.card_feeding_last_method(self.context, self.child)
|
||||
self.assertEqual(data['type'], 'feeding')
|
||||
self.assertFalse(data['empty'])
|
||||
self.assertFalse(data['hide_empty'])
|
||||
self.assertEqual(len(data['feedings']), 3)
|
||||
for feeding in data['feedings']:
|
||||
self.assertEqual(data["type"], "feeding")
|
||||
self.assertFalse(data["empty"])
|
||||
self.assertFalse(data["hide_empty"])
|
||||
self.assertEqual(len(data["feedings"]), 3)
|
||||
for feeding in data["feedings"]:
|
||||
self.assertIsInstance(feeding, models.Feeding)
|
||||
self.assertEqual(
|
||||
data['feedings'][2].method,
|
||||
models.Feeding.objects.first().method)
|
||||
data["feedings"][2].method, models.Feeding.objects.first().method
|
||||
)
|
||||
|
||||
def test_card_sleep_last(self):
|
||||
data = cards.card_sleep_last(self.context, self.child)
|
||||
self.assertEqual(data['type'], 'sleep')
|
||||
self.assertFalse(data['empty'])
|
||||
self.assertFalse(data['hide_empty'])
|
||||
self.assertIsInstance(data['sleep'], models.Sleep)
|
||||
self.assertEqual(data['sleep'], models.Sleep.objects.first())
|
||||
self.assertEqual(data["type"], "sleep")
|
||||
self.assertFalse(data["empty"])
|
||||
self.assertFalse(data["hide_empty"])
|
||||
self.assertIsInstance(data["sleep"], models.Sleep)
|
||||
self.assertEqual(data["sleep"], models.Sleep.objects.first())
|
||||
|
||||
def test_card_sleep_last_empty(self):
|
||||
models.Sleep.objects.all().delete()
|
||||
data = cards.card_sleep_last(self.context, self.child)
|
||||
self.assertEqual(data['type'], 'sleep')
|
||||
self.assertTrue(data['empty'])
|
||||
self.assertFalse(data['hide_empty'])
|
||||
self.assertEqual(data["type"], "sleep")
|
||||
self.assertTrue(data["empty"])
|
||||
self.assertFalse(data["hide_empty"])
|
||||
|
||||
def test_card_sleep_day(self):
|
||||
data = cards.card_sleep_day(self.context, self.child, self.date)
|
||||
self.assertEqual(data['type'], 'sleep')
|
||||
self.assertFalse(data['empty'])
|
||||
self.assertFalse(data['hide_empty'])
|
||||
self.assertEqual(data['total'], timezone.timedelta(2, 7200))
|
||||
self.assertEqual(data['count'], 4)
|
||||
self.assertEqual(data["type"], "sleep")
|
||||
self.assertFalse(data["empty"])
|
||||
self.assertFalse(data["hide_empty"])
|
||||
self.assertEqual(data["total"], timezone.timedelta(2, 7200))
|
||||
self.assertEqual(data["count"], 4)
|
||||
|
||||
def test_card_sleep_naps_day(self):
|
||||
data = cards.card_sleep_naps_day(self.context, self.child, self.date)
|
||||
self.assertEqual(data['type'], 'sleep')
|
||||
self.assertFalse(data['empty'])
|
||||
self.assertFalse(data['hide_empty'])
|
||||
self.assertEqual(data['total'], timezone.timedelta(0, 9000))
|
||||
self.assertEqual(data['count'], 2)
|
||||
self.assertEqual(data["type"], "sleep")
|
||||
self.assertFalse(data["empty"])
|
||||
self.assertFalse(data["hide_empty"])
|
||||
self.assertEqual(data["total"], timezone.timedelta(0, 9000))
|
||||
self.assertEqual(data["count"], 2)
|
||||
|
||||
def test_card_statistics(self):
|
||||
data = cards.card_statistics(self.context, self.child)
|
||||
stats = [
|
||||
{
|
||||
'title': 'Diaper change frequency',
|
||||
'stat': timezone.timedelta(0, 44228, 571429),
|
||||
'type': 'duration'
|
||||
"title": "Diaper change frequency",
|
||||
"stat": timezone.timedelta(0, 44228, 571429),
|
||||
"type": "duration",
|
||||
},
|
||||
# Statistics date basis is not particularly strong to these feeding
|
||||
# examples.
|
||||
# TODO: Improve testing of feeding frequency statistics.
|
||||
{
|
||||
'type': 'duration',
|
||||
'stat': 0.0,
|
||||
'title': 'Feeding frequency (past 3 days)'
|
||||
"type": "duration",
|
||||
"stat": 0.0,
|
||||
"title": "Feeding frequency (past 3 days)",
|
||||
},
|
||||
{
|
||||
'type': 'duration',
|
||||
'stat': 0.0,
|
||||
'title': 'Feeding frequency (past 2 weeks)'},
|
||||
{
|
||||
'type': 'duration',
|
||||
'stat': timezone.timedelta(0, 7200),
|
||||
'title': 'Feeding frequency'
|
||||
"type": "duration",
|
||||
"stat": 0.0,
|
||||
"title": "Feeding frequency (past 2 weeks)",
|
||||
},
|
||||
{
|
||||
'title': 'Average nap duration',
|
||||
'stat': timezone.timedelta(0, 4500),
|
||||
'type': 'duration'
|
||||
"type": "duration",
|
||||
"stat": timezone.timedelta(0, 7200),
|
||||
"title": "Feeding frequency",
|
||||
},
|
||||
{
|
||||
'title': 'Average naps per day',
|
||||
'stat': 2.0,
|
||||
'type': 'float'
|
||||
"title": "Average nap duration",
|
||||
"stat": timezone.timedelta(0, 4500),
|
||||
"type": "duration",
|
||||
},
|
||||
{"title": "Average naps per day", "stat": 2.0, "type": "float"},
|
||||
{
|
||||
"title": "Average sleep duration",
|
||||
"stat": timezone.timedelta(0, 6750),
|
||||
"type": "duration",
|
||||
},
|
||||
{
|
||||
'title': 'Average sleep duration',
|
||||
'stat': timezone.timedelta(0, 6750),
|
||||
'type': 'duration'
|
||||
"title": "Average awake duration",
|
||||
"stat": timezone.timedelta(0, 19200),
|
||||
"type": "duration",
|
||||
},
|
||||
{"title": "Weight change per week", "stat": 1.0, "type": "float"},
|
||||
{"title": "Height change per week", "stat": 1.0, "type": "float"},
|
||||
{
|
||||
'title': 'Average awake duration',
|
||||
'stat': timezone.timedelta(0, 19200),
|
||||
'type': 'duration'
|
||||
"title": "Head circumference change per week",
|
||||
"stat": 1.0,
|
||||
"type": "float",
|
||||
},
|
||||
{
|
||||
'title': 'Weight change per week',
|
||||
'stat': 1.0, 'type':
|
||||
'float'
|
||||
},
|
||||
{
|
||||
'title': 'Height change per week',
|
||||
'stat': 1.0, 'type':
|
||||
'float'
|
||||
},
|
||||
{
|
||||
'title': 'Head circumference change per week',
|
||||
'stat': 1.0, 'type':
|
||||
'float'
|
||||
},
|
||||
{
|
||||
'title': 'BMI change per week',
|
||||
'stat': 1.0, 'type':
|
||||
'float'
|
||||
}
|
||||
{"title": "BMI change per week", "stat": 1.0, "type": "float"},
|
||||
]
|
||||
|
||||
self.assertEqual(data['stats'], stats)
|
||||
self.assertFalse(data['empty'])
|
||||
self.assertFalse(data['hide_empty'])
|
||||
self.assertEqual(data["stats"], stats)
|
||||
self.assertFalse(data["empty"])
|
||||
self.assertFalse(data["hide_empty"])
|
||||
|
||||
def test_card_timer_list(self):
|
||||
user = User.objects.first()
|
||||
child = models.Child.objects.first()
|
||||
child_two = models.Child.objects.create(
|
||||
first_name='Child',
|
||||
last_name='Two',
|
||||
birth_date=timezone.localdate()
|
||||
first_name="Child", last_name="Two", birth_date=timezone.localdate()
|
||||
)
|
||||
timers = {
|
||||
'no_child': models.Timer.objects.create(
|
||||
user=user,
|
||||
start=timezone.localtime() - timezone.timedelta(hours=3)
|
||||
"no_child": models.Timer.objects.create(
|
||||
user=user, start=timezone.localtime() - timezone.timedelta(hours=3)
|
||||
),
|
||||
'child': models.Timer.objects.create(
|
||||
"child": models.Timer.objects.create(
|
||||
user=user,
|
||||
child=child,
|
||||
start=timezone.localtime() - timezone.timedelta(hours=2)
|
||||
start=timezone.localtime() - timezone.timedelta(hours=2),
|
||||
),
|
||||
'child_two': models.Timer.objects.create(
|
||||
"child_two": models.Timer.objects.create(
|
||||
user=user,
|
||||
child=child_two,
|
||||
start=timezone.localtime() - timezone.timedelta(hours=1)
|
||||
start=timezone.localtime() - timezone.timedelta(hours=1),
|
||||
),
|
||||
}
|
||||
|
||||
data = cards.card_timer_list(self.context)
|
||||
self.assertIsInstance(data['instances'][0], models.Timer)
|
||||
self.assertEqual(len(data['instances']), 3)
|
||||
self.assertIsInstance(data["instances"][0], models.Timer)
|
||||
self.assertEqual(len(data["instances"]), 3)
|
||||
|
||||
data = cards.card_timer_list(self.context, child)
|
||||
self.assertIsInstance(data['instances'][0], models.Timer)
|
||||
self.assertTrue(timers['no_child'] in data['instances'])
|
||||
self.assertTrue(timers['child'] in data['instances'])
|
||||
self.assertFalse(timers['child_two'] in data['instances'])
|
||||
self.assertIsInstance(data["instances"][0], models.Timer)
|
||||
self.assertTrue(timers["no_child"] in data["instances"])
|
||||
self.assertTrue(timers["child"] in data["instances"])
|
||||
self.assertFalse(timers["child_two"] in data["instances"])
|
||||
|
||||
data = cards.card_timer_list(self.context, child_two)
|
||||
self.assertIsInstance(data['instances'][0], models.Timer)
|
||||
self.assertTrue(timers['no_child'] in data['instances'])
|
||||
self.assertTrue(timers['child_two'] in data['instances'])
|
||||
self.assertFalse(timers['child'] in data['instances'])
|
||||
self.assertIsInstance(data["instances"][0], models.Timer)
|
||||
self.assertTrue(timers["no_child"] in data["instances"])
|
||||
self.assertTrue(timers["child_two"] in data["instances"])
|
||||
self.assertFalse(timers["child"] in data["instances"])
|
||||
|
||||
def test_card_tummytime_last(self):
|
||||
data = cards.card_tummytime_last(self.context, self.child)
|
||||
self.assertEqual(data['type'], 'tummytime')
|
||||
self.assertFalse(data['empty'])
|
||||
self.assertFalse(data['hide_empty'])
|
||||
self.assertIsInstance(data['tummytime'], models.TummyTime)
|
||||
self.assertEqual(data['tummytime'], models.TummyTime.objects.first())
|
||||
self.assertEqual(data["type"], "tummytime")
|
||||
self.assertFalse(data["empty"])
|
||||
self.assertFalse(data["hide_empty"])
|
||||
self.assertIsInstance(data["tummytime"], models.TummyTime)
|
||||
self.assertEqual(data["tummytime"], models.TummyTime.objects.first())
|
||||
|
||||
def test_card_tummytime_day(self):
|
||||
data = cards.card_tummytime_day(self.context, self.child, self.date)
|
||||
self.assertEqual(data['type'], 'tummytime')
|
||||
self.assertFalse(data['empty'])
|
||||
self.assertFalse(data['hide_empty'])
|
||||
self.assertIsInstance(data['instances'].first(), models.TummyTime)
|
||||
self.assertIsInstance(data['last'], models.TummyTime)
|
||||
stats = {'count': 3, 'total': timezone.timedelta(0, 300)}
|
||||
self.assertEqual(data['stats'], stats)
|
||||
self.assertEqual(data["type"], "tummytime")
|
||||
self.assertFalse(data["empty"])
|
||||
self.assertFalse(data["hide_empty"])
|
||||
self.assertIsInstance(data["instances"].first(), models.TummyTime)
|
||||
self.assertIsInstance(data["last"], models.TummyTime)
|
||||
stats = {"count": 3, "total": timezone.timedelta(0, 300)}
|
||||
self.assertEqual(data["stats"], stats)
|
||||
|
|
|
@ -14,42 +14,37 @@ class ViewsTestCase(TestCase):
|
|||
def setUpClass(cls):
|
||||
super(ViewsTestCase, cls).setUpClass()
|
||||
fake = Factory.create()
|
||||
call_command('migrate', verbosity=0)
|
||||
call_command("migrate", verbosity=0)
|
||||
|
||||
cls.c = HttpClient()
|
||||
|
||||
fake_user = fake.simple_profile()
|
||||
cls.credentials = {
|
||||
'username': fake_user['username'],
|
||||
'password': fake.password()
|
||||
"username": fake_user["username"],
|
||||
"password": fake.password(),
|
||||
}
|
||||
cls.user = User.objects.create_user(
|
||||
is_superuser=True, **cls.credentials)
|
||||
cls.user = User.objects.create_user(is_superuser=True, **cls.credentials)
|
||||
|
||||
cls.c.login(**cls.credentials)
|
||||
|
||||
def test_dashboard_views(self):
|
||||
page = self.c.get('/dashboard/')
|
||||
self.assertEqual(page.url, '/welcome/')
|
||||
page = self.c.get("/dashboard/")
|
||||
self.assertEqual(page.url, "/welcome/")
|
||||
|
||||
call_command('fake', verbosity=0, children=1, days=1)
|
||||
call_command("fake", verbosity=0, children=1, days=1)
|
||||
child = Child.objects.first()
|
||||
page = self.c.get('/dashboard/')
|
||||
self.assertEqual(
|
||||
page.url, '/children/{}/dashboard/'.format(child.slug))
|
||||
page = self.c.get("/dashboard/")
|
||||
self.assertEqual(page.url, "/children/{}/dashboard/".format(child.slug))
|
||||
|
||||
page = self.c.get('/dashboard/')
|
||||
self.assertEqual(
|
||||
page.url, '/children/{}/dashboard/'.format(child.slug))
|
||||
page = self.c.get("/dashboard/")
|
||||
self.assertEqual(page.url, "/children/{}/dashboard/".format(child.slug))
|
||||
# Test the actual child dashboard (including cards).
|
||||
# TODO: Test cards more granularly.
|
||||
page = self.c.get('/children/{}/dashboard/'.format(child.slug))
|
||||
page = self.c.get("/children/{}/dashboard/".format(child.slug))
|
||||
self.assertEqual(page.status_code, 200)
|
||||
|
||||
Child.objects.create(
|
||||
first_name='Second',
|
||||
last_name='Child',
|
||||
birth_date='2000-01-01'
|
||||
first_name="Second", last_name="Child", birth_date="2000-01-01"
|
||||
)
|
||||
page = self.c.get('/dashboard/')
|
||||
page = self.c.get("/dashboard/")
|
||||
self.assertEqual(page.status_code, 200)
|
||||
|
|
|
@ -3,13 +3,13 @@ from django.urls import path
|
|||
|
||||
from . import views
|
||||
|
||||
app_name = 'dashboard'
|
||||
app_name = "dashboard"
|
||||
|
||||
urlpatterns = [
|
||||
path('dashboard/', views.Dashboard.as_view(), name='dashboard'),
|
||||
path("dashboard/", views.Dashboard.as_view(), name="dashboard"),
|
||||
path(
|
||||
'children/<str:slug>/dashboard/',
|
||||
"children/<str:slug>/dashboard/",
|
||||
views.ChildDashboard.as_view(),
|
||||
name='dashboard-child'
|
||||
name="dashboard-child",
|
||||
),
|
||||
]
|
||||
|
|
|
@ -10,30 +10,28 @@ from core.models import Child
|
|||
|
||||
class Dashboard(LoginRequiredMixin, TemplateView):
|
||||
# TODO: Use .card-deck in this template once BS4 is finalized.
|
||||
template_name = 'dashboard/dashboard.html'
|
||||
template_name = "dashboard/dashboard.html"
|
||||
|
||||
# Show the overall dashboard or a child dashboard if one Child instance.
|
||||
def get(self, request, *args, **kwargs):
|
||||
children = Child.objects.count()
|
||||
if children == 0:
|
||||
return HttpResponseRedirect(reverse('babybuddy:welcome'))
|
||||
return HttpResponseRedirect(reverse("babybuddy:welcome"))
|
||||
elif children == 1:
|
||||
return HttpResponseRedirect(
|
||||
reverse(
|
||||
'dashboard:dashboard-child',
|
||||
args={Child.objects.first().slug}
|
||||
)
|
||||
reverse("dashboard:dashboard-child", args={Child.objects.first().slug})
|
||||
)
|
||||
return super(Dashboard, self).get(request, *args, **kwargs)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(Dashboard, self).get_context_data(**kwargs)
|
||||
context['objects'] = Child.objects.all() \
|
||||
.order_by('last_name', 'first_name', 'id')
|
||||
context["objects"] = Child.objects.all().order_by(
|
||||
"last_name", "first_name", "id"
|
||||
)
|
||||
return context
|
||||
|
||||
|
||||
class ChildDashboard(PermissionRequiredMixin, DetailView):
|
||||
model = Child
|
||||
permission_required = ('core.view_child',)
|
||||
template_name = 'dashboard/child.html'
|
||||
permission_required = ("core.view_child",)
|
||||
template_name = "dashboard/child.html"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Server mechanics
|
||||
bind = '0.0.0.0:8000'
|
||||
bind = "0.0.0.0:8000"
|
||||
backlog = 2048
|
||||
daemon = False
|
||||
pidfile = None
|
||||
|
@ -10,9 +10,9 @@ tmp_upload_dir = None
|
|||
proc_name = None
|
||||
|
||||
# Logging
|
||||
errorlog = '-'
|
||||
loglevel = 'info'
|
||||
accesslog = '-'
|
||||
errorlog = "-"
|
||||
loglevel = "info"
|
||||
accesslog = "-"
|
||||
access_log_format = '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"'
|
||||
|
||||
#
|
||||
|
@ -65,7 +65,7 @@ access_log_format = '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"
|
|||
#
|
||||
|
||||
workers = 1
|
||||
worker_class = 'sync'
|
||||
worker_class = "sync"
|
||||
worker_connections = 1000
|
||||
timeout = 30
|
||||
keepalive = 2
|
||||
|
@ -112,14 +112,13 @@ def worker_int(worker):
|
|||
|
||||
# get traceback info
|
||||
import threading, sys, traceback
|
||||
|
||||
id2name = dict([(th.ident, th.name) for th in threading.enumerate()])
|
||||
code = []
|
||||
for threadId, stack in sys._current_frames().items():
|
||||
code.append("\n# Thread: %s(%d)" % (id2name.get(threadId,""),
|
||||
threadId))
|
||||
code.append("\n# Thread: %s(%d)" % (id2name.get(threadId, ""), threadId))
|
||||
for filename, lineno, name, line in traceback.extract_stack(stack):
|
||||
code.append('File: "%s", line %d, in %s' % (filename,
|
||||
lineno, name))
|
||||
code.append('File: "%s", line %d, in %s' % (filename, lineno, name))
|
||||
if line:
|
||||
code.append(" %s" % (line.strip()))
|
||||
worker.log.debug("\n".join(code))
|
||||
|
|
|
@ -8,5 +8,7 @@ from .sleep_totals import sleep_totals # NOQA
|
|||
from .tummytime_duration import tummytime_duration # NOQA
|
||||
from .weight_weight import weight_weight # NOQA
|
||||
from .height_height import height_height # NOQA
|
||||
from .head_circumference_head_circumference import head_circumference_head_circumference # NOQA
|
||||
from .head_circumference_head_circumference import (
|
||||
head_circumference_head_circumference,
|
||||
) # NOQA
|
||||
from .bmi_bmi import bmi_bmi # NOQA
|
||||
|
|
|
@ -13,25 +13,22 @@ def bmi_bmi(objects):
|
|||
:param objects: a QuerySet of BMI instances.
|
||||
:returns: a tuple of the the graph's html and javascript.
|
||||
"""
|
||||
objects = objects.order_by('-date')
|
||||
objects = objects.order_by("-date")
|
||||
|
||||
trace = go.Scatter(
|
||||
name=_('BMI'),
|
||||
x=list(objects.values_list('date', flat=True)),
|
||||
y=list(objects.values_list('bmi', flat=True)),
|
||||
fill='tozeroy',
|
||||
name=_("BMI"),
|
||||
x=list(objects.values_list("date", flat=True)),
|
||||
y=list(objects.values_list("bmi", flat=True)),
|
||||
fill="tozeroy",
|
||||
)
|
||||
|
||||
layout_args = utils.default_graph_layout_options()
|
||||
layout_args['barmode'] = 'stack'
|
||||
layout_args['title'] = _('<b>BMI</b>')
|
||||
layout_args['xaxis']['title'] = _('Date')
|
||||
layout_args['xaxis']['rangeselector'] = utils.rangeselector_date()
|
||||
layout_args['yaxis']['title'] = _('BMI')
|
||||
layout_args["barmode"] = "stack"
|
||||
layout_args["title"] = _("<b>BMI</b>")
|
||||
layout_args["xaxis"]["title"] = _("Date")
|
||||
layout_args["xaxis"]["rangeselector"] = utils.rangeselector_date()
|
||||
layout_args["yaxis"]["title"] = _("BMI")
|
||||
|
||||
fig = go.Figure({
|
||||
'data': [trace],
|
||||
'layout': go.Layout(**layout_args)
|
||||
})
|
||||
output = plotly.plot(fig, output_type='div', include_plotlyjs=False)
|
||||
fig = go.Figure({"data": [trace], "layout": go.Layout(**layout_args)})
|
||||
output = plotly.plot(fig, output_type="div", include_plotlyjs=False)
|
||||
return utils.split_graph_output(output)
|
||||
|
|
|
@ -24,23 +24,20 @@ def diaperchange_amounts(instances):
|
|||
|
||||
amounts = [round(amount, 2) for amount in totals.values()]
|
||||
trace = go.Bar(
|
||||
name=_('Diaper change amount'),
|
||||
name=_("Diaper change amount"),
|
||||
x=list(totals.keys()),
|
||||
y=amounts,
|
||||
hoverinfo='text',
|
||||
textposition='outside',
|
||||
text=amounts
|
||||
hoverinfo="text",
|
||||
textposition="outside",
|
||||
text=amounts,
|
||||
)
|
||||
|
||||
layout_args = utils.default_graph_layout_options()
|
||||
layout_args['title'] = _('<b>Diaper Change Amounts</b>')
|
||||
layout_args['xaxis']['title'] = _('Date')
|
||||
layout_args['xaxis']['rangeselector'] = utils.rangeselector_date()
|
||||
layout_args['yaxis']['title'] = _('Change amount')
|
||||
layout_args["title"] = _("<b>Diaper Change Amounts</b>")
|
||||
layout_args["xaxis"]["title"] = _("Date")
|
||||
layout_args["xaxis"]["rangeselector"] = utils.rangeselector_date()
|
||||
layout_args["yaxis"]["title"] = _("Change amount")
|
||||
|
||||
fig = go.Figure({
|
||||
'data': [trace],
|
||||
'layout': go.Layout(**layout_args)
|
||||
})
|
||||
output = plotly.plot(fig, output_type='div', include_plotlyjs=False)
|
||||
fig = go.Figure({"data": [trace], "layout": go.Layout(**layout_args)})
|
||||
output = plotly.plot(fig, output_type="div", include_plotlyjs=False)
|
||||
return utils.split_graph_output(output)
|
||||
|
|
|
@ -13,7 +13,7 @@ def diaperchange_lifetimes(changes):
|
|||
:param changes: a QuerySet of Diaper Change instances.
|
||||
:returns: a tuple of the the graph's html and javascript.
|
||||
"""
|
||||
changes = changes.order_by('time')
|
||||
changes = changes.order_by("time")
|
||||
durations = []
|
||||
last_change = changes.first()
|
||||
for change in changes[1:]:
|
||||
|
@ -23,23 +23,20 @@ def diaperchange_lifetimes(changes):
|
|||
last_change = change
|
||||
|
||||
trace = go.Box(
|
||||
y=[round(d.seconds/3600, 2) for d in durations],
|
||||
name=_('Changes'),
|
||||
y=[round(d.seconds / 3600, 2) for d in durations],
|
||||
name=_("Changes"),
|
||||
jitter=0.3,
|
||||
pointpos=-1.8,
|
||||
boxpoints='all'
|
||||
boxpoints="all",
|
||||
)
|
||||
|
||||
layout_args = utils.default_graph_layout_options()
|
||||
layout_args['height'] = 800
|
||||
layout_args['title'] = _('<b>Diaper Lifetimes</b>')
|
||||
layout_args['yaxis']['title'] = _('Time between changes (hours)')
|
||||
layout_args['yaxis']['zeroline'] = False
|
||||
layout_args['yaxis']['dtick'] = 1
|
||||
layout_args["height"] = 800
|
||||
layout_args["title"] = _("<b>Diaper Lifetimes</b>")
|
||||
layout_args["yaxis"]["title"] = _("Time between changes (hours)")
|
||||
layout_args["yaxis"]["zeroline"] = False
|
||||
layout_args["yaxis"]["dtick"] = 1
|
||||
|
||||
fig = go.Figure({
|
||||
'data': [trace],
|
||||
'layout': go.Layout(**layout_args)
|
||||
})
|
||||
output = plotly.plot(fig, output_type='div', include_plotlyjs=False)
|
||||
fig = go.Figure({"data": [trace], "layout": go.Layout(**layout_args)})
|
||||
output = plotly.plot(fig, output_type="div", include_plotlyjs=False)
|
||||
return utils.split_graph_output(output)
|
||||
|
|
|
@ -16,46 +16,50 @@ def diaperchange_types(changes):
|
|||
:param changes: a QuerySet of Diaper Change instances.
|
||||
:returns: a tuple of the the graph's html and javascript.
|
||||
"""
|
||||
changes = changes.annotate(date=TruncDate('time'))\
|
||||
.values('date') \
|
||||
.annotate(wet_count=Count(Case(When(wet=True, then=1)))) \
|
||||
.annotate(solid_count=Count(Case(When(solid=True, then=1)))) \
|
||||
.annotate(total=Count('id')) \
|
||||
.order_by('-date')
|
||||
changes = (
|
||||
changes.annotate(date=TruncDate("time"))
|
||||
.values("date")
|
||||
.annotate(wet_count=Count(Case(When(wet=True, then=1))))
|
||||
.annotate(solid_count=Count(Case(When(solid=True, then=1))))
|
||||
.annotate(total=Count("id"))
|
||||
.order_by("-date")
|
||||
)
|
||||
|
||||
solid_trace = go.Scatter(
|
||||
mode='markers',
|
||||
name=_('Solid'),
|
||||
x=list(changes.values_list('date', flat=True)),
|
||||
y=list(changes.values_list('solid_count', flat=True)),
|
||||
mode="markers",
|
||||
name=_("Solid"),
|
||||
x=list(changes.values_list("date", flat=True)),
|
||||
y=list(changes.values_list("solid_count", flat=True)),
|
||||
)
|
||||
wet_trace = go.Scatter(
|
||||
mode='markers',
|
||||
name=_('Wet'),
|
||||
x=list(changes.values_list('date', flat=True)),
|
||||
y=list(changes.values_list('wet_count', flat=True))
|
||||
mode="markers",
|
||||
name=_("Wet"),
|
||||
x=list(changes.values_list("date", flat=True)),
|
||||
y=list(changes.values_list("wet_count", flat=True)),
|
||||
)
|
||||
total_trace = go.Scatter(
|
||||
name=_('Total'),
|
||||
x=list(changes.values_list('date', flat=True)),
|
||||
y=list(changes.values_list('total', flat=True))
|
||||
name=_("Total"),
|
||||
x=list(changes.values_list("date", flat=True)),
|
||||
y=list(changes.values_list("total", flat=True)),
|
||||
)
|
||||
|
||||
layout_args = utils.default_graph_layout_options()
|
||||
layout_args['barmode'] = 'stack'
|
||||
layout_args['title'] = _('<b>Diaper Change Types</b>')
|
||||
layout_args['xaxis']['title'] = _('Date')
|
||||
layout_args['xaxis']['rangeselector'] = utils.rangeselector_date()
|
||||
layout_args['yaxis']['title'] = _('Number of changes')
|
||||
layout_args["barmode"] = "stack"
|
||||
layout_args["title"] = _("<b>Diaper Change Types</b>")
|
||||
layout_args["xaxis"]["title"] = _("Date")
|
||||
layout_args["xaxis"]["rangeselector"] = utils.rangeselector_date()
|
||||
layout_args["yaxis"]["title"] = _("Number of changes")
|
||||
|
||||
fig = go.Figure({
|
||||
'data': [solid_trace, wet_trace, total_trace],
|
||||
'layout': go.Layout(**layout_args)
|
||||
})
|
||||
fig = go.Figure(
|
||||
{
|
||||
"data": [solid_trace, wet_trace, total_trace],
|
||||
"layout": go.Layout(**layout_args),
|
||||
}
|
||||
)
|
||||
output = plotly.plot(
|
||||
fig,
|
||||
output_type='div',
|
||||
output_type="div",
|
||||
include_plotlyjs=False,
|
||||
config={'locale': get_language()}
|
||||
config={"locale": get_language()},
|
||||
)
|
||||
return utils.split_graph_output(output)
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue