mybuddy/core/models.py

742 lines
22 KiB
Python
Raw Normal View History

2017-08-13 15:20:09 +00:00
# -*- coding: utf-8 -*-
2023-10-21 18:16:08 +00:00
import datetime
2022-03-08 13:48:27 +00:00
import re
from datetime import timedelta
from django.core.cache import cache
from django.core.exceptions import ValidationError
2022-04-12 02:13:12 +00:00
from django.core.validators import RegexValidator
2017-08-11 18:32:02 +00:00
from django.db import models
from django.utils import timezone
2022-04-12 02:13:12 +00:00
from django.utils.text import format_lazy, slugify
from django.utils.translation import gettext_lazy as _
2022-03-02 22:31:11 +00:00
from taggit.managers import TaggableManager as TaggitTaggableManager
2022-04-12 02:13:12 +00:00
from taggit.models import GenericTaggedItemBase, TagBase
from babybuddy.site_settings import NapSettings
2022-05-26 04:03:12 +00:00
from core.utils import random_color
2022-02-15 21:24:13 +00:00
2022-02-27 19:00:12 +00:00
2017-11-10 02:15:09 +00:00
def validate_date(date, field_name):
"""
Confirm that a date is not in the future.
:param date: a timezone aware date instance.
:param field_name: the name of the field being checked.
:return:
"""
if date and date > timezone.localdate():
raise ValidationError(
2022-02-10 00:00:30 +00:00
{field_name: _("Date can not be in the future.")}, code="date_invalid"
)
2017-11-10 02:15:09 +00:00
def validate_duration(model, max_duration=timedelta(hours=24)):
"""
Basic sanity checks for models with a duration
:param model: a model instance with 'start' and 'end' attributes
:param max_duration: maximum allowed duration between start and end time
:return:
"""
if model.start and model.end:
if model.start > model.end:
2017-11-01 20:14:42 +00:00
raise ValidationError(
2022-02-10 00:00:30 +00:00
_("Start time must come before end time."), code="end_before_start"
)
if model.end - model.start > max_duration:
2022-02-10 00:00:30 +00:00
raise ValidationError(_("Duration too long."), code="max_duration")
2017-08-11 18:32:02 +00:00
2017-08-13 14:48:16 +00:00
def validate_unique_period(queryset, model):
"""
Confirm that model's start and end date do not intersect with other
instances.
:param queryset: a queryset of instances to check against.
:param model: a model instance with 'start' and 'end' attributes
:return:
"""
if model.id:
queryset = queryset.exclude(id=model.id)
if model.start and model.end:
if queryset.filter(start__lt=model.end, end__gt=model.start):
raise ValidationError(
2022-02-10 00:00:30 +00:00
_("Another entry intersects the specified time period."),
code="period_intersection",
)
def validate_time(time, field_name):
"""
Confirm that a time is not in the future.
:param time: a timezone aware datetime instance.
:param field_name: the name of the field being checked.
:return:
"""
if time and time > timezone.localtime():
raise ValidationError(
2022-02-10 00:00:30 +00:00
{field_name: _("Date/time can not be in the future.")}, code="time_invalid"
)
2022-02-15 21:24:13 +00:00
class Tag(TagBase):
2022-05-26 04:03:12 +00:00
DARK_COLOR = "#101010"
LIGHT_COLOR = "#EFEFEF"
2022-02-27 19:00:12 +00:00
2022-02-15 09:13:35 +00:00
color = models.CharField(
verbose_name=_("Color"),
2022-02-15 09:13:35 +00:00
max_length=32,
2022-02-15 21:24:13 +00:00
default=random_color,
2022-03-08 11:55:50 +00:00
validators=[RegexValidator(r"^#[0-9a-fA-F]{6}$")],
2022-02-15 09:13:35 +00:00
)
last_used = models.DateTimeField(
verbose_name=_("Last used"),
2022-02-27 20:12:01 +00:00
default=timezone.now,
2022-02-15 09:13:35 +00:00
blank=False,
)
2022-05-26 04:03:12 +00:00
class Meta:
verbose_name = _("Tag")
verbose_name_plural = _("Tags")
2022-03-08 13:48:27 +00:00
@property
def complementary_color(self):
if not self.color:
2022-05-26 04:03:12 +00:00
return self.DARK_COLOR
2022-03-08 13:48:27 +00:00
r, g, b = [int(x, 16) for x in re.match("#(..)(..)(..)", self.color).groups()]
yiq = ((r * 299) + (g * 587) + (b * 114)) // 1000
if yiq >= 128:
2022-05-26 04:03:12 +00:00
return self.DARK_COLOR
2022-03-08 13:48:27 +00:00
else:
2022-05-26 04:03:12 +00:00
return self.LIGHT_COLOR
2022-03-08 13:48:27 +00:00
2022-02-15 09:13:35 +00:00
class Tagged(GenericTaggedItemBase):
2022-02-15 09:13:35 +00:00
tag = models.ForeignKey(
Tag,
verbose_name=_("Tag"),
2022-02-15 09:13:35 +00:00
on_delete=models.CASCADE,
related_name="%(app_label)s_%(class)s_items",
)
2022-02-20 11:40:06 +00:00
def save_base(self, *args, **kwargs):
"""
Update last_used of the used tag, whenever it is used in a
save-operation.
"""
2022-02-27 20:12:01 +00:00
self.tag.last_used = timezone.now()
2022-02-20 11:40:06 +00:00
self.tag.save()
return super().save_base(*args, **kwargs)
2022-03-02 22:31:11 +00:00
class TaggableManager(TaggitTaggableManager):
pass
2022-02-27 19:00:12 +00:00
class BMI(models.Model):
model_name = "bmi"
child = models.ForeignKey(
"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, default=timezone.localdate, null=False, verbose_name=_("Date")
)
notes = models.TextField(blank=True, null=True, verbose_name=_("Notes"))
tags = TaggableManager(blank=True, through=Tagged)
objects = models.Manager()
class Meta:
default_permissions = ("view", "add", "change", "delete")
ordering = ["-date"]
verbose_name = _("BMI")
verbose_name_plural = _("BMI")
def __str__(self):
return str(_("BMI"))
def clean(self):
validate_date(self.date, "date")
2017-08-16 12:49:58 +00:00
class Child(models.Model):
2022-02-10 00:00:30 +00:00
model_name = "child"
first_name = models.CharField(max_length=255, verbose_name=_("First name"))
2021-12-29 21:41:39 +00:00
last_name = models.CharField(
2022-02-10 00:00:30 +00:00
blank=True, max_length=255, verbose_name=_("Last name")
)
2022-02-10 00:00:30 +00:00
birth_date = models.DateField(blank=False, null=False, verbose_name=_("Birth date"))
2023-10-21 18:16:08 +00:00
birth_time = models.TimeField(blank=True, null=True, verbose_name=_("Birth time"))
slug = models.SlugField(
allow_unicode=True,
blank=False,
editable=False,
max_length=100,
unique=True,
2022-02-10 00:00:30 +00:00
verbose_name=_("Slug"),
)
picture = models.ImageField(
2022-02-10 00:00:30 +00:00
blank=True, null=True, upload_to="child/picture/", verbose_name=_("Picture")
)
2017-08-13 14:48:16 +00:00
objects = models.Manager()
2022-02-10 00:00:30 +00:00
cache_key_count = "core.child.count"
2017-08-13 14:48:16 +00:00
class Meta:
2022-02-10 00:00:30 +00:00
default_permissions = ("view", "add", "change", "delete")
ordering = ["last_name", "first_name"]
verbose_name = _("Child")
verbose_name_plural = _("Children")
2017-08-13 14:48:16 +00:00
def __str__(self):
2021-12-29 21:41:39 +00:00
return self.name()
2017-08-13 14:48:16 +00:00
2017-08-18 12:08:23 +00:00
def save(self, *args, **kwargs):
self.slug = slugify(self, allow_unicode=True)
2017-08-18 12:08:23 +00:00
super(Child, self).save(*args, **kwargs)
cache.set(self.cache_key_count, Child.objects.count(), None)
def delete(self, using=None, keep_parents=False):
super(Child, self).delete(using, keep_parents)
cache.set(self.cache_key_count, Child.objects.count(), None)
2017-08-18 12:08:23 +00:00
def name(self, reverse=False):
2021-12-29 21:41:39 +00:00
if not self.last_name:
return self.first_name
if reverse:
2022-02-10 00:00:30 +00:00
return "{}, {}".format(self.last_name, self.first_name)
return "{} {}".format(self.first_name, self.last_name)
2023-10-21 18:16:08 +00:00
def birth_datetime(self):
if self.birth_time:
return timezone.make_aware(
datetime.datetime.combine(self.birth_date, self.birth_time)
)
return self.birth_date
@classmethod
def count(cls):
2022-02-10 00:00:30 +00:00
"""Get a (cached) count of total number of Child instances."""
return cache.get_or_set(cls.cache_key_count, Child.objects.count, None)
2017-08-13 14:48:16 +00:00
2017-08-13 19:51:25 +00:00
class DiaperChange(models.Model):
2022-02-10 00:00:30 +00:00
model_name = "diaperchange"
2017-12-03 21:52:27 +00:00
child = models.ForeignKey(
2022-02-10 00:00:30 +00:00
"Child",
on_delete=models.CASCADE,
2022-02-10 00:00:30 +00:00
related_name="diaper_change",
verbose_name=_("Child"),
2019-04-19 02:55:55 +00:00
)
2022-04-12 02:13:42 +00:00
time = models.DateTimeField(
blank=False, default=timezone.localtime, null=False, verbose_name=_("Time")
)
2022-02-10 00:00:30 +00:00
wet = models.BooleanField(verbose_name=_("Wet"))
solid = models.BooleanField(verbose_name=_("Solid"))
color = models.CharField(
blank=True,
choices=[
2022-02-10 00:00:30 +00:00
("black", _("Black")),
("brown", _("Brown")),
("green", _("Green")),
("yellow", _("Yellow")),
],
max_length=255,
2022-02-10 00:00:30 +00:00
verbose_name=_("Color"),
)
2022-02-10 00:00:30 +00:00
amount = models.FloatField(blank=True, null=True, verbose_name=_("Amount"))
notes = models.TextField(blank=True, null=True, verbose_name=_("Notes"))
tags = TaggableManager(blank=True, through=Tagged)
2017-08-13 19:51:25 +00:00
objects = models.Manager()
class Meta:
2022-02-10 00:00:30 +00:00
default_permissions = ("view", "add", "change", "delete")
ordering = ["-time"]
verbose_name = _("Diaper Change")
verbose_name_plural = _("Diaper Changes")
2017-08-13 19:51:25 +00:00
def __str__(self):
2022-02-10 00:00:30 +00:00
return str(_("Diaper Change"))
2017-08-13 19:51:25 +00:00
2017-08-18 15:00:58 +00:00
def attributes(self):
attributes = []
if self.wet:
2022-02-10 00:00:30 +00:00
attributes.append(self._meta.get_field("wet").verbose_name)
2017-08-18 15:00:58 +00:00
if self.solid:
2022-02-10 00:00:30 +00:00
attributes.append(self._meta.get_field("solid").verbose_name)
2017-08-18 15:00:58 +00:00
if self.color:
attributes.append(self.get_color_display())
2017-08-18 15:00:58 +00:00
return attributes
2017-11-01 20:14:42 +00:00
def clean(self):
2022-02-10 00:00:30 +00:00
validate_time(self.time, "time")
2017-08-13 19:51:25 +00:00
2017-08-13 15:59:14 +00:00
class Feeding(models.Model):
2022-02-10 00:00:30 +00:00
model_name = "feeding"
2017-12-03 21:52:27 +00:00
child = models.ForeignKey(
2022-02-10 00:00:30 +00:00
"Child",
on_delete=models.CASCADE,
2022-02-10 00:00:30 +00:00
related_name="feeding",
verbose_name=_("Child"),
)
start = models.DateTimeField(
blank=False,
default=timezone.localtime,
null=False,
verbose_name=_("Start time"),
)
end = models.DateTimeField(
blank=False, default=timezone.localtime, null=False, verbose_name=_("End time")
)
duration = models.DurationField(
2022-02-10 00:00:30 +00:00
editable=False, null=True, verbose_name=_("Duration")
)
type = models.CharField(
choices=[
2022-02-10 00:00:30 +00:00
("breast milk", _("Breast milk")),
("formula", _("Formula")),
("fortified breast milk", _("Fortified breast milk")),
("solid food", _("Solid food")),
],
max_length=255,
2022-02-10 00:00:30 +00:00
verbose_name=_("Type"),
)
method = models.CharField(
choices=[
2022-02-10 00:00:30 +00:00
("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,
2022-02-10 00:00:30 +00:00
verbose_name=_("Method"),
)
2022-02-10 00:00:30 +00:00
amount = models.FloatField(blank=True, null=True, verbose_name=_("Amount"))
notes = models.TextField(blank=True, null=True, verbose_name=_("Notes"))
tags = TaggableManager(blank=True, through=Tagged)
2017-08-13 15:59:14 +00:00
objects = models.Manager()
class Meta:
2022-02-10 00:00:30 +00:00
default_permissions = ("view", "add", "change", "delete")
ordering = ["-start"]
verbose_name = _("Feeding")
verbose_name_plural = _("Feedings")
2017-08-13 15:59:14 +00:00
def __str__(self):
2022-02-10 00:00:30 +00:00
return str(_("Feeding"))
2017-08-13 15:59:14 +00:00
def save(self, *args, **kwargs):
if self.start and self.end:
self.duration = self.end - self.start
super(Feeding, self).save(*args, **kwargs)
def clean(self):
2022-02-10 00:00:30 +00:00
validate_time(self.start, "start")
validate_duration(self)
validate_unique_period(Feeding.objects.filter(child=self.child), self)
2022-02-27 19:00:12 +00:00
class HeadCircumference(models.Model):
model_name = "head_circumference"
child = models.ForeignKey(
"Child",
on_delete=models.CASCADE,
related_name="head_circumference",
verbose_name=_("Child"),
)
head_circumference = models.FloatField(
blank=False, null=False, verbose_name=_("Head Circumference")
)
date = models.DateField(
blank=False, default=timezone.localdate, null=False, verbose_name=_("Date")
)
notes = models.TextField(blank=True, null=True, verbose_name=_("Notes"))
tags = TaggableManager(blank=True, through=Tagged)
objects = models.Manager()
class Meta:
default_permissions = ("view", "add", "change", "delete")
ordering = ["-date"]
verbose_name = _("Head Circumference")
verbose_name_plural = _("Head Circumference")
def __str__(self):
return str(_("Head Circumference"))
def clean(self):
validate_date(self.date, "date")
class Height(models.Model):
model_name = "height"
child = models.ForeignKey(
"Child",
on_delete=models.CASCADE,
related_name="height",
verbose_name=_("Child"),
)
height = models.FloatField(blank=False, null=False, verbose_name=_("Height"))
date = models.DateField(
blank=False, default=timezone.localdate, null=False, verbose_name=_("Date")
)
notes = models.TextField(blank=True, null=True, verbose_name=_("Notes"))
tags = TaggableManager(blank=True, through=Tagged)
objects = models.Manager()
class Meta:
default_permissions = ("view", "add", "change", "delete")
ordering = ["-date"]
verbose_name = _("Height")
verbose_name_plural = _("Height")
def __str__(self):
return str(_("Height"))
def clean(self):
validate_date(self.date, "date")
2017-08-13 20:48:16 +00:00
class Note(models.Model):
2022-02-10 00:00:30 +00:00
model_name = "note"
2017-12-03 21:52:27 +00:00
child = models.ForeignKey(
2022-02-10 00:00:30 +00:00
"Child", on_delete=models.CASCADE, related_name="note", verbose_name=_("Child")
)
2022-02-10 00:00:30 +00:00
note = models.TextField(verbose_name=_("Note"))
time = models.DateTimeField(
2022-04-12 02:13:42 +00:00
blank=False, default=timezone.localtime, verbose_name=_("Time")
)
2023-10-20 23:59:37 +00:00
image = models.ImageField(
blank=True, null=True, upload_to="notes/images/", verbose_name=_("Image")
)
tags = TaggableManager(blank=True, through=Tagged)
2017-08-13 20:48:16 +00:00
objects = models.Manager()
class Meta:
2022-02-10 00:00:30 +00:00
default_permissions = ("view", "add", "change", "delete")
ordering = ["-time"]
verbose_name = _("Note")
verbose_name_plural = _("Notes")
2017-08-13 20:48:16 +00:00
def __str__(self):
2022-02-10 00:00:30 +00:00
return str(_("Note"))
2017-08-13 20:48:16 +00:00
class Pumping(models.Model):
model_name = "pumping"
child = models.ForeignKey(
"Child",
on_delete=models.CASCADE,
related_name="pumping",
verbose_name=_("Child"),
)
2023-06-16 21:06:24 +00:00
start = models.DateTimeField(
blank=False,
default=timezone.localtime,
null=False,
verbose_name=_("Start time"),
)
end = models.DateTimeField(
blank=False,
default=timezone.localtime,
null=False,
verbose_name=_("End time"),
)
2023-06-16 21:06:24 +00:00
duration = models.DurationField(
editable=False,
null=True,
verbose_name=_("Duration"),
)
amount = models.FloatField(blank=False, null=False, verbose_name=_("Amount"))
notes = models.TextField(blank=True, null=True, verbose_name=_("Notes"))
2023-01-28 14:14:23 +00:00
tags = TaggableManager(blank=True, through=Tagged)
objects = models.Manager()
class Meta:
default_permissions = ("view", "add", "change", "delete")
2023-06-16 21:06:24 +00:00
ordering = ["-start"]
verbose_name = _("Pumping")
verbose_name_plural = _("Pumping")
def __str__(self):
return str(_("Pumping"))
2023-06-16 21:06:24 +00:00
def save(self, *args, **kwargs):
if self.start and self.end:
self.duration = self.end - self.start
super(Pumping, self).save(*args, **kwargs)
def clean(self):
2023-06-16 21:06:24 +00:00
validate_time(self.start, "start")
validate_duration(self)
validate_unique_period(Pumping.objects.filter(child=self.child), self)
2017-08-13 14:48:16 +00:00
class Sleep(models.Model):
2022-02-10 00:00:30 +00:00
model_name = "sleep"
2017-12-03 21:52:27 +00:00
child = models.ForeignKey(
2022-02-10 00:00:30 +00:00
"Child", on_delete=models.CASCADE, related_name="sleep", verbose_name=_("Child")
)
start = models.DateTimeField(
blank=False,
default=timezone.localtime,
null=False,
verbose_name=_("Start time"),
)
end = models.DateTimeField(
blank=False, default=timezone.localtime, null=False, verbose_name=_("End time")
)
nap = models.BooleanField(null=False, blank=True, verbose_name=_("Nap"))
duration = models.DurationField(
2022-02-10 00:00:30 +00:00
editable=False, null=True, verbose_name=_("Duration")
)
2022-02-10 00:00:30 +00:00
notes = models.TextField(blank=True, null=True, verbose_name=_("Notes"))
tags = TaggableManager(blank=True, through=Tagged)
2017-08-13 14:48:16 +00:00
objects = models.Manager()
settings = NapSettings(_("Nap settings"))
2017-08-13 14:48:16 +00:00
class Meta:
2022-02-10 00:00:30 +00:00
default_permissions = ("view", "add", "change", "delete")
ordering = ["-start"]
verbose_name = _("Sleep")
verbose_name_plural = _("Sleep")
2017-08-13 14:48:16 +00:00
def __str__(self):
2022-02-10 00:00:30 +00:00
return str(_("Sleep"))
2017-08-13 19:05:44 +00:00
def save(self, *args, **kwargs):
if self.nap is None:
self.nap = (
Sleep.settings.nap_start_min
<= timezone.localtime(self.start).time()
<= Sleep.settings.nap_start_max
)
if self.start and self.end:
self.duration = self.end - self.start
super(Sleep, self).save(*args, **kwargs)
def clean(self):
2022-02-10 00:00:30 +00:00
validate_time(self.start, "start")
validate_time(self.end, "end")
validate_duration(self)
validate_unique_period(Sleep.objects.filter(child=self.child), self)
2017-08-13 19:05:44 +00:00
2019-05-17 04:33:26 +00:00
class Temperature(models.Model):
2022-02-10 00:00:30 +00:00
model_name = "temperature"
2019-05-17 04:33:26 +00:00
child = models.ForeignKey(
2022-02-10 00:00:30 +00:00
"Child",
2019-05-17 04:33:26 +00:00
on_delete=models.CASCADE,
2022-02-10 00:00:30 +00:00
related_name="temperature",
verbose_name=_("Child"),
2019-05-17 04:33:26 +00:00
)
temperature = models.FloatField(
2022-02-10 00:00:30 +00:00
blank=False, null=False, verbose_name=_("Temperature")
2019-05-17 04:33:26 +00:00
)
2022-04-12 02:13:42 +00:00
time = models.DateTimeField(
blank=False, default=timezone.localtime, null=False, verbose_name=_("Time")
)
2022-02-10 00:00:30 +00:00
notes = models.TextField(blank=True, null=True, verbose_name=_("Notes"))
tags = TaggableManager(blank=True, through=Tagged)
2019-05-17 04:33:26 +00:00
objects = models.Manager()
class Meta:
2022-02-10 00:00:30 +00:00
default_permissions = ("view", "add", "change", "delete")
ordering = ["-time"]
verbose_name = _("Temperature")
verbose_name_plural = _("Temperature")
2019-05-17 04:33:26 +00:00
def __str__(self):
2022-02-10 00:00:30 +00:00
return str(_("Temperature"))
2019-05-17 04:33:26 +00:00
def clean(self):
2022-02-10 00:00:30 +00:00
validate_time(self.time, "time")
2019-05-17 04:33:26 +00:00
class Timer(models.Model):
2022-02-10 00:00:30 +00:00
model_name = "timer"
2020-01-28 21:56:28 +00:00
child = models.ForeignKey(
2022-02-10 00:00:30 +00:00
"Child",
2020-01-28 21:56:28 +00:00
blank=True,
null=True,
on_delete=models.CASCADE,
2022-02-10 00:00:30 +00:00
related_name="timers",
verbose_name=_("Child"),
2020-01-28 21:56:28 +00:00
)
name = models.CharField(
2022-02-10 00:00:30 +00:00
blank=True, max_length=255, null=True, verbose_name=_("Name")
)
start = models.DateTimeField(
2022-02-10 00:00:30 +00:00
default=timezone.now, blank=False, verbose_name=_("Start time")
)
2022-02-10 00:00:30 +00:00
active = models.BooleanField(default=True, editable=False, verbose_name=_("Active"))
2017-12-03 21:52:27 +00:00
user = models.ForeignKey(
2022-02-10 00:00:30 +00:00
"auth.User",
on_delete=models.CASCADE,
2022-02-10 00:00:30 +00:00
related_name="timers",
verbose_name=_("User"),
)
objects = models.Manager()
class Meta:
2022-02-10 00:00:30 +00:00
default_permissions = ("view", "add", "change", "delete")
ordering = ["-start"]
2022-02-10 00:00:30 +00:00
verbose_name = _("Timer")
verbose_name_plural = _("Timers")
def __str__(self):
2022-02-10 00:00:30 +00:00
return self.name or str(format_lazy(_("Timer #{id}"), id=self.id))
2020-01-28 21:56:28 +00:00
@property
def title_with_child(self):
2022-02-10 00:00:30 +00:00
"""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:
2022-02-10 00:00:30 +00:00
title = format_lazy("{title} ({child})", title=title, child=self.child)
return title
2020-01-28 21:56:28 +00:00
@property
def user_username(self):
2022-02-10 00:00:30 +00:00
"""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()
def duration(self):
return timezone.now() - self.start
def restart(self):
"""Restart the timer."""
self.start = timezone.now()
self.save()
def stop(self):
"""Stop (delete) the timer."""
self.delete()
def save(self, *args, **kwargs):
self.name = self.name or None
super(Timer, self).save(*args, **kwargs)
def clean(self):
2022-02-10 00:00:30 +00:00
validate_time(self.start, "start")
2017-08-13 19:05:44 +00:00
class TummyTime(models.Model):
2022-02-10 00:00:30 +00:00
model_name = "tummytime"
2017-12-03 21:52:27 +00:00
child = models.ForeignKey(
2022-02-10 00:00:30 +00:00
"Child",
on_delete=models.CASCADE,
2022-02-10 00:00:30 +00:00
related_name="tummy_time",
verbose_name=_("Child"),
)
start = models.DateTimeField(
blank=False,
default=timezone.localtime,
null=False,
verbose_name=_("Start time"),
)
end = models.DateTimeField(
blank=False, default=timezone.localtime, null=False, verbose_name=_("End time")
)
duration = models.DurationField(
2022-02-10 00:00:30 +00:00
editable=False, null=True, verbose_name=_("Duration")
)
milestone = models.CharField(
2022-02-10 00:00:30 +00:00
blank=True, max_length=255, verbose_name=_("Milestone")
)
tags = TaggableManager(blank=True, through=Tagged)
2017-08-13 19:05:44 +00:00
objects = models.Manager()
class Meta:
2022-02-10 00:00:30 +00:00
default_permissions = ("view", "add", "change", "delete")
ordering = ["-start"]
verbose_name = _("Tummy Time")
verbose_name_plural = _("Tummy Time")
2017-08-13 19:05:44 +00:00
def __str__(self):
2022-02-10 00:00:30 +00:00
return str(_("Tummy Time"))
2017-08-13 19:05:44 +00:00
def save(self, *args, **kwargs):
if self.start and self.end:
self.duration = self.end - self.start
super(TummyTime, self).save(*args, **kwargs)
def clean(self):
2022-02-10 00:00:30 +00:00
validate_time(self.start, "start")
validate_time(self.end, "end")
validate_duration(self)
2022-02-10 00:00:30 +00:00
validate_unique_period(TummyTime.objects.filter(child=self.child), self)
2017-11-10 02:15:09 +00:00
class Weight(models.Model):
2022-02-10 00:00:30 +00:00
model_name = "weight"
2017-12-03 21:52:27 +00:00
child = models.ForeignKey(
2022-02-10 00:00:30 +00:00
"Child",
on_delete=models.CASCADE,
2022-02-10 00:00:30 +00:00
related_name="weight",
verbose_name=_("Child"),
)
2022-02-10 00:00:30 +00:00
weight = models.FloatField(blank=False, null=False, verbose_name=_("Weight"))
2022-04-12 02:13:42 +00:00
date = models.DateField(
blank=False, default=timezone.localdate, null=False, verbose_name=_("Date")
)
2022-02-10 00:00:30 +00:00
notes = models.TextField(blank=True, null=True, verbose_name=_("Notes"))
tags = TaggableManager(blank=True, through=Tagged)
2017-11-10 02:15:09 +00:00
objects = models.Manager()
class Meta:
2022-02-10 00:00:30 +00:00
default_permissions = ("view", "add", "change", "delete")
ordering = ["-date"]
verbose_name = _("Weight")
verbose_name_plural = _("Weight")
2017-11-10 02:15:09 +00:00
def __str__(self):
2022-02-10 00:00:30 +00:00
return str(_("Weight"))
2017-11-10 02:15:09 +00:00
def clean(self):
2022-02-10 00:00:30 +00:00
validate_date(self.date, "date")
class WeightPercentile(models.Model):
model_name = "weight percentile"
age_in_days = models.DurationField(null=False)
p3_weight = models.FloatField(null=False)
p15_weight = models.FloatField(null=False)
p50_weight = models.FloatField(null=False)
p85_weight = models.FloatField(null=False)
p97_weight = models.FloatField(null=False)
sex = models.CharField(
null=False,
max_length=255,
choices=[
("girl", _("Girl")),
("boy", _("Boy")),
],
)
class Meta:
constraints = [
models.UniqueConstraint(
fields=["age_in_days", "sex"], name="unique_age_sex"
)
]
def __str__(self):
return f"Sex: {self.sex}, Age: {self.age_in_days} days, p3: {self.p3_weight} kg, p15: {self.p15_weight} kg, p50: {self.p50_weight} kg, p85: {self.p85_weight} kg, p97: {self.p97_weight} kg"