Source code for app.utils.adherence

"""
Module for monitoring patient medication adherence.
"""
from collections import defaultdict
from datetime import timedelta

import numpy as np


# TODO: add more options
DOSAGE_TO_FREQ = {
    "QD - 1x per day": {"freq": 1, "freq_repeat": 1, "freq_repeat_unit": "day"},
    "BID - 2x per day": {"freq": 2, "freq_repeat": 1, "freq_repeat_unit": "day"},
    "TID - 3x per day": {"freq": 3, "freq_repeat": 1, "freq_repeat_unit": "day"},
    "QID - 4x per day": {"freq": 4, "freq_repeat": 1, "freq_repeat_unit": "day"},
}

DAY_STD = {"day": 1, "week": 7, "month": 30}


# TODO: Refactor such that the functions below can go in Prescription class.
[docs]def get_next_refill_date( last_refill_date, duration, duration_unit, refills, refill_num ): """ Return next refill date based on most recent (last) refill date and dosage information. last_refill_date: datetime duration: int duration_unit: string - either 'day', 'week', or 'month' refills: int - number of refills for entire treatment refill_num: int - the refill the patient is currently on """ if refill_num == refills or refills == 0: return None days_per_cycle = np.floor(duration * DAY_STD[duration_unit] / (refills + 1)) return last_refill_date + timedelta(days=days_per_cycle)
[docs]def get_days_until_refill(curr_date, next_refill_date): """ 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. curr_date: datetime - the date against which next refill date is compared next_refill_date: datetime """ if next_refill_date is None: return None return max([(next_refill_date - curr_date).days, 0])
[docs]def adherence_by_drug_name(prescriptions, measure="general"): """ Return average adherence rates by drug name. e.g. {'Acetominophen: 0.89', 'Ibuprofen: 0.73'} prescriptions: list - list of Prescription objects measure: string - measure of adherence, either 'general' (taking the right number of pills expected) or 'ontime' (taking pills at the right time) """ if measure not in ["general", "ontime"]: raise ValueError('`measure` must be "general" or "ontime"') med_adh = defaultdict(list) for rx in prescriptions: if measure == "general": med_adh[rx.drug].append(rx.frac_required_intakes()) else: med_adh[rx.drug].append(rx.frac_on_time()) for name in med_adh.keys(): rates = med_adh[name] med_adh[name] = np.mean(rates) return med_adh
[docs]def least_adhered_by_drug_name(prescriptions, n=5, measure="general"): """ Get least adhered to medications by drug name. prescriptions: list - list of Prescription objects n: int - how many to return measure: string - measure of adherence, either 'general' (taking the right number of pills expected) or 'ontime' (taking pills at the right time) """ med_adh = adherence_by_drug_name(prescriptions, measure=measure) sorted_adh = [ (name, frac) for name, frac in sorted(med_adh.items(), key=lambda x: x[1], reverse=True) ] return sorted_adh[:n]
[docs]def most_adhered_by_drug_name(prescriptions, n=5, measure="general"): """ Get most adhered to medications by drug name. prescriptions: list - list of Prescription objects n: int - how many to return measure: string - measure of adherence, either 'general' (taking the right number of pills expected) or 'ontime' (taking pills at the right time) """ med_adh = adherence_by_drug_name(prescriptions, measure=measure) sorted_adh = [ (name, frac) for name, frac in sorted(med_adh.items(), key=lambda x: x[1], reverse=True) ] return sorted_adh[:n]
[docs]def get_unprescribed_patients(user): """Return the list of patients currently without any prescriptions.""" unprescribed = [p for p in user.patients if not p.prescriptions] return sorted(unprescribed, key=lambda p: (p.lastname, p.firstname))
[docs]def get_deviating_patients(user): """Return the list of patients currently deviating from their prescriptions.""" deviating = [p for p in user.patients if p.prescriptions and not p.is_adherent()] return sorted(deviating, key=lambda p: (p.lastname, p.firstname))
[docs]def get_adhering_patients(user): """Return the list of patients currently taking all medication on time.""" adhering = [p for p in user.patients if p.prescriptions and p.is_adherent()] return sorted(adhering, key=lambda p: (p.lastname, p.firstname))
[docs]def get_all_patients(user): """Return unprescribed, deviating, and adhering patients (in order).""" patients = get_unprescribed_patients(user) patients += get_deviating_patients(user) patients += get_adhering_patients(user) return patients