Source code for app.models.medication

from datetime import datetime
from datetime import timedelta

import numpy as np

from app import db
from app.utils.adherence import DAY_STD


[docs]class Prescription(db.Model): """ Prescription model. drug = name of drug desc = purpose/description of drug/prescription (aka "indication") strength = how much of the drug per tablet (e.g. 30mg) strength_unit = unit for strength (e.g. mg, ug) quantity = number of tablets per fill cycle form = form factor of pill (for now, only tablet aka 'tab') amount = number of units taken per intake route = how it gets into the body (only oral for now) freq = how many times per freq_repeat_unit freq_repeat = every <freq_repeat> <freq_repeat_unit> freq_repeat_unit = how often a day of intake repeats (e.g. every 3 days) duration = length of entire treatment refills = number of expected refills throughout duration time_of_day = time pill(s) should be taken (e.g. AM, PM) created = date this Prescription was created; auto-filled start_date = start date of first intake (regardless of refill) last_refill_date = date of most recent refill next_refill = date of next refill; auto-filled field days_until_refill = days until next refill; auto-filled field patient_id = patient ID for which this Prescription is for intakes = collection of Intakes for this Prescription (one to many field) """ id = db.Column(db.Integer, primary_key=True) # Prescription-related drug = db.Column(db.String(100), unique=False, nullable=False) desc = db.Column(db.String(500), unique=False, nullable=False) strength = db.Column(db.Integer, unique=False, nullable=False) strength_unit = db.Column(db.String(20), unique=False, nullable=False) quantity = db.Column(db.Integer, unique=False, nullable=False) form = db.Column(db.String(20), unique=False, nullable=False) amount = db.Column(db.Integer, unique=False, nullable=False) route = db.Column(db.String(10), unique=False, nullable=False) freq = db.Column(db.Integer, unique=False, nullable=False) freq_repeat = db.Column(db.Integer, unique=False, nullable=False) freq_repeat_unit = db.Column(db.String(10), unique=False, nullable=False) duration = db.Column(db.Integer, unique=False, nullable=False) duration_unit = db.Column(db.String(10), unique=False, nullable=False) refills = db.Column(db.Integer, unique=False, nullable=False) time_of_day = db.Column(db.String(10), unique=False, nullable=True) # Metadata start_date = db.Column(db.DateTime(), unique=False, nullable=False) created = db.Column(db.DateTime(), unique=False, nullable=False) last_refill_date = db.Column(db.DateTime(), unique=False, nullable=True) next_refill_date = db.Column(db.DateTime(), unique=False, nullable=True) refill_num = db.Column(db.Integer, unique=False, nullable=True) days_until_refill = db.Column(db.Integer, unique=False, nullable=True) # Foreign keys patient_id = db.Column(db.Integer, db.ForeignKey("patient.id"), nullable=False) # One-to-many relationship intakes = db.relationship("Intake", backref="prescription", lazy=True)
[docs] def has_started(self): """ Whether this Prescription is active, according to start date. """ return datetime.now() >= self.start_date
[docs] def is_adherent( self, on_time_threshold=0.9, required_intakes_threshold=0.9, date=datetime.now() ): """ Whether this Prescription is adhered to by the Patient. """ return ( self.frac_on_time(date=date) >= on_time_threshold and self.frac_required_intakes(date=date) >= required_intakes_threshold )
[docs] def frac_on_time(self, date=datetime.now()): """ Return fraction of intakes that were on time, out of all recorded intakes. date: datetime - get fraction on time intakes on or before this date """ intakes = list(filter(lambda i: i.timestamp <= date, self.intakes)) if len(intakes) == 0: return 0.0 on_time = Intake.query.filter( Intake.prescription_id == self.id, Intake.on_time, Intake.timestamp <= date, ).all() return len(on_time) / len(intakes)
[docs] def frac_required_intakes(self, date=datetime.now()): """ Return fraction of recorded intakes, out of total number of intakes that are supposed to be recorded by this time. date: datetime - get fraction on track intakes on or before this date """ # start of treatment until specified date days_since_start = (date - self.start_date).days - 1 if days_since_start <= 0: return 1.0 pills_per_day = int( self.amount * self.freq / (self.freq_repeat * DAY_STD[self.freq_repeat_unit]) ) n_required_intakes = days_since_start * pills_per_day if n_required_intakes == 0: return 1.0 return len(self.intakes) / n_required_intakes
[docs] def next_refill_date(self): """ Return next refill date based on most recent (last) refill date and dosage information. """ if self.refill_num == self.refills or self.refills == 0: return None days_per_cycle = np.floor( self.duration * DAY_STD[self.duration_unit] / (self.refills + 1) ) return self.last_refill_date + timedelta(days=days_per_cycle)
[docs] def days_until_refill(self): """ Return number of days until next refill. If curr_date > next_refill_date, for instance, if refill was not fulfilled in time, then days until refill is still 0. """ if self.next_refill_date() is None: return None return max([(self.next_refill_date() - datetime.now()).days, 0])
[docs] def generate_schedule(self): """ Generates the schedule which is a list of rx's, where each rx is a list of dictionaries with key being the date and value being another dictionary of metadata. Currently only works for the case when freq_repeat = 1 and freq_repeat_unit = day """ dates = [self.start_date + timedelta(days=i) for i in range(self.duration)] schedule = [ { date: { "timestamp": date + timedelta(hours=8) if self.time_of_day == "AM" else date + timedelta(hours=20), "drug": self.drug, "desc": self.desc, "amount": self.amount, "route": self.route, } } for date in dates ] return schedule
[docs]class Intake(db.Model): """ Intake model. Intakes are created when the app receives a recording, indicating that a patient has taken medication. """ id = db.Column(db.Integer, primary_key=True) s3_url = db.Column(db.String(500), unique=True, nullable=True) recording_data = db.Column(db.JSON(), unique=False, nullable=True) timestamp = db.Column(db.DateTime(), unique=False, nullable=False) on_time = db.Column(db.Boolean(), unique=False, nullable=False) # Foreign key prescription_id = db.Column( db.Integer, db.ForeignKey("prescription.id"), nullable=False )