Mantilogs API-ification: Part 3 - The Model Redesign

Aside from having to make some calls later on today I will be spending full days in the office for the most part now, possibly even weekends. Aside from days when I will be doing more cleaning and packing up the stuff I don't currently need.

First thing I need to do is fix my gitlab, I broke it trying to configure it's domain somehow. It's been a whole thing... anyway, once I get that done I will be spending some time with my guitar and then get on to the model redesign until lunch.

After lunch I need to make the aforementioned phone calls in the hunt for somewhere to move to. Who knows where this will end up, really. I anticipate a couple hours on the phone but it could be 10 minutes or days, even.

I also have another project I want to add to the list for this year, I did some R&D over the weekend and would like to make an old dream real by building myself a little TV station that will run inside my network with a Django back-end (what else? I am really fond of the ORM and Django admin) that will allow me to add commercials and series to a database that can then be streamed to an RTMP server I am thinking and then write a web-based front-end to be able to tune in.  

Anyway, guitar time.

Alright, Gitlab. I've wasted enough time with you, I will use Gitea. It's lighter weight and if I need any other tools added on I can do it myself. I like all the features and everything but I need to be writing code not troubleshooting my tools. On top of that it seems the data center is having some issues so... that's fun.  

Yeah, it would appear the issues are network based. Figures. Looks like I will have to commit to the local network git server for today. Before I get to work on anything though I will do some... server updates. HANG ON TO YOUR ASSES!

There we go, server's back up and it seems like the network has stabilized. I can get back to work.

Rebase complete.

Now that it's 11:30 and I spent most of the morning trying to fix Gitlab only to find out it was the network at the data center... really wish they would e-mail me about things.  

On to the redesign of the logs...

First up is the Feed log, then I will break the basic log into a few different types of log.

The feeding log should track updates, food given and food returned. Returned food is just a way to track what isn't eaten, this could tell us if they only like certain kinds of food at certain times, temps, sizes, etc. It will also track when the log is updated so we can see how long something went uneaten before it was returned.

# THE GREAT LOG MODEL REWRITE OF '23
class Feed_Log(models.Model):
    """Log items fed, returned and when items were returned"""

    def __str__(self):
        return f"{self.gecko} ate {self.feed} @ {self.time}"

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
   
    time = models.DateTimeField(default=datetime.datetime.now)
    last_updated = models.DateTimeField(default=datetime.datetime.now)
 
    gecko = models.ForeignKey('Gecko', on_delete=models.CASCADE)
    feed = ArrayField(
        models.CharField(max_length=80)
    )
    # Feed items that were returned.
    feed_not_eaten = ArrayField(
        models.CharField(max_length=80)
    )
    all_items_eaten = models.BooleanField(default=False)
    # Feed Supplement (Dropdown: Calcium, Multivitamin, Vitamin D + Calcium)
    supplements = [('CALC', 'Calcium'),
                   ('VITD', 'Vitamin D & Calcium'), ('MULT', 'Multivitamin')]
    feed_supplement = models.CharField(
        max_length=40, choices=supplements, default='CALC')
    # Notes about feeding interactions or whatever else might be of interest.
    notes = models.TextField(max_length=2048, blank=True)

Hopefully all is well here, I'm a bit scatter-brained this morning given everything going on.

Aside from this I will have to create some other log models from the original log. So, looking at the Log model;

class Log(models.Model):
    """Usually Daily. Should be simple and quick to fill out."""

    def __str__(self):
        return f"{self.gecko} @ {self.time}"
    # ID
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    # Time of log
    time = models.DateTimeField(default=datetime.datetime.now)
    # Gecko (FK)
    gecko = models.ForeignKey('Gecko', on_delete=models.CASCADE)
    # Defecation
    defecation = models.BooleanField(default=False)
    # Enrichment Center Time
    spent_time_in_enrichment = models.BooleanField(default=False)
    time_spent_in_enrichment = models.IntegerField(
        default=0, help_text="In minutes")

    # Behavior/Interaction
    behavior = models.TextField(max_length=2048, blank=True)
    # Problems
    problems = models.TextField(max_length=2048, blank=True)
    # Notes
    other_notes = models.TextField(max_length=2048, blank=True)

    # Log writer:
    writer = models.ForeignKey(
        User, blank=True, null=True, on_delete=models.CASCADE)
Original Log Model

I don't really need these logs to be daily anymore, since I am breaking it down... First I will take the Defecation bool and make it into a defecation log.

I feel like knowing when they eat vs when they pass the food is important information and can warn of health problems. Also knowing if there's anything unusual about the defecations and keeping a log that can be looked through and graphed out to find any correlations would be helpful as well.

Next would be an Enrichment log. This could be simple as playing, letting them climb around on stuff... or complex as having them wander mazes or hunt for hidden food around a large area with a lot of hiding spaces. (Spitballing) That would require logging the amount of time spent and the type of enrichment as well as what the critter gets up to in the enrichment. So if there are food items hidden how many were there, how many were found, etc.

Then would be interaction logs, logging any human interaction with them and how long it was and any notes.

Behavior logs have already been broken off into the micrologs application, so after that would be... Problem logs... though that's not a very good descriptor. The idea is to log anything that seems bad, like lethargy or bloating, loss of appetite, etc. I'll have to come up with something better to call them when I get to that bit.

Now with that figured out it's already lunch time so I guess I will get to the actual design and writing of these logs after I make them phone calls.

Really dreading these calls. Never liked talking on the phone, my heart always skips a beat when it rings. Better take my dog out and brush my teeth so I can get it over with.

So far, nothing. Mostly just getting voicemail and some of the time not even that. Didn't realize how bad I got the last few years in my seclusion but it's quite apparent now that I have a serious problem with social anxiety. Heart palpitations when I try to make a call. Well, that's what the whole thing is about, I need to move somewhere I can get mental healthcare and fix this dumpster fire of a brain.

I think I will stop here for today and pick up where I left off tomorrow. I sent out a couple of e-mails asking about places and maybe I will get something by this time tomorrow to give me a little more direction here. If not I will just run down all the numbers in the list tomorrow and leave voicemails on every one then hope for the best.

Also doesn't help that the furnace keeps coming on, even if I did get a hold of someone they couldn't hear me between that and the traffic. Yeah, continuing on with the models, I guess.

Starting with Defecation logs and workign my way down the list.

# THE GREAT LOG MODEL REWRITE OF '23
class Feed_Log(models.Model):
    """Log items fed, returned and when items were returned"""

    def __str__(self):
        return f"{self.gecko} ate {self.feed} @ {self.time}"

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
   
    time = models.DateTimeField(default=datetime.datetime.now)
    last_updated = models.DateTimeField(default=datetime.datetime.now)
 
    gecko = models.ForeignKey('Gecko', on_delete=models.CASCADE)
    feed = ArrayField(
        models.CharField(max_length=80)
    )
    # Feed items that were returned.
    feed_not_eaten = ArrayField(
        models.CharField(max_length=80)
    )
    all_items_eaten = models.BooleanField(default=False)
    hand_feeding_required = models.BooleanField(default=False)
    # Feed Supplement (Dropdown: Calcium, Multivitamin, Vitamin D + Calcium)
    supplements = [('CALC', 'Calcium'),
                   ('VITD', 'Vitamin D & Calcium'), ('MULT', 'Multivitamin')]
    feed_supplement = models.CharField(
        max_length=40, choices=supplements, default='CALC')
    # Notes about feeding interactions or whatever else might be of interest.
    notes = models.TextField(max_length=2048, blank=True)

# Defecation Log -- When it happened and any abnormalities
class Defecation_Log(models.Model):
    """
    Log the defecation of geckos and any abnormalities therein.
    """
    def __str__(self):
        return f"{self.gecko.name} at {self.time_recorded} | Abnormal: {self.abnormal}"

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    gecko = models.ForeignKey('Gecko', on_delete=models.CASCADE)
    time_recorded = models.DateTimeField(default=datetime.datetime.now)
    abnormal = models.BooleanField(default=False)
    notes = models.TextField(max_length=2048, blank=True)


# Enrichment Log -- When, how long, what done, if feed hidden how much, feed found.
class Enrichment_Log(models.Model):
    def __str__(self):
        return f"{self.gecko.name} at {self.time_started}"
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    gecko = models.ForeignKey('Gecko', on_delete=models.CASCADE)
    time_started = models.DateTimeField(default=datetime.datetime.now)
    time_ended = models.DateTimeField(default=datetime.datetime.now)
    enrichment_type = models.CharField(max_length=256)
    hidden_feed_items = models.IntegerField(default=0)
    found_feed_items = models.IntegerField(default=0)


# Interaction Log -- When, how long, what done, notes.
class Interaction_Log(models.Model):
    def __str__(self):
        return f"{self.gecko.name} and {self.interaction_with} @ {self.time_started}"
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    gecko = models.ForeignKey('Gecko', on_delete=models.CASCADE)
    time_started = models.DateTimeField(default=datetime.datetime.now)
    time_ended = models.DateTimeField(default=datetime.datetime.now)
    interaction_with = models.ForeignKey(User, on_delete=models.PROTECT)
    notes = models.TextField(max_length=2048, blank=True)

# Problem Log -- Health issues, anything strange and scary.
class Incident_Log(models.Model):
    def __str__(self):
        return f""
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    gecko = models.ForeignKey('Gecko', on_delete=models.CASCADE)
    time_noticed = models.DateTimeField(default=datetime.datetime.now)
    about = models.TextField(max_length=8092)
The Log Models

Looks pretty good to me so far. I'll need to spend a little more time thinking about these before I generate and run migrations though.

To do that I will write the documentation for each model, perhaps I will think of anything I missed while I go. To the aethenium!

Feed Log Doc

Now for the rest of the models.

Like I thought, I am recognizing some fields I needed to add. Like if the defecation is fresh and if there's any notes for the enrichment time.

The log model redesign documentation

Alright, now that I have all that documented I suppose I should generate and run migrations, then I can start serializing, then the UI and finally the tools to convert the old logs to the new format.

It's already 4pm now, don't feel like I got much of anything done today but I guess that's to be expected. Before I clock out and go make dinner I will get these migrations run and outline the serilizers.

Oh, joy, it would appear some funny things have occured with my migrations somewhere in the scuffle when I lost internet connectivity for those few months.

django.db.utils.ProgrammingError: column "homozygous" of relation "leopard_gecko_morph" already exists

Sure does. From the looks of things I should be alright just modifying the migration.

Just gotta snip this bit out:

migrations.RemoveField(
            model_name='morph',
            name='incomplete_recessive',
        ),
        migrations.AddField(
            model_name='morph',
            name='homozygous',
            field=models.BooleanField(default=False),
        ),
        migrations.AddField(
            model_name='morph',
            name='recessive',
            field=models.BooleanField(default=False),
        ),
No need.
Running migrations:
Applying leopard_gecko.0018_auto_20230417_1609... OK
All is as it should be.

Phew. That could have been a BIG OL pain in the ass to fix if it were any more complicated.

Now on to outlining my serializers for the API and adding the new models to the Admin interface, then clocking out and making dinner.

Outlined in the API.

It occured to me that I will have to figure out the user's timezone when they input times. Cross that bridge when I come to it.

Now just need to add them to the admin...

And the model redesign is now integrated with the Admin panel.

Alright, 4:40pm now, gonna go make some dinner after I commit my changes for the day.

Clocking out.