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".' ) reminder_time = models.TimeField( null=True, blank=True, help_text="Send a push reminder at this time if the dose isn't ticked off. " "Leave empty for no reminder.", ) 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 ReminderSent(models.Model): """One row per dose per day once its reminder has gone out. This is what stops a reminder being re-sent every time the cron job ticks. """ scheduled_dose = models.ForeignKey( ScheduledDose, on_delete=models.CASCADE, related_name="reminders" ) date = models.DateField() sent_at = models.DateTimeField(auto_now_add=True) class Meta: ordering = ["-sent_at"] constraints = [ models.UniqueConstraint( fields=["scheduled_dose", "date"], name="unique_reminder_per_day" ) ] def __str__(self): return f"{self.date} — {self.scheduled_dose}" class DailyNote(models.Model): """Free-form journal entry for one day: appetite, behaviour, how Osiris is doing.""" date = models.DateField(default=timezone.localdate, unique=True) body = models.TextField(blank=True) updated_at = models.DateTimeField(auto_now=True) class Meta: ordering = ["-date"] def __str__(self): return f"{self.date} — {self.body[:40]}" class GlobalNote(models.Model): """The single shared note: standing instructions and things to do. There is exactly one row, shared by everyone; the constraint pins its pk so a second one cannot be created even by accident. """ body = models.TextField(blank=True) updated_at = models.DateTimeField(auto_now=True) class Meta: constraints = [ models.CheckConstraint(condition=models.Q(id=1), name="single_global_note") ] def __str__(self): return "Global note" @classmethod def load(cls) -> "GlobalNote": note, _ = cls.objects.get_or_create(pk=1) return note 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"