osiris/tracker/models.py
2026-07-14 20:10:17 +02:00

108 lines
3.3 KiB
Python

from django.db import models
from django.utils import timezone
class TimeOfDay(models.IntegerChoices):
"""Integer-backed so that ordering by this field is chronological."""
MORNING = 1, "Morning"
NOON = 2, "Noon"
EVENING = 3, "Evening"
NIGHT = 4, "Night"
class Medication(models.Model):
"""A drug Osiris is on, e.g. "Vetmedin"."""
name = models.CharField(max_length=100, unique=True)
is_active = models.BooleanField(
default=True, help_text="Uncheck when the treatment ends, instead of deleting."
)
notes = models.TextField(blank=True)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ["name"]
def __str__(self):
return self.name
class ScheduledDose(models.Model):
"""One recurring dose of a medication, e.g. "Vetmedin, 1/4, every morning".
A medication taken morning and evening has two of these.
"""
medication = models.ForeignKey(
Medication, on_delete=models.CASCADE, related_name="scheduled_doses"
)
time_of_day = models.IntegerField(choices=TimeOfDay.choices)
amount = models.CharField(
max_length=20, default="1/4", help_text='Free text, e.g. "1/4", "1", "5 mg".'
)
is_active = models.BooleanField(default=True)
class Meta:
ordering = ["medication__name", "time_of_day"]
constraints = [
models.UniqueConstraint(
fields=["medication", "time_of_day"],
name="unique_medication_time_of_day",
)
]
def __str__(self):
return f"{self.medication.name}{self.amount} ({self.get_time_of_day_display().lower()})"
class DoseLog(models.Model):
"""Whether a given scheduled dose was actually given on a given day.
A missing row means "not recorded"; a row with taken=False means "explicitly
marked as not given", so unchecking a box is distinguishable from never
having touched it.
"""
scheduled_dose = models.ForeignKey(
ScheduledDose, on_delete=models.CASCADE, related_name="logs"
)
date = models.DateField(default=timezone.localdate)
taken = models.BooleanField(default=True)
taken_at = models.DateTimeField(null=True, blank=True)
notes = models.TextField(blank=True)
class Meta:
ordering = ["-date", "scheduled_dose__time_of_day"]
constraints = [
models.UniqueConstraint(
fields=["scheduled_dose", "date"], name="unique_dose_per_day"
)
]
def __str__(self):
state = "given" if self.taken else "missed"
return f"{self.date}{self.scheduled_dose}{state}"
def save(self, *args, **kwargs):
if self.taken and self.taken_at is None:
self.taken_at = timezone.now()
if not self.taken:
self.taken_at = None
return super().save(*args, **kwargs)
class PulseReading(models.Model):
"""One heart-rate measurement per day, in beats per minute."""
date = models.DateField(default=timezone.localdate, unique=True)
bpm = models.PositiveSmallIntegerField(help_text="Beats per minute.")
notes = models.TextField(blank=True)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ["-date"]
def __str__(self):
return f"{self.date}{self.bpm} bpm"